From b9a31d46529f86e40b610b07e3e4f2f5a37591d7 Mon Sep 17 00:00:00 2001 From: Shinokuni Date: Sat, 20 Apr 2019 13:16:25 +0200 Subject: [PATCH] Implement paging for the items list and refactor the filter/sort logic using more queries --- app/build.gradle | 3 + .../readrops/app/activities/MainActivity.java | 128 ++++++------------ .../readrops/app/database/dao/ItemDao.java | 45 +++++- .../app/repositories/ARepository.java | 4 - .../app/repositories/LocalFeedRepository.java | 37 +---- .../app/viewmodels/MainViewModel.java | 103 +++++++++++++- .../app/views/MainItemListAdapter.java | 3 +- .../localfeed/OperationCallback.java | 8 -- .../readropslibrary/localfeed/RSSQuery.java | 6 +- 9 files changed, 188 insertions(+), 149 deletions(-) delete mode 100644 readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/OperationCallback.java diff --git a/app/build.gradle b/app/build.gradle index 83ba4c33..90e65596 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,6 +48,9 @@ dependencies { implementation 'android.arch.lifecycle:extensions:1.1.1' implementation "android.arch.persistence.room:runtime:1.1.1" annotationProcessor "android.arch.persistence.room:compiler:1.1.1" + implementation 'android.arch.paging:runtime:1.0.1' + implementation 'android.arch.paging:common:1.0.1' + implementation "joda-time:joda-time:2.9.9" implementation 'org.jsoup:jsoup:1.11.3' diff --git a/app/src/main/java/com/readrops/app/activities/MainActivity.java b/app/src/main/java/com/readrops/app/activities/MainActivity.java index 8b79ed21..1fce9ea3 100644 --- a/app/src/main/java/com/readrops/app/activities/MainActivity.java +++ b/app/src/main/java/com/readrops/app/activities/MainActivity.java @@ -1,13 +1,9 @@ package com.readrops.app.activities; import android.arch.lifecycle.ViewModelProviders; +import android.arch.paging.PagedList; import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.RectF; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; @@ -15,7 +11,6 @@ import android.support.annotation.Nullable; import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.AppCompatActivity; -import android.support.v7.content.res.AppCompatResources; import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -48,14 +43,10 @@ import com.readrops.app.database.pojo.ItemWithFeed; import com.readrops.app.utils.DrawerManager; import com.readrops.app.utils.GlideApp; import com.readrops.app.utils.SharedPreferencesManager; -import com.readrops.app.utils.Utils; import com.readrops.app.viewmodels.MainViewModel; import com.readrops.app.views.MainItemListAdapter; -import org.apache.commons.collections4.CollectionUtils; - import java.util.ArrayList; -import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -63,7 +54,6 @@ import java.util.Map; import io.reactivex.Observer; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; -import io.reactivex.observers.DisposableCompletableObserver; import io.reactivex.observers.DisposableSingleObserver; import io.reactivex.schedulers.Schedulers; @@ -81,8 +71,7 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou private Drawer drawer; private FloatingActionMenu actionMenu; - private List allItems; - private List filteredItems; + private PagedList allItems; private MainViewModel viewModel; private DrawerManager drawerManager; @@ -93,11 +82,7 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou private int feedCount; private int feedNb; - private int filterFeedId; - private boolean readItLater; - - private boolean showReadItems; - private ListSortType sortType; + private boolean scrollToTop; private ActionMode actionMode; @@ -113,17 +98,15 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou actionMenu = findViewById(R.id.fab_menu); viewModel = ViewModelProviders.of(this).get(MainViewModel.class); - allItems = new ArrayList<>(); + viewModel.setShowReadItems(SharedPreferencesManager.readBoolean(this, + SharedPreferencesManager.SharedPrefKey.SHOW_READ_ARTICLES)); - showReadItems = SharedPreferencesManager.readBoolean(this, - SharedPreferencesManager.SharedPrefKey.SHOW_READ_ARTICLES); - - viewModel.getItemsWithFeed().observe(this, (itemWithFeeds -> { + viewModel.getItemsWithFeed().observe(this, itemWithFeeds -> { allItems = itemWithFeeds; if (!refreshLayout.isRefreshing()) - filterItems(filterFeedId); - })); + adapter.submitList(itemWithFeeds); + }); refreshLayout = findViewById(R.id.swipe_refresh_layout); refreshLayout.setOnRefreshListener(this); @@ -134,7 +117,6 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou feedCount = 0; initRecyclerView(); - sortType = ListSortType.NEWEST_TO_OLDEST; drawer = new DrawerBuilder() .withActivity(this) @@ -154,65 +136,24 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou if (drawerItem instanceof PrimaryDrawerItem) { drawer.closeDrawer(); int id = (int)drawerItem.getIdentifier(); - filterFeedId = 0; switch (id) { case DrawerManager.ARTICLES_ITEM_ID: - readItLater = false; - filterItems(0); + viewModel.setFilterType(MainViewModel.FilterType.NO_FILTER); + viewModel.invalidate(); break; case DrawerManager.READ_LATER_ID: - readItLater = true; - filterItems(0); + viewModel.setFilterType(MainViewModel.FilterType.READ_IT_LATER_FILTER); + viewModel.invalidate(); break; } } else if (drawerItem instanceof SecondaryDrawerItem) { - readItLater = false; drawer.closeDrawer(); - filterItems((int)drawerItem.getIdentifier()); - } - } - private void filterItems(int id) { - filterFeedId = id; - filteredItems = new ArrayList<>(allItems); - - CollectionUtils.filter(filteredItems, object -> { - boolean showRead; - if (object.getItem().isRead()) - showRead = (object.getItem().isRead() == showReadItems); - else - showRead = true; // item unread - - if (id != 0) { - if (readItLater) - return object.getItem().isReadItLater() && object.getFeedId() == id && showRead; - else - return !object.getItem().isReadItLater() && object.getFeedId() == id && showRead; - } else { - if (readItLater) - return object.getItem().isReadItLater() && showRead; - else - return !object.getItem().isReadItLater() && showRead; - } - }); - - sortItems(); - adapter.submitList(filteredItems); - } - - private void sortItems() { - switch (sortType) { - case OLDEST_TO_NEWEST: - Collections.sort(filteredItems, ((o1, o2) -> o1.getItem().getPubDate().compareTo(o2.getItem().getPubDate()))); - break; - case NEWEST_TO_OLDEST: - Collections.sort(filteredItems, ((o1, o2) -> -1 * o1.getItem().getPubDate().compareTo(o2.getItem().getPubDate()))); - break; - default: - Collections.sort(filteredItems, ((o1, o2) -> -1 * o1.getItem().getPubDate().compareTo(o2.getItem().getPubDate()))); - break; + viewModel.setFilterFeedId((int) drawerItem.getIdentifier()); + viewModel.setFilterType(MainViewModel.FilterType.FEED_FILTER); + viewModel.invalidate(); } } @@ -392,7 +333,7 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou .observeOn(AndroidSchedulers.mainThread()) .subscribe(); - if (readItLater) + if (viewModel.getFilterType() == MainViewModel.FilterType.READ_IT_LATER_FILTER) adapter.notifyItemChanged(viewHolder.getAdapterPosition()); } } @@ -406,7 +347,19 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { @Override public void onItemRangeInserted(int positionStart, int itemCount) { - recyclerView.scrollToPosition(0); + if (scrollToTop) { + recyclerView.scrollToPosition(0); + scrollToTop = false; + } + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + if (scrollToTop) { + recyclerView.scrollToPosition(0); + scrollToTop = false; + } else + super.onItemRangeMoved(fromPosition, toPosition, itemCount); } }); } @@ -501,8 +454,9 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou syncProgressLayout.setVisibility(View.GONE); refreshLayout.setRefreshing(false); + scrollToTop = true; adapter.submitList(allItems); - filterItems(filterFeedId); + updateDrawerFeeds(); // update drawer after syncing feeds } }); @@ -513,7 +467,7 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou getMenuInflater().inflate(R.menu.item_list_menu, menu); MenuItem articlesItem = menu.findItem(R.id.item_filter_read_items); - articlesItem.setChecked(showReadItems); + articlesItem.setChecked(viewModel.showReadItems()); return true; } @@ -524,17 +478,17 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou case R.id.item_filter_read_items: if (item.isChecked()) { item.setChecked(false); - showReadItems = false; + viewModel.setShowReadItems(false); SharedPreferencesManager.writeValue(this, SharedPreferencesManager.SharedPrefKey.SHOW_READ_ARTICLES, false); } else { item.setChecked(true); - showReadItems = true; + viewModel.setShowReadItems(true); SharedPreferencesManager.writeValue(this, SharedPreferencesManager.SharedPrefKey.SHOW_READ_ARTICLES, true); } - filterItems(filterFeedId); + viewModel.invalidate(); return true; case R.id.item_sort: displayFilterDialog(); @@ -545,7 +499,7 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou } private void displayFilterDialog() { - int index = sortType == ListSortType.OLDEST_TO_NEWEST ? 1 : 0; + int index = viewModel.getSortType() == ListSortType.OLDEST_TO_NEWEST ? 1 : 0; new MaterialDialog.Builder(this) .title(getString(R.string.filter)) @@ -554,14 +508,12 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou String[] items = getResources().getStringArray(R.array.filter_items); if (text.toString().equals(items[0])) - sortType = ListSortType.NEWEST_TO_OLDEST; + viewModel.setSortType(ListSortType.NEWEST_TO_OLDEST); else - sortType = ListSortType.OLDEST_TO_NEWEST; - - sortItems(); - adapter.submitList(filteredItems); - adapter.notifyDataSetChanged(); + viewModel.setSortType(ListSortType.OLDEST_TO_NEWEST); + scrollToTop = true; + viewModel.invalidate(); return true; }) .show(); diff --git a/app/src/main/java/com/readrops/app/database/dao/ItemDao.java b/app/src/main/java/com/readrops/app/database/dao/ItemDao.java index 9035c821..dbd88d31 100644 --- a/app/src/main/java/com/readrops/app/database/dao/ItemDao.java +++ b/app/src/main/java/com/readrops/app/database/dao/ItemDao.java @@ -2,6 +2,8 @@ package com.readrops.app.database.dao; import android.arch.lifecycle.LiveData; +import android.arch.paging.DataSource; +import android.arch.paging.PageKeyedDataSource; import android.arch.persistence.room.Dao; import android.arch.persistence.room.Insert; import android.arch.persistence.room.Query; @@ -14,14 +16,45 @@ import java.util.List; @Dao public interface ItemDao { - @Query("Select * from Item Where feed_id = :feedId") - LiveData> getAllByFeed(int feedId); + String SELECT_ALL_FIELDS = "Item.id, title, clean_description, image_link, pub_date, read, read_it_later, " + + "Feed.name, text_color, background_color, icon_url, read_time, Feed.id as feedId, Folder.id as folder_id, " + + "Folder.name as folder_name"; - @Query("Select * from Item Order By pub_date DESC") - LiveData> getAll(); + String SELECT_ALL_JOIN = "Item Inner Join Feed, Folder on Item.feed_id = Feed.id And Folder.id = Feed.folder_id"; - @Query("Select Item.id, title, clean_description, image_link, pub_date, read, read_it_later, Feed.name, text_color, background_color, icon_url, read_time, Feed.id as feedId, Folder.id as folder_id, Folder.name as folder_name from Item Inner Join Feed, Folder on Item.feed_id = Feed.id And Folder.id = Feed.folder_id Order By Item.id DESC") - LiveData> getAllItemWithFeeds(); + String SELECT_ALL_ORDER_BY_ASC = "Order by Item.id DESC"; + + String SELECT_ALL_ORDER_BY_DESC = "Order By pub_date ASC"; + + @Query("Select " + SELECT_ALL_FIELDS + " from " + SELECT_ALL_JOIN + " Where feed_id = :feedId " + + "And read = :readState And read_it_later = 0 " + SELECT_ALL_ORDER_BY_ASC) + DataSource.Factory selectAllByFeedASC(int feedId, int readState); + + @Query("Select " + SELECT_ALL_FIELDS + " from " + SELECT_ALL_JOIN + " Where feed_id = :feedId " + + "And read = :readState And read_it_later = 0 " + SELECT_ALL_ORDER_BY_DESC) + DataSource.Factory selectAllByFeedsDESC(int feedId, int readState); + + @Query("Select " + SELECT_ALL_FIELDS + " from " + SELECT_ALL_JOIN + " Where read_it_later = 1 " + + "And read = :readState " + SELECT_ALL_ORDER_BY_ASC) + DataSource.Factory selectAllReadItLaterASC(int readState); + + @Query("Select " + SELECT_ALL_FIELDS + " from " + SELECT_ALL_JOIN + " Where read_it_later = 1 " + + "And read = :readState " + SELECT_ALL_ORDER_BY_DESC) + DataSource.Factory selectAllReadItLaterDESC(int readState); + + /** + * ASC means here from the newest (inserted) to the oldest + */ + @Query("Select " + SELECT_ALL_FIELDS + " From " + SELECT_ALL_JOIN + " Where read = :readState And " + + "read_it_later = 0 " + SELECT_ALL_ORDER_BY_ASC) + DataSource.Factory selectAllASC(int readState); + + /** + * DESC means here from the oldest to the newest + */ + @Query("Select " + SELECT_ALL_FIELDS + " From " + SELECT_ALL_JOIN + " Where read = :readState And " + + "read_it_later = 0 " + SELECT_ALL_ORDER_BY_DESC) + PageKeyedDataSource.Factory selectAllDESC(int readState); @Query("Select case When :guid In (Select guid from Item) Then 'true' else 'false' end") String guidExist(String guid); diff --git a/app/src/main/java/com/readrops/app/repositories/ARepository.java b/app/src/main/java/com/readrops/app/repositories/ARepository.java index b8ff4477..7dc4e096 100644 --- a/app/src/main/java/com/readrops/app/repositories/ARepository.java +++ b/app/src/main/java/com/readrops/app/repositories/ARepository.java @@ -36,12 +36,8 @@ public abstract class ARepository { public abstract Observable sync(List feeds); - public abstract void addFeed(ParsingResult result); - public abstract Single> addFeeds(List results); - public abstract void updateFeed(Feed feed); - public abstract void updateFeedWithFolder(FeedWithFolder feedWithFolder); public abstract Completable deleteFeed(int feedId); diff --git a/app/src/main/java/com/readrops/app/repositories/LocalFeedRepository.java b/app/src/main/java/com/readrops/app/repositories/LocalFeedRepository.java index 97b46b4c..f6011374 100644 --- a/app/src/main/java/com/readrops/app/repositories/LocalFeedRepository.java +++ b/app/src/main/java/com/readrops/app/repositories/LocalFeedRepository.java @@ -2,7 +2,7 @@ package com.readrops.app.repositories; import android.accounts.NetworkErrorException; import android.app.Application; -import android.arch.lifecycle.LiveData; +import android.arch.paging.PageKeyedDataSource; import android.support.annotation.Nullable; import com.readrops.app.database.entities.Folder; @@ -41,16 +41,11 @@ public class LocalFeedRepository extends ARepository { private static final String TAG = LocalFeedRepository.class.getSimpleName(); - private LiveData> itemsWhithFeed; + private PageKeyedDataSource.Factory itemsWhithFeed; public LocalFeedRepository(Application application) { super(application); - itemsWhithFeed = database.itemDao().getAllItemWithFeeds(); - } - - public LiveData> getItemsWithFeed() { - return itemsWhithFeed; } @Override @@ -106,23 +101,6 @@ public class LocalFeedRepository extends ARepository { }); } - @Override - public void addFeed(ParsingResult result) { - executor.execute(() -> { - try { - RSSQuery rssQuery = new RSSQuery(); - - RSSQueryResult queryResult = rssQuery.queryUrl(result.getUrl(), new HashMap<>()); - if (queryResult != null && queryResult.getException() == null) { - insertFeed(queryResult.getFeed(), queryResult.getRssType()); - } - - } catch (Exception e) { - - } - }); - } - @Override public Single> addFeeds(List results) { return Single.create(emitter -> { @@ -164,17 +142,6 @@ public class LocalFeedRepository extends ARepository { }); } - @Override - public void updateFeed(Feed feed) { - executor.execute(() -> { - try { - database.feedDao().update(feed); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - } - @Override public void updateFeedWithFolder(FeedWithFolder feedWithFolder) { executor.execute(() -> { diff --git a/app/src/main/java/com/readrops/app/viewmodels/MainViewModel.java b/app/src/main/java/com/readrops/app/viewmodels/MainViewModel.java index aaffced6..cffe389b 100644 --- a/app/src/main/java/com/readrops/app/viewmodels/MainViewModel.java +++ b/app/src/main/java/com/readrops/app/viewmodels/MainViewModel.java @@ -1,10 +1,19 @@ package com.readrops.app.viewmodels; import android.app.Application; +import android.arch.core.util.Function; import android.arch.lifecycle.AndroidViewModel; +import android.arch.lifecycle.LifecycleOwner; import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.MediatorLiveData; +import android.arch.lifecycle.Observer; +import android.arch.lifecycle.Transformations; +import android.arch.paging.DataSource; +import android.arch.paging.LivePagedListBuilder; +import android.arch.paging.PagedList; import android.support.annotation.NonNull; +import com.readrops.app.activities.MainActivity; import com.readrops.app.database.Database; import com.readrops.app.database.entities.Feed; import com.readrops.app.database.entities.Folder; @@ -22,19 +31,101 @@ import io.reactivex.Single; public class MainViewModel extends AndroidViewModel { - private LiveData> itemsWithFeed; + private MediatorLiveData> itemsWithFeed; + private LiveData> lastFetch; private LocalFeedRepository repository; private Database db; + private boolean showReadItems; + private FilterType filterType; + private MainActivity.ListSortType sortType; + private int filterFeedId; + public MainViewModel(@NonNull Application application) { super(application); + filterType = FilterType.NO_FILTER; + sortType = MainActivity.ListSortType.NEWEST_TO_OLDEST; + repository = new LocalFeedRepository(application); - itemsWithFeed = repository.getItemsWithFeed(); db = Database.getInstance(application); + + itemsWithFeed = new MediatorLiveData<>(); + + buildPagedList(); } - public LiveData> getItemsWithFeed() { + private void buildPagedList() { + DataSource.Factory items; + int readState = showReadItems ? 1 : 0; + + switch (filterType) { + case FEED_FILTER: + if (sortType == MainActivity.ListSortType.NEWEST_TO_OLDEST) + items = db.itemDao().selectAllByFeedASC(filterFeedId, readState); + else + items = db.itemDao().selectAllByFeedsDESC(filterFeedId, readState); + break; + case READ_IT_LATER_FILTER: + if (sortType == MainActivity.ListSortType.NEWEST_TO_OLDEST) + items = db.itemDao().selectAllReadItLaterASC(readState); + else + items = db.itemDao().selectAllReadItLaterDESC(readState); + break; + default: + if (sortType == MainActivity.ListSortType.NEWEST_TO_OLDEST) + items = db.itemDao().selectAllASC(readState); + else + items = db.itemDao().selectAllDESC(readState); + break; + } + + if (lastFetch != null) + itemsWithFeed.removeSource(lastFetch); + + lastFetch = new LivePagedListBuilder<>(items, new PagedList.Config.Builder() + .setPageSize(40) + .setPrefetchDistance(80) + .setEnablePlaceholders(false) + .build()) + .build(); + + itemsWithFeed.addSource(lastFetch, itemWithFeeds -> itemsWithFeed.setValue(itemWithFeeds)); + } + + public void invalidate() { + buildPagedList(); + } + + public void setShowReadItems(boolean showReadItems) { + this.showReadItems = showReadItems; + } + + public boolean showReadItems() { + return showReadItems; + } + + public void setFilterType(FilterType filterType) { + this.filterType = filterType; + } + + public FilterType getFilterType() { + return filterType; + } + + public void setSortType(MainActivity.ListSortType sortType) { + this.sortType = sortType; + } + + public MainActivity.ListSortType getSortType() { + return sortType; + } + + public void setFilterFeedId(int filterFeedId) { + this.filterFeedId = filterFeedId; + } + + public MediatorLiveData> getItemsWithFeed() { return itemsWithFeed; } @@ -89,4 +180,10 @@ public class MainViewModel extends AndroidViewModel { emitter.onComplete(); }); } + + public enum FilterType { + FEED_FILTER, + READ_IT_LATER_FILTER, + NO_FILTER + } } diff --git a/app/src/main/java/com/readrops/app/views/MainItemListAdapter.java b/app/src/main/java/com/readrops/app/views/MainItemListAdapter.java index 1c5b15be..d6da6043 100644 --- a/app/src/main/java/com/readrops/app/views/MainItemListAdapter.java +++ b/app/src/main/java/com/readrops/app/views/MainItemListAdapter.java @@ -1,5 +1,6 @@ package com.readrops.app.views; +import android.arch.paging.PagedListAdapter; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; @@ -43,7 +44,7 @@ import java.util.List; import static com.bumptech.glide.load.resource.bitmap.BitmapTransitionOptions.withCrossFade; -public class MainItemListAdapter extends ListAdapter implements ListPreloader.PreloadModelProvider { +public class MainItemListAdapter extends PagedListAdapter implements ListPreloader.PreloadModelProvider { private GlideRequests glideRequests; private OnItemClickListener listener; diff --git a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/OperationCallback.java b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/OperationCallback.java deleted file mode 100644 index 5cd89b87..00000000 --- a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/OperationCallback.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.readrops.readropslibrary.localfeed; - -public interface OperationCallback { - - void onSuccess(); - - void OnFailure(); -} diff --git a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/RSSQuery.java b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/RSSQuery.java index c767ee5a..2f30b8cc 100644 --- a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/RSSQuery.java +++ b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/RSSQuery.java @@ -38,7 +38,7 @@ public class RSSQuery { /** * Request the url given in parameter. - * This method is synchronous, it has to be called from another thread than the main one. + * This method is synchronous, it has to be called from another thread than the main one. * @param url url to request * @throws Exception */ @@ -116,8 +116,6 @@ public class RSSQuery { } } - - /** * Parse input feed * @param stream source to parse @@ -145,7 +143,7 @@ public class RSSQuery { switch (type) { case RSS_2: feed = serializer.read(RSSFeed.class, xml); - if (((RSSFeed)feed).getChannel().getFeedUrl() == null) // workaround si the channel does not have any atom:link tag + if (((RSSFeed)feed).getChannel().getFeedUrl() == null) // workaround if the channel does not have any atom:link tag ((RSSFeed)feed).getChannel().getLinks().add(new RSSLink(null, response.request().url().toString())); feed.setEtag(etag); feed.setLastModified(lastModified);