From 88aa232f8103fd3cbee065e4b0a7b765f616f831 Mon Sep 17 00:00:00 2001 From: Shinokuni Date: Tue, 22 Jan 2019 22:51:18 +0000 Subject: [PATCH] Add a basic feed request and display with some android architect components --- .idea/caches/build_file_checksums.ser | Bin 592 -> 592 bytes LICENSE | 2 +- app/build.gradle | 3 + .../java/com/readrops/app/ARepository.java | 44 +++++ .../com/readrops/app/LocalFeedRepository.java | 130 ++++++++++++ .../java/com/readrops/app/MainActivity.java | 52 ++--- .../java/com/readrops/app/MainAdapter.java | 13 +- .../java/com/readrops/app/MainViewModel.java | 43 ++++ .../java/com/readrops/app/SimpleCallback.java | 8 + .../com/readrops/app/database/Database.java | 47 +++++ .../readrops/app/database/dao/FeedDao.java | 27 +++ .../readrops/app/database/dao/ItemDao.java | 30 +++ .../readrops/app/database/entities/Feed.java | 70 +++++++ .../readrops/app/database/entities/Item.java | 185 ++++++++++++++++++ gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 10 +- .../readropslibrary/QueryCallback.java | 14 ++ .../readropslibrary/localfeed/AItem.java | 7 + .../localfeed/OperationCallback.java | 8 + .../readropslibrary/localfeed/RSSNetwork.java | 71 ++++++- .../localfeed/atom/ATOMEntry.java | 61 +++++- .../localfeed/atom/ATOMFeed.java | 47 +++++ .../localfeed/atom/ATOMLink.java | 2 + .../localfeed/json/JSONAuthor.java | 2 + .../localfeed/json/JSONItem.java | 11 +- .../localfeed/rss/RSSItem.java | 4 +- 26 files changed, 839 insertions(+), 55 deletions(-) create mode 100644 app/src/main/java/com/readrops/app/ARepository.java create mode 100644 app/src/main/java/com/readrops/app/LocalFeedRepository.java create mode 100644 app/src/main/java/com/readrops/app/MainViewModel.java create mode 100644 app/src/main/java/com/readrops/app/SimpleCallback.java create mode 100644 app/src/main/java/com/readrops/app/database/Database.java create mode 100644 app/src/main/java/com/readrops/app/database/dao/FeedDao.java create mode 100644 app/src/main/java/com/readrops/app/database/dao/ItemDao.java create mode 100644 app/src/main/java/com/readrops/app/database/entities/Feed.java create mode 100644 app/src/main/java/com/readrops/app/database/entities/Item.java create mode 100644 readropslibrary/src/main/java/com/readrops/readropslibrary/QueryCallback.java create mode 100644 readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/AItem.java create mode 100644 readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/OperationCallback.java diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 22ce2ab86df7b1d558d728fb708936e87b8212c6..2d2630691eebab96cf15027ba7026989702b87d6 100644 GIT binary patch delta 33 rcmV++0N(%51kePKm;_SgTkWx&qX7}2>W2txw!jR!Vz<6V+lg^_5Hb*f delta 33 rcmV++0N(%51kePKm;^`hk`%F=qX7}!>~bTy8DGdlC!IxHsTdh}`V0;R diff --git a/LICENSE b/LICENSE index 2dbc93c0..4c285030 100644 --- a/LICENSE +++ b/LICENSE @@ -96,7 +96,7 @@ TERMS AND CONDITIONS 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. - Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. + Notwithstanding any other provision of this License, you have permission to url or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. diff --git a/app/build.gradle b/app/build.gradle index 0b253764..9e6b450c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,4 +40,7 @@ dependencies { implementation 'com.squareup.retrofit2:converter-simplexml:2.4.0' implementation 'com.android.support:recyclerview-v7:28.0.0' + + implementation "android.arch.persistence.room:runtime:1.1.1" + annotationProcessor "android.arch.persistence.room:compiler:1.1.1" } diff --git a/app/src/main/java/com/readrops/app/ARepository.java b/app/src/main/java/com/readrops/app/ARepository.java new file mode 100644 index 00000000..882886f1 --- /dev/null +++ b/app/src/main/java/com/readrops/app/ARepository.java @@ -0,0 +1,44 @@ +package com.readrops.app; + +import android.app.Application; +import android.os.Handler; +import android.os.Looper; + +import com.readrops.app.database.Database; +import com.readrops.app.database.entities.Item; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +public abstract class ARepository { + + protected Executor executor; + protected SimpleCallback callback; + + protected Database database; + + protected ARepository(Application application) { + executor = Executors.newSingleThreadExecutor(); + this.database = Database.getInstance(application); + } + + public void setCallback(SimpleCallback callback) { + this.callback = callback; + } + + public abstract void sync(); + + public abstract void addFeed(Item item); + + public abstract void deleteFeed(Item item); + + public abstract void moveFeed(Item item); + + protected void failureCallBackInMainThread(Exception ex) { + Handler handler = new Handler(Looper.getMainLooper()); + + handler.post(() -> { + callback.onFailure(ex); + }); + } +} diff --git a/app/src/main/java/com/readrops/app/LocalFeedRepository.java b/app/src/main/java/com/readrops/app/LocalFeedRepository.java new file mode 100644 index 00000000..21eb8f56 --- /dev/null +++ b/app/src/main/java/com/readrops/app/LocalFeedRepository.java @@ -0,0 +1,130 @@ +package com.readrops.app; + +import android.app.Application; +import android.arch.lifecycle.LiveData; +import android.os.Handler; + +import com.readrops.app.database.Database; +import com.readrops.app.database.entities.Feed; +import com.readrops.app.database.entities.Item; +import com.readrops.readropslibrary.QueryCallback; +import com.readrops.readropslibrary.localfeed.AItem; +import com.readrops.readropslibrary.localfeed.RSSNetwork; +import com.readrops.readropslibrary.localfeed.atom.ATOMEntry; +import com.readrops.readropslibrary.localfeed.json.JSONItem; +import com.readrops.readropslibrary.localfeed.rss.RSSItem; + +import java.util.List; + +import static com.readrops.readropslibrary.localfeed.RSSNetwork.RSSType.RSS_2; +import static com.readrops.readropslibrary.localfeed.RSSNetwork.RSSType.RSS_ATOM; +import static com.readrops.readropslibrary.localfeed.RSSNetwork.RSSType.RSS_JSON; + +public class LocalFeedRepository extends ARepository implements QueryCallback { + + private LiveData> items; + + public LocalFeedRepository(Application application) { + super(application); + + items = database.itemDao().getAll(); + } + + public LiveData> getItems() { + return items; + } + + @Override + public void sync() { + executor.execute(() -> { + RSSNetwork request = new RSSNetwork(); + + List feeds = database.feedDao().getAllFeeds(); + + for (Feed feed : feeds) { + try { + request.request(feed.getUrl(), this); + } catch (Exception e) { + failureCallBackInMainThread(e); + } + } + + callback.onSuccess(); + }); + } + + @Override + public void addFeed(Item item) { + executor.execute(() -> { + + }); + } + + @Override + public void deleteFeed(Item item) { + + } + + @Override + public void moveFeed(Item item) { + + } + + + @Override + public void onSyncSuccess(List items, RSSNetwork.RSSType type, String feedUrl) { + switch (type) { + case RSS_2: + List rssItems = (List)items; + parseRSSItems(rssItems, feedUrl); + break; + case RSS_ATOM: + List atomItems = (List)items; + parseATOMItems(atomItems, feedUrl); + break; + case RSS_JSON: + List jsonItems = (List)items; + parseJSONItems(jsonItems, feedUrl); + break; + } + } + + @Override + public void onSyncFailure(Exception ex) { + failureCallBackInMainThread(ex); + } + + private void parseRSSItems(List items, String feedUrl) { + Feed feed = database.feedDao().getFeedByUrl(feedUrl); + + List dbItems = Item.itemsFromRSS(items, feed); + + for (Item dbItem : dbItems) { + if (!Boolean.valueOf(database.itemDao().guidExist(dbItem.getGuid()))) + database.itemDao().insert(dbItem); + } + } + + private void parseATOMItems(List items, String feedUrl) { + Feed feed = database.feedDao().getFeedByUrl(feedUrl); + + List dbItems = Item.itemsFromATOM(items, feed); + + for (Item dbItem : dbItems) { + if (!Boolean.valueOf(database.itemDao().guidExist(dbItem.getGuid()))) + database.itemDao().insert(dbItem); + } + } + + private void parseJSONItems(List items, String feedUrl) { + Feed feed = database.feedDao().getFeedByUrl(feedUrl); + + List dbItems = Item.itemsFromJSON(items, feed); + + for (Item dbItem : dbItems) { + if (!Boolean.valueOf(database.itemDao().guidExist(dbItem.getGuid()))) + database.itemDao().insert(dbItem); + } + } + +} diff --git a/app/src/main/java/com/readrops/app/MainActivity.java b/app/src/main/java/com/readrops/app/MainActivity.java index 812e27d7..6da77b57 100644 --- a/app/src/main/java/com/readrops/app/MainActivity.java +++ b/app/src/main/java/com/readrops/app/MainActivity.java @@ -1,5 +1,6 @@ package com.readrops.app; +import android.arch.lifecycle.ViewModelProvider; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.DividerItemDecoration; @@ -7,9 +8,15 @@ import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; +import com.readrops.app.database.entities.Item; import com.readrops.readropslibrary.PageParser; +import com.readrops.readropslibrary.QueryCallback; import com.readrops.readropslibrary.Utils.Utils; +import com.readrops.readropslibrary.localfeed.AItem; import com.readrops.readropslibrary.localfeed.RSSNetwork; +import com.readrops.readropslibrary.localfeed.atom.ATOMEntry; +import com.readrops.readropslibrary.localfeed.json.JSONFeed; +import com.readrops.readropslibrary.localfeed.json.JSONItem; import com.readrops.readropslibrary.localfeed.rss.RSSFeed; import com.readrops.readropslibrary.localfeed.rss.RSSItem; @@ -31,8 +38,9 @@ public class MainActivity extends AppCompatActivity { private RecyclerView recyclerView; private MainAdapter adapter; - private List itemList; + private List itemList; + private MainViewModel viewModel; @Override protected void onCreate(Bundle savedInstanceState) { @@ -50,42 +58,14 @@ public class MainActivity extends AppCompatActivity { thread.start();*/ - getItems(); + viewModel = ViewModelProvider.AndroidViewModelFactory.getInstance(this.getApplication()).create(MainViewModel.class); - } - - private void getItems() { - RSSNetwork request = new RSSNetwork(); - - request.request("https://www.numerama.com/feed/", new Callback() { - @Override - public void onFailure(Call call, IOException e) { - - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - if (response.isSuccessful()) { - InputStream stream = response.body().byteStream(); - String xml = Utils.inputStreamToString(stream); - - Serializer serializer = new Persister(); - - try { - RSSFeed rssFeed = serializer.read(RSSFeed.class, xml); - itemList = rssFeed.getChannel().getItems(); - - runOnUiThread(() -> { - initRecyclerView(); - }); - - - } catch (Exception e) { - e.printStackTrace(); - } - } - } + viewModel.getItems().observe(this, (List items) -> { + this.itemList = items; + initRecyclerView(); }); + + viewModel.sync(); } private void initRecyclerView() { @@ -99,6 +79,4 @@ public class MainActivity extends AppCompatActivity { recyclerView.setAdapter(adapter); } - - } diff --git a/app/src/main/java/com/readrops/app/MainAdapter.java b/app/src/main/java/com/readrops/app/MainAdapter.java index 8caaa50f..2f35195a 100644 --- a/app/src/main/java/com/readrops/app/MainAdapter.java +++ b/app/src/main/java/com/readrops/app/MainAdapter.java @@ -12,6 +12,7 @@ import android.widget.ImageView; import android.widget.TextView; import com.bumptech.glide.Glide; +import com.readrops.app.database.entities.Item; import com.readrops.readropslibrary.PageParser; import com.readrops.readropslibrary.localfeed.rss.RSSItem; @@ -19,10 +20,10 @@ import java.util.List; public class MainAdapter extends RecyclerView.Adapter { - List items; + List items; private Context context; - public MainAdapter(Context context, List items) { + public MainAdapter(Context context, List items) { this.context = context; this.items = items; } @@ -38,13 +39,13 @@ public class MainAdapter extends RecyclerView.Adapter { @Override public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) { - RSSItem item = items.get(i); + Item item = items.get(i); viewHolder.bind(item); - Thread thread = new Thread(() -> { + /*Thread thread = new Thread(() -> { String imageUrl = PageParser.getOGImageLink(item.getLink()); Glide.with(context).load(imageUrl).into(viewHolder.itemImage); - }); + });*/ } @@ -66,7 +67,7 @@ public class MainAdapter extends RecyclerView.Adapter { itemImage = itemView.findViewById(R.id.item_image); } - private void bind(RSSItem item) { + private void bind(Item item) { itemTitle.setText(item.getTitle()); } } diff --git a/app/src/main/java/com/readrops/app/MainViewModel.java b/app/src/main/java/com/readrops/app/MainViewModel.java new file mode 100644 index 00000000..95bd996e --- /dev/null +++ b/app/src/main/java/com/readrops/app/MainViewModel.java @@ -0,0 +1,43 @@ +package com.readrops.app; + +import android.app.Application; +import android.arch.lifecycle.AndroidViewModel; +import android.arch.lifecycle.LiveData; +import android.support.annotation.NonNull; + +import com.readrops.app.database.entities.Item; + +import java.util.List; + +public class MainViewModel extends AndroidViewModel implements SimpleCallback { + + private LiveData> items; + private LocalFeedRepository repository; + + public MainViewModel(@NonNull Application application) { + super(application); + + repository = new LocalFeedRepository(application); + repository.setCallback(this); + items = repository.getItems(); + } + + public LiveData> getItems() { + return items; + } + + public void sync() { + repository.sync(); + } + + + @Override + public void onSuccess() { + + } + + @Override + public void onFailure(Exception ex) { + + } +} diff --git a/app/src/main/java/com/readrops/app/SimpleCallback.java b/app/src/main/java/com/readrops/app/SimpleCallback.java new file mode 100644 index 00000000..34d7f4b3 --- /dev/null +++ b/app/src/main/java/com/readrops/app/SimpleCallback.java @@ -0,0 +1,8 @@ +package com.readrops.app; + +public interface SimpleCallback { + + void onSuccess(); + + void onFailure(Exception ex); +} diff --git a/app/src/main/java/com/readrops/app/database/Database.java b/app/src/main/java/com/readrops/app/database/Database.java new file mode 100644 index 00000000..1f0a7a9b --- /dev/null +++ b/app/src/main/java/com/readrops/app/database/Database.java @@ -0,0 +1,47 @@ +package com.readrops.app.database; + +import android.arch.persistence.db.SupportSQLiteDatabase; +import android.arch.persistence.room.Room; +import android.arch.persistence.room.RoomDatabase; +import android.content.Context; +import android.support.annotation.NonNull; + +import com.readrops.app.database.dao.FeedDao; +import com.readrops.app.database.dao.ItemDao; +import com.readrops.app.database.entities.Feed; +import com.readrops.app.database.entities.Item; + + +@android.arch.persistence.room.Database(entities = {Feed.class, Item.class}, version = 1) +public abstract class Database extends RoomDatabase { + + public abstract FeedDao feedDao(); + + public abstract ItemDao itemDao(); + + private static Database database; + + public static Database getInstance(Context context) { + if (database == null) + database = Room.databaseBuilder(context, Database.class, "readrops-db").addCallback(roomCallback).build(); + + return database; + } + + public static RoomDatabase.Callback roomCallback = new RoomDatabase.Callback() { + @Override + public void onCreate(@NonNull SupportSQLiteDatabase db) { + super.onCreate(db); + + Feed feed1 = new Feed("XDA Developers", "desc", "https://www.xda-developers.com/feed/"); + + new Thread(() -> database.feedDao().insert(feed1)).start(); + } + + @Override + public void onOpen(@NonNull SupportSQLiteDatabase db) { + super.onOpen(db); + } + }; + +} \ No newline at end of file diff --git a/app/src/main/java/com/readrops/app/database/dao/FeedDao.java b/app/src/main/java/com/readrops/app/database/dao/FeedDao.java new file mode 100644 index 00000000..0f24b119 --- /dev/null +++ b/app/src/main/java/com/readrops/app/database/dao/FeedDao.java @@ -0,0 +1,27 @@ +package com.readrops.app.database.dao; + + +import android.arch.persistence.room.Dao; +import android.arch.persistence.room.Insert; +import android.arch.persistence.room.Query; + +import com.readrops.app.database.entities.Feed; + +import java.util.List; + +@Dao +public interface FeedDao { + + @Query("Select * from Feed") + List getAllFeeds(); + + @Insert + void insert(Feed feed); + + @Query("Select count(*) from Feed") + int getFeedCount(); + + @Query("Select * from Feed Where url = :feedUrl") + Feed getFeedByUrl(String feedUrl); + +} 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 new file mode 100644 index 00000000..85344d8f --- /dev/null +++ b/app/src/main/java/com/readrops/app/database/dao/ItemDao.java @@ -0,0 +1,30 @@ +package com.readrops.app.database.dao; + + +import android.arch.lifecycle.LiveData; +import android.arch.persistence.room.Dao; +import android.arch.persistence.room.Insert; +import android.arch.persistence.room.Query; + +import com.readrops.app.database.entities.Item; + +import java.util.List; + +@Dao +public interface ItemDao { + + @Query("Select * from Item Where feed_id = :feedId") + LiveData> getAllByFeed(int feedId); + + @Query("Select * from Item") + LiveData> getAll(); + + @Query("Select case When :guid In (Select guid from Item) Then 'true' else 'false' end") + String guidExist(String guid); + + @Insert + void insert(Item item); + + @Insert + void insertAll(List items); +} diff --git a/app/src/main/java/com/readrops/app/database/entities/Feed.java b/app/src/main/java/com/readrops/app/database/entities/Feed.java new file mode 100644 index 00000000..2a6fdab3 --- /dev/null +++ b/app/src/main/java/com/readrops/app/database/entities/Feed.java @@ -0,0 +1,70 @@ +package com.readrops.app.database.entities; + + +import android.arch.persistence.room.*; + +import com.readrops.readropslibrary.localfeed.rss.RSSChannel; + +@Entity +public class Feed { + + @PrimaryKey(autoGenerate = true) + private int id; + + private String name; + + private String description; + + private String url; + + public Feed() { + + } + + public Feed(String name, String description, String url) { + this.name = name; + this.description = description; + this.url = url; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public static Feed feedFromRSS(RSSChannel channel) { + Feed feed = new Feed(); + + feed.setUrl(channel.getLink()); + feed.setDescription(channel.getDescription()); + + return feed; + } +} diff --git a/app/src/main/java/com/readrops/app/database/entities/Item.java b/app/src/main/java/com/readrops/app/database/entities/Item.java new file mode 100644 index 00000000..cd7da09d --- /dev/null +++ b/app/src/main/java/com/readrops/app/database/entities/Item.java @@ -0,0 +1,185 @@ +package com.readrops.app.database.entities; + +import android.arch.persistence.room.*; + +import com.readrops.readropslibrary.localfeed.atom.ATOMEntry; +import com.readrops.readropslibrary.localfeed.json.JSONItem; +import com.readrops.readropslibrary.localfeed.rss.RSSChannel; +import com.readrops.readropslibrary.localfeed.rss.RSSItem; + +import java.util.ArrayList; +import java.util.List; + + + +@Entity +(foreignKeys = @ForeignKey(entity = Feed.class, parentColumns = "id", childColumns = "feed_id")) +public class Item { + + @PrimaryKey(autoGenerate = true) + private int id; + + private String title; + + private String description; + + private String link; + + @ColumnInfo(name = "image_link") + private String imageLink; + + private String author; + + @ColumnInfo(name = "pub_date") + private String pubDate; + + private String content; + + @ColumnInfo(name = "feed_id", index = true) + private int feedId; + + @ColumnInfo(index = true) + private String guid; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getLink() { + return link; + } + + public void setLink(String link) { + this.link = link; + } + + public String getImageLink() { + return imageLink; + } + + public void setImageLink(String imageLink) { + this.imageLink = imageLink; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getPubDate() { + return pubDate; + } + + public void setPubDate(String pubDate) { + this.pubDate = pubDate; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getGuid() { + return guid; + } + + public void setGuid(String guid) { + this.guid = guid; + } + + public int getFeedId() { + return feedId; + } + + public void setFeedId(int feedId) { + this.feedId = feedId; + } + + public static List itemsFromRSS(List items, Feed feed) { + List dbItems = new ArrayList<>(); + + for(RSSItem item : items) { + Item newItem = new Item(); + + newItem.setAuthor(item.getAuthor()); + newItem.setContent(item.getContent()); + newItem.setDescription(item.getDescription()); + newItem.setGuid(item.getGuid()); + newItem.setTitle(item.getTitle()); + newItem.setImageLink(item.getImageLink()); + newItem.setPubDate(item.getPubDate()); + newItem.setLink(item.getLink()); + + newItem.setFeedId(feed.getId()); + + dbItems.add(newItem); + } + + return dbItems; + } + + public static List itemsFromATOM(List items, Feed feed) { + List dbItems = new ArrayList<>(); + + for (ATOMEntry item : items) { + Item dbItem = new Item(); + + dbItem.setContent(item.getContent()); + dbItem.setDescription(item.getSummary()); + dbItem.setGuid(item.getId()); + dbItem.setTitle(item.getTitle()); + dbItem.setPubDate(item.getUpdated()); + dbItem.setLink(item.getLink().getHref()); + + dbItem.setFeedId(feed.getId()); + } + + return dbItems; + } + + public static List itemsFromJSON(List items, Feed feed) { + List dbItems = new ArrayList<>(); + + for (JSONItem item : items) { + Item dbItem = new Item(); + + dbItem.setAuthor(item.getAuthor().getName()); + dbItem.setContent(item.getContent()); + dbItem.setDescription(item.getSummary()); + dbItem.setGuid(item.getId()); + dbItem.setTitle(item.getTitle()); + dbItem.setPubDate(item.getPubDate()); + dbItem.setLink(item.getUrl()); + + dbItem.setFeedId(feed.getId()); + } + + return dbItems; + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9a4163a4..94908edf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Tue Jan 15 18:55:37 GMT 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip diff --git a/gradlew b/gradlew index cccdd3d5..865855ec 100755 --- a/gradlew +++ b/gradlew @@ -7,16 +7,16 @@ ############################################################################## # Attempt to set APP_HOME -# Resolve links: $0 may be a link +# Resolve links: $0 may be a url PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" + url=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$url" : '/.*' > /dev/null; then + PRG="$url" else - PRG=`dirname "$PRG"`"/$link" + PRG=`dirname "$PRG"`"/$url" fi done SAVED="`pwd`" diff --git a/readropslibrary/src/main/java/com/readrops/readropslibrary/QueryCallback.java b/readropslibrary/src/main/java/com/readrops/readropslibrary/QueryCallback.java new file mode 100644 index 00000000..5af2ae16 --- /dev/null +++ b/readropslibrary/src/main/java/com/readrops/readropslibrary/QueryCallback.java @@ -0,0 +1,14 @@ +package com.readrops.readropslibrary; + +import com.readrops.readropslibrary.localfeed.AItem; +import com.readrops.readropslibrary.localfeed.RSSNetwork; + +import java.util.List; + +public interface QueryCallback { + + void onSyncSuccess(List items, RSSNetwork.RSSType type, String feedUrl); + + void onSyncFailure(Exception ex); + +} diff --git a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/AItem.java b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/AItem.java new file mode 100644 index 00000000..1fab7380 --- /dev/null +++ b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/AItem.java @@ -0,0 +1,7 @@ +package com.readrops.readropslibrary.localfeed; + +/** + * Just an abstract class to wrap the three rss items types + */ +public abstract class AItem { +} diff --git a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/OperationCallback.java b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/OperationCallback.java new file mode 100644 index 00000000..5cd89b87 --- /dev/null +++ b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/OperationCallback.java @@ -0,0 +1,8 @@ +package com.readrops.readropslibrary.localfeed; + +public interface OperationCallback { + + void onSuccess(); + + void OnFailure(); +} diff --git a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/RSSNetwork.java b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/RSSNetwork.java index 73b4ef6a..236d7e14 100644 --- a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/RSSNetwork.java +++ b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/RSSNetwork.java @@ -1,16 +1,83 @@ package com.readrops.readropslibrary.localfeed; +import com.google.gson.Gson; +import com.readrops.readropslibrary.QueryCallback; +import com.readrops.readropslibrary.Utils.Utils; +import com.readrops.readropslibrary.localfeed.atom.ATOMFeed; +import com.readrops.readropslibrary.localfeed.json.JSONFeed; +import com.readrops.readropslibrary.localfeed.rss.RSSFeed; + +import org.simpleframework.xml.Serializer; +import org.simpleframework.xml.core.Persister; + +import java.io.InputStream; +import java.util.List; + import okhttp3.OkHttpClient; import okhttp3.Request; +import okhttp3.Response; public class RSSNetwork { - public void request(String url, okhttp3.Callback callback) { + /** + * Request the url given in parameter. + * This method is synchronous, it has to be called from another thread than the main one. + * @param url url to request + * @param callback result callback if success or error + * @throws Exception + */ + public void request(String url, final QueryCallback callback) throws Exception { OkHttpClient okHttpClient = new OkHttpClient(); Request request = new Request.Builder().url(url).build(); + Response response = okHttpClient.newCall(request).execute(); - okHttpClient.newCall(request).enqueue(callback); + if (response.isSuccessful()) + parseFeed(response.body().byteStream(), getRSSType(response.header("content-type")), callback, url); + else + callback.onSyncFailure(new Exception("Error " + response.code() + " when requesting url " + url)); + } + + private void parseFeed(InputStream stream, RSSType type, QueryCallback callback, String url) throws Exception { + //String xml = Utils.inputStreamToString(stream); + Serializer serializer = new Persister(); + + switch (type) { + case RSS_2: + RSSFeed rssFeed = serializer.read(RSSFeed.class, stream); + callback.onSyncSuccess(rssFeed.getChannel().getItems(), type, url); + break; + case RSS_ATOM: + ATOMFeed atomFeed = serializer.read(ATOMFeed.class, stream); + callback.onSyncSuccess(atomFeed.getEntries(), type, url); + break; + case RSS_JSON: + Gson gson = new Gson(); + JSONFeed feed = gson.fromJson(Utils.inputStreamToString(stream), JSONFeed.class); + callback.onSyncSuccess(feed.getItems(), type, url); + break; + } + } + + private RSSType getRSSType(String contentType) { + if (contentType.contains(RSSType.RSS_2.value)) + return RSSType.RSS_2; + else if (contentType.contains(RSSType.RSS_ATOM.value)) + return RSSType.RSS_ATOM; + else + return RSSType.RSS_JSON; + } + + public enum RSSType { + RSS_2("rss"), + RSS_ATOM("atom"), + RSS_JSON("json"); + + private String value; + + RSSType(String value) { + this.value = value; + } } diff --git a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/atom/ATOMEntry.java b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/atom/ATOMEntry.java index 76f59836..cba712cd 100644 --- a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/atom/ATOMEntry.java +++ b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/atom/ATOMEntry.java @@ -1,11 +1,13 @@ package com.readrops.readropslibrary.localfeed.atom; +import com.readrops.readropslibrary.localfeed.AItem; + import org.simpleframework.xml.Attribute; import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; @Root(name = "entry", strict = false) -public class ATOMEntry { +public class ATOMEntry extends AItem { @Element(required = false) private String title; @@ -19,11 +21,68 @@ public class ATOMEntry { @Element(required = false) private String summary; + @Element(required = false) + private String id; + @Element(required = false) private String content; @Attribute(name = "type", required = false) private String contentType; + public String getTitle() { + return title; + } + public void setTitle(String title) { + this.title = title; + } + + public ATOMLink getLink() { + return link; + } + + public void setLink(ATOMLink link) { + this.link = link; + } + + public String getUpdated() { + return updated; + } + + public void setUpdated(String updated) { + this.updated = updated; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } } diff --git a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/atom/ATOMFeed.java b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/atom/ATOMFeed.java index 9d28f180..3974a7cd 100644 --- a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/atom/ATOMFeed.java +++ b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/atom/ATOMFeed.java @@ -27,4 +27,51 @@ public class ATOMFeed { @ElementList(inline = true, required = false) private List entries; + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getLink() { + return link; + } + + public void setLink(String link) { + this.link = link; + } + + public String getSubtitle() { + return subtitle; + } + + public void setSubtitle(String subtitle) { + this.subtitle = subtitle; + } + + public String getUpdated() { + return updated; + } + + public void setUpdated(String updated) { + this.updated = updated; + } + + public ATOMAuthor getAuthor() { + return author; + } + + public void setAuthor(ATOMAuthor author) { + this.author = author; + } + + public List getEntries() { + return entries; + } + + public void setEntries(List entries) { + this.entries = entries; + } } diff --git a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/atom/ATOMLink.java b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/atom/ATOMLink.java index 915eb85a..2245f991 100644 --- a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/atom/ATOMLink.java +++ b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/atom/ATOMLink.java @@ -12,4 +12,6 @@ public class ATOMLink { public String getHref() { return href; } + + } diff --git a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/json/JSONAuthor.java b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/json/JSONAuthor.java index 203badf6..059f432c 100644 --- a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/json/JSONAuthor.java +++ b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/json/JSONAuthor.java @@ -34,4 +34,6 @@ public class JSONAuthor { public void setAvatarUrl(String avatarUrl) { this.avatarUrl = avatarUrl; } + + } diff --git a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/json/JSONItem.java b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/json/JSONItem.java index f1f902e3..c5e90184 100644 --- a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/json/JSONItem.java +++ b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/json/JSONItem.java @@ -1,8 +1,9 @@ package com.readrops.readropslibrary.localfeed.json; import com.google.gson.annotations.SerializedName; +import com.readrops.readropslibrary.localfeed.AItem; -public class JSONItem { +public class JSONItem extends AItem { private String id; @@ -104,6 +105,14 @@ public class JSONItem { this.author = author; } + public String getContent() { + if (contentHtml == null) + return contentText; + else + return contentHtml; + + } + @SerializedName("date_modified") private String modDate; diff --git a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/rss/RSSItem.java b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/rss/RSSItem.java index 365b944f..a5fa8dd3 100644 --- a/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/rss/RSSItem.java +++ b/readropslibrary/src/main/java/com/readrops/readropslibrary/localfeed/rss/RSSItem.java @@ -1,11 +1,13 @@ package com.readrops.readropslibrary.localfeed.rss; +import com.readrops.readropslibrary.localfeed.AItem; + import org.simpleframework.xml.Element; import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Root; @Root(name = "item", strict = false) -public class RSSItem { +public class RSSItem extends AItem { @Element(name = "title", required = false) private String title;