diff --git a/app/build.gradle b/app/build.gradle
index 1bf718ab..a895eb85 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -73,6 +73,8 @@ dependencies {
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support:support-annotations:28.0.0'
+ implementation 'com.android.support:design:28.0.0'
+ implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.getbase:floatingactionbutton:1.10.1'
implementation 'com.nispok:snackbar:2.11.0'
implementation 'com.github.aegnor:rencode-java:cb628e824e'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 31d29570..b1ec99be 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -248,18 +248,14 @@
-
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 a8f7e6e5..054f140a 100644
--- a/app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java
+++ b/app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java
@@ -82,8 +82,7 @@ import org.transdroid.core.gui.navigation.NavigationFilter;
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.rss.RssfeedsActivity_;
+import org.transdroid.core.gui.rss.RssFeedsActivity_;
import org.transdroid.core.gui.search.FilePickerHelper;
import org.transdroid.core.gui.search.UrlEntryDialog;
import org.transdroid.core.gui.settings.MainSettingsActivity_;
@@ -471,7 +470,6 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
filterSearch.setVisibility(View.GONE);
torrentsToolbar.getMenu().findItem(R.id.action_search).setVisible(false);
torrentsToolbar.getMenu().findItem(R.id.action_rss).setVisible(false);
- torrentsToolbar.getMenu().findItem(R.id.action_remoterss).setVisible(false);
torrentsToolbar.getMenu().findItem(R.id.action_settings).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
torrentsToolbar.getMenu().findItem(R.id.action_help).setVisible(true);
actionsToolbar.getMenu().findItem(R.id.action_enableturtle).setVisible(false);
@@ -495,12 +493,10 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
filtersList.setVisibility(View.VISIBLE);
filterSearch.setVisibility(View.VISIBLE);
boolean addByFile = Daemon.supportsAddByFile(currentConnection.getType());
- boolean hasRemoteRss = Daemon.supportsRemoteRssManagement(currentConnection.getType());
addmenuFileButton.setVisibility(addByFile ? View.VISIBLE : View.GONE);
// Primary toolbar menu
torrentsToolbar.getMenu().findItem(R.id.action_search).setVisible(navigationHelper.enableSearchUi());
torrentsToolbar.getMenu().findItem(R.id.action_rss).setVisible(navigationHelper.enableRssUi());
- torrentsToolbar.getMenu().findItem(R.id.action_remoterss).setVisible(hasRemoteRss);
torrentsToolbar.getMenu().findItem(R.id.action_settings).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
torrentsToolbar.getMenu().findItem(R.id.action_help).setVisible(false);
// Secondary toolbar menu
@@ -815,7 +811,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
@OptionsItem(R.id.action_rss)
protected void openRss() {
- RssfeedsActivity_.intent(this).start();
+ RssFeedsActivity_.intent(this).start();
}
@OptionsItem(R.id.action_settings)
@@ -823,14 +819,6 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
MainSettingsActivity_.intent(this).start();
}
- @OptionsItem(R.id.action_remoterss)
- @Background
- protected void openRemoteRss() {
- // 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)
protected void openHelp() {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.transdroid.org/download/")));
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
deleted file mode 100644
index 7b390c15..00000000
--- a/app/src/main/java/org/transdroid/core/gui/remoterss/RemoteRssActivity.java
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright 2010-2018 Eric Kok et al.
- *
- * Transdroid is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Transdroid is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Transdroid. If not, see .
- */
-package org.transdroid.core.gui.remoterss;
-
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.AppCompatActivity;
-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.SettingsUtils;
-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;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.List;
-
-/**
- * An activity that displays a list of {@link RemoteRssItem}s via an instance of {@link RemoteRssFragment}.
- * The activity manages the drawer to filter items by the feed they came through.
- *
- * By default it displays the latest items within the last month.
- *
- * @author Twig Nguyen
- */
-@EActivity(R.layout.activity_remoterss)
-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;
-
- // Details view components
- @ViewById
- protected DrawerLayout drawerLayout;
- @ViewById
- protected LinearLayout drawerContainer;
-
- @ViewById
- protected Toolbar torrentsToolbar;
-
- @ViewById
- protected ListView drawerList;
-
- @FragmentById(R.id.remoterss_fragment)
- protected RemoteRssFragment fragmentRemoteRss;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- SettingsUtils.applyDayNightTheme(this);
- super.onCreate(savedInstanceState);
- }
-
- @AfterViews
- protected void init() {
- // Simple action bar with up, torrent name as title and refresh button
- torrentsToolbar.setNavigationIcon(R.drawable.ic_action_drawer);
- setSupportActionBar(torrentsToolbar);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
- // Connect to the last used server
- ServerSetting lastUsed = applicationSettings.getLastUsedServer();
- currentConnection = lastUsed.createServerAdapter(connectivityHelper.getConnectedNetworkName(), this);
-
- 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);
- }
-
- @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);
- }
- }
-
- @Override
- public void onBackPressed() {
- if (drawerLayout.isDrawerOpen(drawerContainer)) {
- drawerLayout.closeDrawers();
- } else {
- finish();
- }
- }
-
- private void showChannelFilters() {
- List feedLabels = new ArrayList<>(feeds.size() +1);
- feedLabels.add(new RemoteRssChannel() {
- @Override
- public String getName() {
- return getString(R.string.remoterss_filter_allrecent);
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
- });
- feedLabels.addAll(feeds);
-
- drawerList.setAdapter(new SimpleListItemAdapter(this, feedLabels));
- }
-
- @ItemClick(R.id.drawer_list)
- protected void onFeedSelected(int position) {
- selectedFilter = position;
- fragmentRemoteRss.updateRemoteItems(position == 0 ? recentItems : feeds.get(position - 1).getItems(), true);
-
- RemoteRssChannel channel = (RemoteRssChannel) drawerList.getAdapter().getItem(position);
- getSupportActionBar().setSubtitle(channel.getName());
-
- drawerLayout.closeDrawers();
- }
-
- 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();
- }
-}
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 fe51e925..43cb2e87 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
@@ -17,32 +17,26 @@
package org.transdroid.core.gui.remoterss;
-import android.app.Fragment;
-import android.support.v4.widget.SwipeRefreshLayout;
-import android.support.v7.widget.ActionMenuView;
+import android.support.v4.app.Fragment;
import android.view.View;
+import android.widget.ArrayAdapter;
import android.widget.ListView;
+import android.widget.Spinner;
import android.widget.TextView;
-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.EFragment;
import org.androidannotations.annotations.ItemClick;
-import org.androidannotations.annotations.UiThread;
+import org.androidannotations.annotations.ItemSelect;
+import org.androidannotations.annotations.OptionsItem;
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.RemoteRssChannel;
import org.transdroid.core.gui.remoterss.data.RemoteRssItem;
-import org.transdroid.core.gui.remoterss.data.RemoteRssSupplier;
-import org.transdroid.daemon.DaemonException;
-import org.transdroid.daemon.task.DaemonTaskSuccessResult;
+import org.transdroid.core.gui.rss.RssFeedsActivity;
+import org.transdroid.core.gui.settings.MainSettingsActivity_;
import java.util.ArrayList;
import java.util.List;
@@ -63,72 +57,74 @@ public class RemoteRssFragment extends Fragment {
// Views
@ViewById
protected View detailsContainer;
- @ViewById(R.id.contextual_menu)
- protected ActionMenuView contextualMenu;
- @ViewById
- protected SwipeRefreshLayout swipeRefreshLayout;
+ @ViewById(R.id.remoterss_filter)
+ protected Spinner remoteRssFilter;
@ViewById
protected ListView torrentsList;
- @ViewById
- protected TextView remoterssStatusMessage;
+ @ViewById(R.id.remoterss_status_message)
+ protected TextView remoteRssStatusMessage;
- protected RemoteRssItemsAdapter adapter;
@AfterViews
protected void init() {
-
// Inject menu options in the actions toolbar
setHasOptionsMenu(true);
-// // On large screens where this fragment is shown next to the torrents list, we show a continues grey vertical
-// // line to separate the lists visually
-// if (!NavigationHelper_.getInstance_(getActivity()).isSmallScreen()) {
-// if (SystemSettings_.getInstance_(getActivity()).useDarkTheme()) {
-// detailsContainer.setBackgroundResource(R.drawable.details_list_background_dark);
-// } else {
-// detailsContainer.setBackgroundResource(R.drawable.details_list_background_light);
-// }
-// }
-
// Set up details adapter
- adapter = new RemoteRssItemsAdapter(getActivity());
+ RemoteRssItemsAdapter adapter = new RemoteRssItemsAdapter(getActivity());
torrentsList.setAdapter(adapter);
torrentsList.setFastScrollEnabled(true);
+ }
- // 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();
- }
- });
- }
+ @Override
+ public void onResume() {
+ super.onResume();
+ this.refreshScreen();
+ }
+
+ @OptionsItem(R.id.action_refresh)
+ protected void refreshScreen() {
+ RssFeedsActivity rssActivity = (RssFeedsActivity) getActivity();
+ rssActivity.refreshRemoteFeeds();
+ }
+
+ @OptionsItem(R.id.action_settings)
+ protected void openSettings() {
+ MainSettingsActivity_.intent(getActivity()).start();
}
/**
* Updates the UI with a new list of RSS items.
*/
public void updateRemoteItems(List remoteItems, boolean scrollToTop) {
+ RemoteRssItemsAdapter adapter = (RemoteRssItemsAdapter) torrentsList.getAdapter();
+
remoteRssItems = new ArrayList<>(remoteItems);
adapter.updateItems(remoteRssItems);
+
if (scrollToTop) {
torrentsList.smoothScrollToPosition(0);
}
// Show/hide a nice message if there are no items to show
if (remoteRssItems.size() > 0) {
- remoterssStatusMessage.setVisibility(View.GONE);
+ remoteRssStatusMessage.setVisibility(View.GONE);
}
else {
- remoterssStatusMessage.setVisibility(View.VISIBLE);
- remoterssStatusMessage.setText(R.string.remoterss_no_files);
+ remoteRssStatusMessage.setVisibility(View.VISIBLE);
+ remoteRssStatusMessage.setText(R.string.remoterss_no_files);
}
- swipeRefreshLayout.setRefreshing(false);
}
- @UiThread
- public void setRefreshing(boolean refreshing) {
- swipeRefreshLayout.setRefreshing(refreshing);
+ public void updateChannelFilters(List feedLabels) {
+ List labels = new ArrayList<>();
+
+ for (RemoteRssChannel feedLabel : feedLabels) {
+ labels.add(feedLabel.getName());
+ }
+
+ ArrayAdapter adapter = new ArrayAdapter<>(this.getContext(), android.R.layout.simple_spinner_dropdown_item, labels);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ remoteRssFilter.setAdapter(adapter);
}
/**
@@ -136,37 +132,14 @@ public class RemoteRssFragment extends Fragment {
*/
@ItemClick(resName = "torrents_list")
protected void detailsListClicked(int position) {
+ RemoteRssItemsAdapter adapter = (RemoteRssItemsAdapter) torrentsList.getAdapter();
RemoteRssItem item = (RemoteRssItem) adapter.getItem(position);
- downloadRemoteRssItem(item);
- }
-
- /**
- * Download the item in a background thread and display success/fail accordingly.
- */
- @Background
- protected void downloadRemoteRssItem(RemoteRssItem item) {
- 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)));
- }
- }
- @UiThread
- protected void onTaskSucceeded(DaemonTaskSuccessResult result, String successMessage) {
- SnackbarManager.show(Snackbar.with(getActivity()).text(successMessage));
+ ((RssFeedsActivity) getActivity()).downloadRemoteRssItem(item);
}
- @UiThread
- protected void onTaskFailed(String message) {
- SnackbarManager.show(Snackbar.with(getActivity())
- .text(message)
- .colorResource(R.color.red)
- .type(SnackbarType.MULTI_LINE)
- );
+ @ItemSelect(R.id.remoterss_filter)
+ protected void onFeedSelected(boolean selected, int position) {
+ ((RssFeedsActivity) getActivity()).onFeedSelected(position);
}
}
diff --git a/app/src/main/java/org/transdroid/core/gui/rss/RssFeedsActivity.java b/app/src/main/java/org/transdroid/core/gui/rss/RssFeedsActivity.java
new file mode 100644
index 00000000..8fc448ce
--- /dev/null
+++ b/app/src/main/java/org/transdroid/core/gui/rss/RssFeedsActivity.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright 2010-2018 Eric Kok et al.
+ *
+ * Transdroid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Transdroid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Transdroid. If not, see .
+ */
+package org.transdroid.core.gui.rss;
+
+import android.annotation.TargetApi;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.TabLayout;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+
+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.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.RssfeedSetting;
+import org.transdroid.core.app.settings.ServerSetting;
+import org.transdroid.core.app.settings.SettingsUtils;
+import org.transdroid.core.gui.TorrentsActivity_;
+import org.transdroid.core.gui.lists.LocalTorrent;
+import org.transdroid.core.gui.log.Log;
+import org.transdroid.core.gui.navigation.NavigationHelper;
+import org.transdroid.core.gui.remoterss.RemoteRssFragment;
+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.RssParser;
+import org.transdroid.core.service.ConnectivityHelper;
+import org.transdroid.daemon.Daemon;
+import org.transdroid.daemon.DaemonException;
+import org.transdroid.daemon.IDaemonAdapter;
+import org.transdroid.daemon.task.DaemonTaskSuccessResult;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+@EActivity(R.layout.activity_rssfeeds)
+public class RssFeedsActivity extends AppCompatActivity {
+
+ // Settings and local data
+ @Bean
+ protected Log log;
+ @Bean
+ protected ApplicationSettings applicationSettings;
+
+ protected static final int RSS_FEEDS_LOCAL = 0;
+ protected static final int RSS_FEEDS_REMOTE = 1;
+
+ @FragmentById(R.id.rssfeeds_fragment)
+ protected RssFeedsFragment fragmentLocalFeeds;
+ @FragmentById(R.id.rssitems_fragment)
+ protected RssItemsFragment fragmentItems;
+ @FragmentById(R.id.remoterss_fragment)
+ protected RemoteRssFragment fragmentRemoteFeeds;
+
+ @ViewById(R.id.rssfeeds_toolbar)
+ protected Toolbar rssFeedsToolbar;
+ @ViewById(R.id.rssfeeds_tabs)
+ protected TabLayout tabLayout;
+ @ViewById(R.id.rssfeeds_pager)
+ protected ViewPager viewPager;
+
+ // remote RSS stuff
+ @NonConfigurationInstance
+ protected ArrayList feeds;
+ @InstanceState
+ protected int selectedFilter;
+ @NonConfigurationInstance
+ protected ArrayList recentItems;
+ @Bean
+ protected ConnectivityHelper connectivityHelper;
+
+
+ protected class LayoutPagerAdapter extends PagerAdapter {
+ boolean hasRemoteRss;
+ String serverName;
+
+ public LayoutPagerAdapter(boolean hasRemoteRss, String name) {
+ super();
+
+ this.hasRemoteRss = hasRemoteRss;
+ this.serverName = (name.length() > 0 ? name : getString(R.string.navigation_rss_tabs_remote));
+ }
+
+ @NonNull
+ @Override
+ public Object instantiateItem(@NonNull ViewGroup container, int position) {
+ int resId = 0;
+
+ if (position == RSS_FEEDS_LOCAL) {
+ resId = R.id.layout_rssfeeds_local;
+ }
+ else if (position == RSS_FEEDS_REMOTE) {
+ resId = R.id.layout_rss_feeds_remote;
+ }
+
+ return findViewById(resId);
+ }
+
+ @Override
+ public int getCount() {
+ return (this.hasRemoteRss ? 2 : 1);
+ }
+
+ @Override
+ public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
+ return (view == o);
+ }
+
+ @Override
+ public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
+ container.removeView((View) object);
+ }
+
+ @Nullable
+ @Override
+ public CharSequence getPageTitle(int position) {
+ switch (position) {
+ case RSS_FEEDS_LOCAL:
+ return getString(R.string.navigation_rss_tabs_local);
+ case RSS_FEEDS_REMOTE:
+ return this.serverName;
+ }
+
+ return super.getPageTitle(position);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ SettingsUtils.applyDayNightTheme(this);
+ super.onCreate(savedInstanceState);
+ }
+
+ @AfterViews
+ protected void init() {
+ setSupportActionBar(rssFeedsToolbar);
+ getSupportActionBar().setTitle(NavigationHelper.buildCondensedFontString(getString(R.string.rss_feeds)));
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ IDaemonAdapter currentConnection = this.getCurrentConnection();
+ boolean hasRemoteRss = Daemon.supportsRemoteRssManagement(currentConnection.getType());
+
+ PagerAdapter pagerAdapter = new LayoutPagerAdapter(hasRemoteRss, currentConnection.getSettings().getName());
+ viewPager.setAdapter(pagerAdapter);
+ tabLayout.setupWithViewPager(viewPager);
+ viewPager.setCurrentItem(0);
+
+ if (!hasRemoteRss) {
+ tabLayout.setVisibility(View.GONE);
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ @OptionsItem(android.R.id.home)
+ protected void navigateUp() {
+ TorrentsActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_CLEAR_TOP).start();
+ }
+
+ /**
+ * Reload the RSS feed settings and start loading all the feeds. To be called from contained fragments.
+ */
+ public void refreshFeeds() {
+ List loaders = new ArrayList<>();
+ // For each RSS feed setting the user created, start a loader that retrieved the RSS feed (via a background
+ // thread) and, on success, determines the new items in the feed
+ for (RssfeedSetting setting : applicationSettings.getRssfeedSettings()) {
+ RssfeedLoader loader = new RssfeedLoader(setting);
+ loaders.add(loader);
+ loadRssfeed(loader);
+ }
+
+ fragmentLocalFeeds.update(loaders);
+ }
+
+ /**
+ * Performs the loading of the RSS feed content and parsing of items, in a background thread.
+ * @param loader The RSS feed loader for which to retrieve the contents
+ */
+ @Background
+ protected void loadRssfeed(RssfeedLoader loader) {
+ try {
+ // Load and parse the feed
+ RssParser parser =
+ new RssParser(loader.getSetting().getUrl(), loader.getSetting().getExcludeFilter(), loader.getSetting().getIncludeFilter());
+ parser.parse();
+ handleRssfeedResult(loader, parser.getChannel(), false);
+ } catch (Exception e) {
+ // Catch any error that may occurred and register this failure
+ handleRssfeedResult(loader, null, true);
+ log.i(this, "RSS feed " + loader.getSetting().getUrl() + " error: " + e.toString());
+ }
+ }
+
+ /**
+ * Stores the retrieved RSS feed content channel into the loader and updates the RSS feed in the feeds list fragment.
+ * @param loader The RSS feed loader that was executed
+ * @param channel The data that was retrieved, or null if it could not be parsed
+ * @param hasError True if a connection error occurred in the loading of the feed; false otherwise
+ */
+ @UiThread
+ protected void handleRssfeedResult(RssfeedLoader loader, Channel channel, boolean hasError) {
+ loader.update(channel, hasError);
+
+ fragmentLocalFeeds.notifyDataSetChanged();
+ }
+
+ /**
+ * Opens an RSS feed in the dedicated fragment (if there was space in the UI) or a new {@link RssItemsActivity}. Optionally this also registers in
+ * the user preferences that the feed was now viewed, so that in the future the new items can be properly marked.
+ * @param loader The RSS feed loader (with settings and the loaded content channel) to show
+ * @param markAsViewedNow True if the user settings should be updated to reflect this feed's last viewed date; false otherwise
+ */
+ public void openRssfeed(RssfeedLoader loader, boolean markAsViewedNow) {
+
+ // The RSS feed content was loaded and can now be shown in the dedicated fragment or a new activity
+ if (fragmentItems != null && fragmentItems.isAdded()) {
+
+ // If desired, update the lastViewedDate and lastViewedItemUrl of this feed in the user setting; this won't
+ // be loaded until the RSS feeds screen in opened again.
+ if (!loader.hasError() && loader.getChannel() != null && markAsViewedNow) {
+ String lastViewedItemUrl = null;
+ if (loader.getChannel().getItems() != null && loader.getChannel().getItems().size() > 0) {
+ lastViewedItemUrl = loader.getChannel().getItems().get(0).getTheLink();
+ }
+ applicationSettings.setRssfeedLastViewer(loader.getSetting().getOrder(), new Date(), lastViewedItemUrl);
+ }
+ fragmentItems.update(loader.getChannel(), loader.hasError(), loader.getSetting().requiresExternalAuthentication());
+
+ } else {
+
+ // Error message or not yet loaded? Show a toast message instead of opening the items activity
+ if (loader.hasError()) {
+ SnackbarManager.show(Snackbar.with(this).text(R.string.rss_error).colorResource(R.color.red));
+ return;
+ }
+ if (loader.getChannel() == null || loader.getChannel().getItems().size() == 0) {
+ SnackbarManager.show(Snackbar.with(this).text(R.string.rss_notloaded).colorResource(R.color.red));
+ return;
+ }
+
+ // If desired, update the lastViewedDate and lastViewedItemUrl of this feed in the user setting; this won't
+ // be loaded until the RSS feeds screen in opened again
+ if (markAsViewedNow) {
+ String lastViewedItemUrl = null;
+ if (loader.getChannel().getItems() != null && loader.getChannel().getItems().size() > 0) {
+ lastViewedItemUrl = loader.getChannel().getItems().get(0).getTheLink();
+ }
+ applicationSettings.setRssfeedLastViewer(loader.getSetting().getOrder(), new Date(), lastViewedItemUrl);
+ }
+
+ String name = loader.getChannel().getTitle();
+ if (TextUtils.isEmpty(name)) {
+ name = loader.getSetting().getName();
+ }
+ if (TextUtils.isEmpty(name) && !TextUtils.isEmpty(loader.getSetting().getUrl())) {
+ name = Uri.parse(loader.getSetting().getUrl()).getHost();
+ }
+ RssItemsActivity_.intent(this).rssfeed(loader.getChannel()).rssfeedName(name)
+ .requiresExternalAuthentication(loader.getSetting().requiresExternalAuthentication()).start();
+
+ }
+ }
+
+ protected IDaemonAdapter getCurrentConnection() {
+ ServerSetting lastUsed = applicationSettings.getLastUsedServer();
+ return lastUsed.createServerAdapter(connectivityHelper.getConnectedNetworkName(), this);
+ }
+
+ // @Background
+ public void refreshRemoteFeeds() {
+ // Connect to the last used server
+ IDaemonAdapter currentConnection = this.getCurrentConnection();
+
+ // remote rss not supported for this connection type
+ if (currentConnection instanceof RemoteRssSupplier == false) {
+ return;
+ }
+
+ try {
+ feeds = ((RemoteRssSupplier) (currentConnection)).getRemoteRssChannels(log);
+
+ // By default it displays the latest items within the last month.
+ 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());
+ }
+ });
+ } catch (DaemonException e) {
+ onCommunicationError(e);
+ return;
+ }
+
+ // @UIThread
+ fragmentRemoteFeeds.updateRemoteItems(
+ selectedFilter == 0 ? recentItems : feeds.get(selectedFilter -1).getItems(),
+ false /* allow android to restore scroll position */ );
+ showRemoteChannelFilters();
+ }
+
+ @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));
+ }
+
+
+ public void onFeedSelected(int position) {
+ selectedFilter = position;
+
+ if (position == 0) {
+ fragmentRemoteFeeds.updateRemoteItems(recentItems, true);
+ }
+ else {
+ RemoteRssChannel channel = feeds.get(selectedFilter -1);
+ fragmentRemoteFeeds.updateRemoteItems(channel.getItems(), true);
+ }
+ }
+
+ /**
+ * Download the item in a background thread and display success/fail accordingly.
+ */
+ @Background
+ public void downloadRemoteRssItem(RemoteRssItem item) {
+ final RemoteRssSupplier supplier = (RemoteRssSupplier) this.getCurrentConnection();
+
+ try {
+ RemoteRssChannel channel = feeds.get(selectedFilter);
+ supplier.downloadRemoteRssItem(log, item, channel);
+ onTaskSucceeded(null, getString(R.string.result_added, item.getTitle()));
+ } catch (DaemonException e) {
+ onTaskFailed(getString(LocalTorrent.getResourceForDaemonException(e)));
+ }
+ }
+
+ @UiThread
+ protected void onTaskSucceeded(DaemonTaskSuccessResult result, String successMessage) {
+ SnackbarManager.show(Snackbar.with(this).text(successMessage));
+ }
+
+ @UiThread
+ protected void onTaskFailed(String message) {
+ SnackbarManager.show(Snackbar.with(this)
+ .text(message)
+ .colorResource(R.color.red)
+ .type(SnackbarType.MULTI_LINE)
+ );
+ }
+
+ private void showRemoteChannelFilters() {
+ List feedLabels = new ArrayList<>(feeds.size() +1);
+ feedLabels.add(new RemoteRssChannel() {
+ @Override
+ public String getName() {
+ return getString(R.string.remoterss_filter_allrecent);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+ });
+ feedLabels.addAll(feeds);
+
+ fragmentRemoteFeeds.updateChannelFilters(feedLabels);
+ }
+}
diff --git a/app/src/main/java/org/transdroid/core/gui/rss/RssfeedsFragment.java b/app/src/main/java/org/transdroid/core/gui/rss/RssFeedsFragment.java
similarity index 89%
rename from app/src/main/java/org/transdroid/core/gui/rss/RssfeedsFragment.java
rename to app/src/main/java/org/transdroid/core/gui/rss/RssFeedsFragment.java
index df227b4b..894bf4f0 100644
--- a/app/src/main/java/org/transdroid/core/gui/rss/RssfeedsFragment.java
+++ b/app/src/main/java/org/transdroid/core/gui/rss/RssFeedsFragment.java
@@ -16,7 +16,7 @@
*/
package org.transdroid.core.gui.rss;
-import android.app.Fragment;
+import android.support.v4.app.Fragment;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -41,7 +41,7 @@ import java.util.List;
*/
@EFragment(R.layout.fragment_rssfeeds)
@OptionsMenu(R.menu.fragment_rssfeeds)
-public class RssfeedsFragment extends Fragment {
+public class RssFeedsFragment extends Fragment {
// Views
@ViewById(R.id.rssfeeds_list)
@@ -77,14 +77,24 @@ public class RssfeedsFragment extends Fragment {
MainSettingsActivity_.intent(getActivity()).start();
}
+ protected RssFeedsActivity getRssActivity() {
+ return (RssFeedsActivity) getActivity();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ this.refreshScreen();
+ }
+
@OptionsItem(R.id.action_refresh)
protected void refreshScreen() {
- ((RssfeedsActivity) getActivity()).refreshFeeds();
+ getRssActivity().refreshFeeds();
}
@ItemClick(R.id.rssfeeds_list)
protected void onFeedClicked(RssfeedLoader loader) {
- ((RssfeedsActivity) getActivity()).openRssfeed(loader, true);
+ getRssActivity().openRssfeed(loader, true);
}
/**
diff --git a/app/src/main/java/org/transdroid/core/gui/rss/RssitemsActivity.java b/app/src/main/java/org/transdroid/core/gui/rss/RssItemsActivity.java
similarity index 96%
rename from app/src/main/java/org/transdroid/core/gui/rss/RssitemsActivity.java
rename to app/src/main/java/org/transdroid/core/gui/rss/RssItemsActivity.java
index c0440c39..46e9a7aa 100644
--- a/app/src/main/java/org/transdroid/core/gui/rss/RssitemsActivity.java
+++ b/app/src/main/java/org/transdroid/core/gui/rss/RssItemsActivity.java
@@ -36,7 +36,7 @@ import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.rssparser.Channel;
@EActivity(R.layout.activity_rssitems)
-public class RssitemsActivity extends AppCompatActivity {
+public class RssItemsActivity extends AppCompatActivity {
@Extra
protected Channel rssfeed = null;
@@ -46,7 +46,7 @@ public class RssitemsActivity extends AppCompatActivity {
protected boolean requiresExternalAuthentication;
@FragmentById(R.id.rssitems_fragment)
- protected RssitemsFragment fragmentItems;
+ protected RssItemsFragment fragmentItems;
@ViewById
protected Toolbar rssfeedsToolbar;
diff --git a/app/src/main/java/org/transdroid/core/gui/rss/RssitemsFragment.java b/app/src/main/java/org/transdroid/core/gui/rss/RssItemsFragment.java
similarity index 92%
rename from app/src/main/java/org/transdroid/core/gui/rss/RssitemsFragment.java
rename to app/src/main/java/org/transdroid/core/gui/rss/RssItemsFragment.java
index 869baf68..e14c42cf 100644
--- a/app/src/main/java/org/transdroid/core/gui/rss/RssitemsFragment.java
+++ b/app/src/main/java/org/transdroid/core/gui/rss/RssItemsFragment.java
@@ -17,13 +17,13 @@
package org.transdroid.core.gui.rss;
import android.app.AlertDialog;
-import android.app.Fragment;
import android.app.SearchManager;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.ActionMode;
@@ -60,10 +60,10 @@ import java.util.List;
* @author Eric Kok
*/
@EFragment(R.layout.fragment_rssitems)
-public class RssitemsFragment extends Fragment {
+public class RssItemsFragment extends Fragment {
@InstanceState
- protected Channel rssfeed = null;
+ protected Channel rssFeed = null;
@InstanceState
protected boolean hasError = false;
@InstanceState
@@ -74,7 +74,7 @@ public class RssitemsFragment extends Fragment {
// Views
@ViewById(R.id.rssitems_list)
- protected ListView rssitemsList;
+ protected ListView rssItemsList;
private MultiChoiceModeListener onItemsSelected = new MultiChoiceModeListener() {
SelectionManagerMode selectionManagerMode;
@@ -84,7 +84,7 @@ public class RssitemsFragment extends Fragment {
// Show contextual action bar to add items in batch mode
mode.getMenuInflater().inflate(R.menu.fragment_rssitems_cab, menu);
Context themedContext = ((AppCompatActivity) getActivity()).getSupportActionBar().getThemedContext();
- selectionManagerMode = new SelectionManagerMode(themedContext, rssitemsList, R.plurals.rss_itemsselected);
+ selectionManagerMode = new SelectionManagerMode(themedContext, rssItemsList, R.plurals.rss_itemsselected);
selectionManagerMode.onCreateActionMode(mode, menu);
return true;
}
@@ -98,9 +98,9 @@ public class RssitemsFragment extends Fragment {
// Get checked torrents
List- checked = new ArrayList<>();
- for (int i = 0; i < rssitemsList.getCheckedItemPositions().size(); i++) {
- if (rssitemsList.getCheckedItemPositions().valueAt(i)) {
- checked.add(rssitemsAdapter.getItem(rssitemsList.getCheckedItemPositions().keyAt(i)));
+ for (int i = 0; i < rssItemsList.getCheckedItemPositions().size(); i++) {
+ if (rssItemsList.getCheckedItemPositions().valueAt(i)) {
+ checked.add(rssitemsAdapter.getItem(rssItemsList.getCheckedItemPositions().keyAt(i)));
}
}
@@ -189,9 +189,9 @@ public class RssitemsFragment extends Fragment {
protected void init() {
// Set up the list adapter, which allows multi-select
- rssitemsList.setAdapter(rssitemsAdapter);
- rssitemsList.setMultiChoiceModeListener(onItemsSelected);
- update(rssfeed, hasError, requiresExternalAuthentication);
+ rssItemsList.setAdapter(rssitemsAdapter);
+ rssItemsList.setMultiChoiceModeListener(onItemsSelected);
+ update(rssFeed, hasError, requiresExternalAuthentication);
}
@@ -204,7 +204,7 @@ public class RssitemsFragment extends Fragment {
public void update(Channel channel, boolean hasError, boolean requiresExternalAuthentication) {
this.requiresExternalAuthentication = requiresExternalAuthentication;
rssitemsAdapter.update(channel);
- rssitemsList.setVisibility(View.GONE);
+ rssItemsList.setVisibility(View.GONE);
emptyText.setVisibility(View.VISIBLE);
if (hasError) {
emptyText.setText(R.string.rss_error);
@@ -218,7 +218,7 @@ public class RssitemsFragment extends Fragment {
emptyText.setText(R.string.rss_empty);
return;
}
- rssitemsList.setVisibility(View.VISIBLE);
+ rssItemsList.setVisibility(View.VISIBLE);
emptyText.setVisibility(View.INVISIBLE);
}
diff --git a/app/src/main/java/org/transdroid/core/gui/rss/RssfeedsActivity.java b/app/src/main/java/org/transdroid/core/gui/rss/RssfeedsActivity.java
index c3b81a08..8fc448ce 100644
--- a/app/src/main/java/org/transdroid/core/gui/rss/RssfeedsActivity.java
+++ b/app/src/main/java/org/transdroid/core/gui/rss/RssfeedsActivity.java
@@ -21,52 +21,151 @@ import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.Parcel;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.TabLayout;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
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.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.RssfeedSetting;
+import org.transdroid.core.app.settings.ServerSetting;
import org.transdroid.core.app.settings.SettingsUtils;
import org.transdroid.core.gui.TorrentsActivity_;
+import org.transdroid.core.gui.lists.LocalTorrent;
import org.transdroid.core.gui.log.Log;
import org.transdroid.core.gui.navigation.NavigationHelper;
+import org.transdroid.core.gui.remoterss.RemoteRssFragment;
+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.RssParser;
+import org.transdroid.core.service.ConnectivityHelper;
+import org.transdroid.daemon.Daemon;
+import org.transdroid.daemon.DaemonException;
+import org.transdroid.daemon.IDaemonAdapter;
+import org.transdroid.daemon.task.DaemonTaskSuccessResult;
import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.Date;
import java.util.List;
@EActivity(R.layout.activity_rssfeeds)
-public class RssfeedsActivity extends AppCompatActivity {
+public class RssFeedsActivity extends AppCompatActivity {
// Settings and local data
@Bean
protected Log log;
@Bean
protected ApplicationSettings applicationSettings;
- protected List loaders;
- // Contained feeds and items fragments
+ protected static final int RSS_FEEDS_LOCAL = 0;
+ protected static final int RSS_FEEDS_REMOTE = 1;
+
@FragmentById(R.id.rssfeeds_fragment)
- protected RssfeedsFragment fragmentFeeds;
+ protected RssFeedsFragment fragmentLocalFeeds;
@FragmentById(R.id.rssitems_fragment)
- protected RssitemsFragment fragmentItems;
- @ViewById
- protected Toolbar rssfeedsToolbar;
+ protected RssItemsFragment fragmentItems;
+ @FragmentById(R.id.remoterss_fragment)
+ protected RemoteRssFragment fragmentRemoteFeeds;
+
+ @ViewById(R.id.rssfeeds_toolbar)
+ protected Toolbar rssFeedsToolbar;
+ @ViewById(R.id.rssfeeds_tabs)
+ protected TabLayout tabLayout;
+ @ViewById(R.id.rssfeeds_pager)
+ protected ViewPager viewPager;
+
+ // remote RSS stuff
+ @NonConfigurationInstance
+ protected ArrayList feeds;
+ @InstanceState
+ protected int selectedFilter;
+ @NonConfigurationInstance
+ protected ArrayList recentItems;
+ @Bean
+ protected ConnectivityHelper connectivityHelper;
+
+
+ protected class LayoutPagerAdapter extends PagerAdapter {
+ boolean hasRemoteRss;
+ String serverName;
+
+ public LayoutPagerAdapter(boolean hasRemoteRss, String name) {
+ super();
+
+ this.hasRemoteRss = hasRemoteRss;
+ this.serverName = (name.length() > 0 ? name : getString(R.string.navigation_rss_tabs_remote));
+ }
+
+ @NonNull
+ @Override
+ public Object instantiateItem(@NonNull ViewGroup container, int position) {
+ int resId = 0;
+
+ if (position == RSS_FEEDS_LOCAL) {
+ resId = R.id.layout_rssfeeds_local;
+ }
+ else if (position == RSS_FEEDS_REMOTE) {
+ resId = R.id.layout_rss_feeds_remote;
+ }
+
+ return findViewById(resId);
+ }
+
+ @Override
+ public int getCount() {
+ return (this.hasRemoteRss ? 2 : 1);
+ }
+
+ @Override
+ public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
+ return (view == o);
+ }
+
+ @Override
+ public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
+ container.removeView((View) object);
+ }
+
+ @Nullable
+ @Override
+ public CharSequence getPageTitle(int position) {
+ switch (position) {
+ case RSS_FEEDS_LOCAL:
+ return getString(R.string.navigation_rss_tabs_local);
+ case RSS_FEEDS_REMOTE:
+ return this.serverName;
+ }
+
+ return super.getPageTitle(position);
+ }
+ }
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -76,9 +175,21 @@ public class RssfeedsActivity extends AppCompatActivity {
@AfterViews
protected void init() {
- setSupportActionBar(rssfeedsToolbar);
+ setSupportActionBar(rssFeedsToolbar);
getSupportActionBar().setTitle(NavigationHelper.buildCondensedFontString(getString(R.string.rss_feeds)));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ IDaemonAdapter currentConnection = this.getCurrentConnection();
+ boolean hasRemoteRss = Daemon.supportsRemoteRssManagement(currentConnection.getType());
+
+ PagerAdapter pagerAdapter = new LayoutPagerAdapter(hasRemoteRss, currentConnection.getSettings().getName());
+ viewPager.setAdapter(pagerAdapter);
+ tabLayout.setupWithViewPager(viewPager);
+ viewPager.setCurrentItem(0);
+
+ if (!hasRemoteRss) {
+ tabLayout.setVisibility(View.GONE);
+ }
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@@ -87,17 +198,11 @@ public class RssfeedsActivity extends AppCompatActivity {
TorrentsActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_CLEAR_TOP).start();
}
- @Override
- protected void onResume() {
- super.onResume();
- refreshFeeds();
- }
-
/**
* Reload the RSS feed settings and start loading all the feeds. To be called from contained fragments.
*/
public void refreshFeeds() {
- loaders = new ArrayList<>();
+ List loaders = new ArrayList<>();
// For each RSS feed setting the user created, start a loader that retrieved the RSS feed (via a background
// thread) and, on success, determines the new items in the feed
for (RssfeedSetting setting : applicationSettings.getRssfeedSettings()) {
@@ -105,7 +210,8 @@ public class RssfeedsActivity extends AppCompatActivity {
loaders.add(loader);
loadRssfeed(loader);
}
- fragmentFeeds.update(loaders);
+
+ fragmentLocalFeeds.update(loaders);
}
/**
@@ -125,7 +231,6 @@ public class RssfeedsActivity extends AppCompatActivity {
handleRssfeedResult(loader, null, true);
log.i(this, "RSS feed " + loader.getSetting().getUrl() + " error: " + e.toString());
}
-
}
/**
@@ -137,11 +242,12 @@ public class RssfeedsActivity extends AppCompatActivity {
@UiThread
protected void handleRssfeedResult(RssfeedLoader loader, Channel channel, boolean hasError) {
loader.update(channel, hasError);
- fragmentFeeds.notifyDataSetChanged();
+
+ fragmentLocalFeeds.notifyDataSetChanged();
}
/**
- * Opens an RSS feed in the dedicated fragment (if there was space in the UI) or a new {@link RssitemsActivity}. Optionally this also registers in
+ * Opens an RSS feed in the dedicated fragment (if there was space in the UI) or a new {@link RssItemsActivity}. Optionally this also registers in
* the user preferences that the feed was now viewed, so that in the future the new items can be properly marked.
* @param loader The RSS feed loader (with settings and the loaded content channel) to show
* @param markAsViewedNow True if the user settings should be updated to reflect this feed's last viewed date; false otherwise
@@ -191,11 +297,128 @@ public class RssfeedsActivity extends AppCompatActivity {
if (TextUtils.isEmpty(name) && !TextUtils.isEmpty(loader.getSetting().getUrl())) {
name = Uri.parse(loader.getSetting().getUrl()).getHost();
}
- RssitemsActivity_.intent(this).rssfeed(loader.getChannel()).rssfeedName(name)
+ RssItemsActivity_.intent(this).rssfeed(loader.getChannel()).rssfeedName(name)
.requiresExternalAuthentication(loader.getSetting().requiresExternalAuthentication()).start();
}
+ }
+ protected IDaemonAdapter getCurrentConnection() {
+ ServerSetting lastUsed = applicationSettings.getLastUsedServer();
+ return lastUsed.createServerAdapter(connectivityHelper.getConnectedNetworkName(), this);
}
+ // @Background
+ public void refreshRemoteFeeds() {
+ // Connect to the last used server
+ IDaemonAdapter currentConnection = this.getCurrentConnection();
+
+ // remote rss not supported for this connection type
+ if (currentConnection instanceof RemoteRssSupplier == false) {
+ return;
+ }
+
+ try {
+ feeds = ((RemoteRssSupplier) (currentConnection)).getRemoteRssChannels(log);
+
+ // By default it displays the latest items within the last month.
+ 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());
+ }
+ });
+ } catch (DaemonException e) {
+ onCommunicationError(e);
+ return;
+ }
+
+ // @UIThread
+ fragmentRemoteFeeds.updateRemoteItems(
+ selectedFilter == 0 ? recentItems : feeds.get(selectedFilter -1).getItems(),
+ false /* allow android to restore scroll position */ );
+ showRemoteChannelFilters();
+ }
+
+ @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));
+ }
+
+
+ public void onFeedSelected(int position) {
+ selectedFilter = position;
+
+ if (position == 0) {
+ fragmentRemoteFeeds.updateRemoteItems(recentItems, true);
+ }
+ else {
+ RemoteRssChannel channel = feeds.get(selectedFilter -1);
+ fragmentRemoteFeeds.updateRemoteItems(channel.getItems(), true);
+ }
+ }
+
+ /**
+ * Download the item in a background thread and display success/fail accordingly.
+ */
+ @Background
+ public void downloadRemoteRssItem(RemoteRssItem item) {
+ final RemoteRssSupplier supplier = (RemoteRssSupplier) this.getCurrentConnection();
+
+ try {
+ RemoteRssChannel channel = feeds.get(selectedFilter);
+ supplier.downloadRemoteRssItem(log, item, channel);
+ onTaskSucceeded(null, getString(R.string.result_added, item.getTitle()));
+ } catch (DaemonException e) {
+ onTaskFailed(getString(LocalTorrent.getResourceForDaemonException(e)));
+ }
+ }
+
+ @UiThread
+ protected void onTaskSucceeded(DaemonTaskSuccessResult result, String successMessage) {
+ SnackbarManager.show(Snackbar.with(this).text(successMessage));
+ }
+
+ @UiThread
+ protected void onTaskFailed(String message) {
+ SnackbarManager.show(Snackbar.with(this)
+ .text(message)
+ .colorResource(R.color.red)
+ .type(SnackbarType.MULTI_LINE)
+ );
+ }
+
+ private void showRemoteChannelFilters() {
+ List feedLabels = new ArrayList<>(feeds.size() +1);
+ feedLabels.add(new RemoteRssChannel() {
+ @Override
+ public String getName() {
+ return getString(R.string.remoterss_filter_allrecent);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+ });
+ feedLabels.addAll(feeds);
+
+ fragmentRemoteFeeds.updateChannelFilters(feedLabels);
+ }
}
diff --git a/app/src/main/java/org/transdroid/core/service/RssCheckerJobRunner.java b/app/src/main/java/org/transdroid/core/service/RssCheckerJobRunner.java
index 1ecbea54..910c90da 100644
--- a/app/src/main/java/org/transdroid/core/service/RssCheckerJobRunner.java
+++ b/app/src/main/java/org/transdroid/core/service/RssCheckerJobRunner.java
@@ -33,7 +33,7 @@ import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.NotificationSettings;
import org.transdroid.core.app.settings.RssfeedSetting;
import org.transdroid.core.gui.log.Log;
-import org.transdroid.core.gui.rss.RssfeedsActivity_;
+import org.transdroid.core.gui.rss.RssFeedsActivity_;
import org.transdroid.core.rssparser.Item;
import org.transdroid.core.rssparser.RssParser;
import org.transdroid.daemon.util.Collections2;
@@ -121,7 +121,7 @@ public class RssCheckerJobRunner {
// Provide a notification, since there are new RSS items
PendingIntent pi = PendingIntent
- .getActivity(context, 80000, new Intent(context, RssfeedsActivity_.class), PendingIntent.FLAG_UPDATE_CURRENT);
+ .getActivity(context, 80000, new Intent(context, RssFeedsActivity_.class), PendingIntent.FLAG_UPDATE_CURRENT);
String title = context.getResources().getQuantityString(R.plurals.rss_service_new, unread, Integer.toString(unread));
String forString = Collections2.joinString(hasUnread, ", ");
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationChannels.CHANNEL_RSS_CHECKER)
diff --git a/app/src/main/res/layout-w900dp/activity_rssfeeds.xml b/app/src/main/res/layout-w900dp/activity_rssfeeds.xml
index bd4390d3..997ccd2e 100644
--- a/app/src/main/res/layout-w900dp/activity_rssfeeds.xml
+++ b/app/src/main/res/layout-w900dp/activity_rssfeeds.xml
@@ -19,7 +19,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context=".core.gui.rss.RssfeedsActivity_">
+ tools:context=".core.gui.rss.RssFeedsActivity_">
-
-
-
-
-
+ android:baselineAligned="false"
+ android:orientation="horizontal">
+
+
+
diff --git a/app/src/main/res/layout/activity_remoterss.xml b/app/src/main/res/layout/activity_remoterss.xml
deleted file mode 100644
index 0d28fa6b..00000000
--- a/app/src/main/res/layout/activity_remoterss.xml
+++ /dev/null
@@ -1,79 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_rssfeeds.xml b/app/src/main/res/layout/activity_rssfeeds.xml
index 6fb3f0e4..436fd436 100644
--- a/app/src/main/res/layout/activity_rssfeeds.xml
+++ b/app/src/main/res/layout/activity_rssfeeds.xml
@@ -19,7 +19,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context=".core.gui.rss.RssfeedsActivity_">
+ tools:context=".core.gui.rss.RssFeedsActivity_">
-
+ android:layout_marginTop="-2dp">
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_rssitems.xml b/app/src/main/res/layout/activity_rssitems.xml
index 15e62d88..1350234e 100644
--- a/app/src/main/res/layout/activity_rssitems.xml
+++ b/app/src/main/res/layout/activity_rssitems.xml
@@ -19,7 +19,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context=".core.gui.rss.RssitemsActivity_">
+ tools:context=".core.gui.rss.RssItemsActivity_">
.
-->
-
+
+
+
-
-
-
-
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ tools:listitem="@layout/list_item_remoterssitem"
+ tools:visibility="visible" />
+
diff --git a/app/src/main/res/menu/activity_torrents_main.xml b/app/src/main/res/menu/activity_torrents_main.xml
index ba8b6e5c..01fc6115 100644
--- a/app/src/main/res/menu/activity_torrents_main.xml
+++ b/app/src/main/res/menu/activity_torrents_main.xml
@@ -28,11 +28,6 @@
android:icon="@drawable/ic_action_rss"
android:title="@string/action_rss"
app:showAsAction="ifRoom" />
-
- Visit transdroid.org
Close
- Select server and filter
- Close filters list
- %1$s allows you to monitor and manage the torrent client you run at home or on your seedbox. Setting things up can be a bit tricky, but we offer step-by-step guides and promise it\'ll be worth it!
- Connected, but no torrents are active within the current filter
- Select a torrent to view its details
- Servers
- Status
- Labels
- All
- Downloading
- Seeding
- Active
- Inactive
-
- - %1$d torrent selected
- - %1$d torrents selected
-
-
- - %1$d files selected
- - %1$d files selected
-
- Select all
- Select finished
- Invert selection
- Add torrent to…
- http://…
+ Select server and filter
+ Close filters list
+ %1$s allows you to monitor and manage the torrent client you run at home or on your seedbox. Setting things up can be a bit tricky, but we offer step-by-step guides and promise it\'ll be worth it!
+ Connected, but no torrents are active within the current filter
+ Select a torrent to view its details
+ Servers
+ Status
+ Labels
+ All
+ Downloading
+ Seeding
+ Active
+ Inactive
+
+ - %1$d torrent selected
+ - %1$d torrents selected
+
+
+ - %1$d files selected
+ - %1$d files selected
+
+ Select all
+ Select finished
+ Invert selection
+ Add torrent to…
+ http://…
+ Transdroid
+ Server
- STATUS: %1$s
- Waiting to check…
- Verifying local data…
- Waiting to download %s
- Error…
- %1$s OF %2$s (%3$s)
- %1$s, UPLOADED %2$s
- SINCE %1$s
- ~ %1$s
- ETA %1$s
- OF %1$s
- UNKNOWN ETA
- RATIO %1$s
- %1$s OF %2$s SEEDERS
- %1$s OF %2$s LEECHERS
- ↑ %1$s
- ↓ %1$s
- %1$s ↓
- Downloading
- Seeding
- Paused
- Queued
- Stopped
- Unknown status
- Not downloaded
- Low priority
- Normal priority
- High priority
- PIECES
- TRACKERS
- ERRORS
- FILES
- Maximum transfer speeds
- MAX DOWNLOAD
- MAX UPLOAD
- KB/S
- Reset
- -
- Update
-
- - New torrent added
- - %1$s new torrents added
-
-
- - Torrent is finished
- - %1$s torrents are finished
-
-
- - %1$s added, %2$s finished torrent
- - %1$s added, %2$s finished torrents
-
- %1$s and others
-
- All labels
- Unlabeled
- New label
- Setting a label is not supported by your client
- PICK A LABEL
- NEW LABEL
- Remove label
- E.g. movies or linux
-
- %1$s added (refreshing)
- %1$s removed
- %1$s removed and data deleted
- %1$s resumed (refreshing)
- %1$s stopped (refreshing)
- %1$s started (refreshing)
- %1$s paused (refreshing)
- Torrents paused (refreshing)
- Torrents resumed (refreshing)
- Torrents stopped (refreshing)
- Torrents started (refreshing)
- Trackers updated
- Label set to \'%1$s\'
- Label removed
- %1$s is downloading %2$s
- normally
- sequentially
- %1$s has %2$s
- first and last piece priority
- normal piece priority
- Checking %1$s data
- Torrent moved to \'%1$s\'
- File priorities updated
- Maximum transfer speeds set
-
- Torrent search
- \'%1$s\' on %2$s
- Search for torrents
- No results for your query
- S: %1$s
- L: %1$s
- This feature requires a one-time installation of the Torrent Search module. Click download to get the install package (apk) from transdroid.org and restart your search.
- Download module
- Opening details for %1$s
- The Barcode Scanner could not be found. Would you like to install it from the Play Store?
- No compatible file manager could not be found. Would you like to install IO File Manager from the Play Store?
-
- - %1$d result selected
- - %1$d results selected
-
-
- RSS feeds
- You have not defined any RSS feeds yet to monitor. Torrent-specific RSS feeds keep you up to date with new releases and you are notified of new items.
- Select an RSS feed to view the new items
- The RSS feed is not available or it contains no items
- Sorry, please wait until the RSS feed is loaded
- Sorry, this RSS feed could not be loaded at this time
-
- - %1$d item selected
- - %1$d items selected
-
-
- - New RSS feed torrent available
- - %1$s new RSS feed torrents
-
- New torrents for %1$s
+ STATUS: %1$s
+ Waiting to check…
+ Verifying local data…
+ Waiting to download %s
+ Error…
+ %1$s OF %2$s (%3$s)
+ %1$s, UPLOADED %2$s
+ SINCE %1$s
+ ~ %1$s
+ ETA %1$s
+ OF %1$s
+ UNKNOWN ETA
+ RATIO %1$s
+ %1$s OF %2$s SEEDERS
+ %1$s OF %2$s LEECHERS
+ ↑ %1$s
+ ↓ %1$s
+ %1$s ↓
+ Downloading
+ Seeding
+ Paused
+ Queued
+ Stopped
+ Unknown status
+ Not downloaded
+ Low priority
+ Normal priority
+ High priority
+ PIECES
+ TRACKERS
+ ERRORS
+ FILES
+ Maximum transfer speeds
+ MAX DOWNLOAD
+ MAX UPLOAD
+ KB/S
+ Reset
+ -
+ Update
+
+ - New torrent added
+ - %1$s new torrents added
+
+
+ - Torrent is finished
+ - %1$s torrents are finished
+
+
+ - %1$s added, %2$s finished torrent
+ - %1$s added, %2$s finished torrents
+
+ %1$s and others
+
+ All labels
+ Unlabeled
+ New label
+ Setting a label is not supported by your client
+ PICK A LABEL
+ NEW LABEL
+ Remove label
+ E.g. movies or linux
+
+ %1$s added (refreshing)
+ %1$s removed
+ %1$s removed and data deleted
+ %1$s resumed (refreshing)
+ %1$s stopped (refreshing)
+ %1$s started (refreshing)
+ %1$s paused (refreshing)
+ Torrents paused (refreshing)
+ Torrents resumed (refreshing)
+ Torrents stopped (refreshing)
+ Torrents started (refreshing)
+ Trackers updated
+ Label set to \'%1$s\'
+ Label removed
+ %1$s is downloading %2$s
+ normally
+ sequentially
+ %1$s has %2$s
+ first and last piece priority
+ normal piece priority
+ Checking %1$s data
+ Torrent moved to \'%1$s\'
+ File priorities updated
+ Maximum transfer speeds set
+
+ Torrent search
+ \'%1$s\' on %2$s
+ Search for torrents
+ No results for your query
+ S: %1$s
+ L: %1$s
+ This feature requires a one-time installation of the Torrent Search module. Click download to get the install package (apk) from transdroid.org and restart your search.
+ Download module
+ Opening details for %1$s
+ The Barcode Scanner could not be found. Would you like to install it from the Play Store?
+ No compatible file manager could not be found. Would you like to install IO File Manager from the Play Store?
+
+ - %1$d result selected
+ - %1$d results selected
+
+
+ RSS feeds
+ You have not defined any RSS feeds yet to monitor. Torrent-specific RSS feeds keep you up to date with new releases and you are notified of new items.
+ Select an RSS feed to view the new items
+ The RSS feed is not available or it contains no items
+ Sorry, please wait until the RSS feed is loaded
+ Sorry, this RSS feed could not be loaded at this time
+
+ - %1$d item selected
+ - %1$d items selected
+
+
+ - New RSS feed torrent available
+ - %1$s new RSS feed torrents
+
+ New torrents for %1$s
(All recent)
No torrent files found.\n\nAre your RSS feeds configured correctly?