Parse OPML file to get feeds and folders and insert them, for local accounts only

This commit is contained in:
Shinokuni 2019-11-04 13:48:42 +01:00
parent d4228ee953
commit ad28e44268
7 changed files with 152 additions and 9 deletions

View File

@ -39,6 +39,9 @@ public abstract class FolderDao implements BaseDao<Folder> {
@Query("Delete From Folder Where remoteId in (:ids)")
abstract void deleteByIds(List<String> ids);
@Query("Select * From Folder Where name = :name And account_id = :accountId")
public abstract Folder getFolderByName(String name, int accountId);
/**
* Insert, update and delete folders
*

View File

@ -4,6 +4,7 @@ package com.readrops.app.fragments.settings;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.Nullable;
@ -19,6 +20,7 @@ import com.readrops.app.activities.ManageFeedsFoldersActivity;
import com.readrops.app.database.entities.account.Account;
import com.readrops.app.database.entities.account.AccountType;
import com.readrops.app.viewmodels.AccountViewModel;
import com.readrops.readropslibrary.opml.OpmlParser;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.observers.DisposableCompletableObserver;
@ -103,6 +105,7 @@ public class AccountSettingsFragment extends PreferenceFragmentCompat {
super.onCreate(savedInstanceState);
viewModel = ViewModelProviders.of(this).get(AccountViewModel.class);
viewModel.setAccount(account);
}
private void deleteAccount() {
@ -132,15 +135,35 @@ public class AccountSettingsFragment extends PreferenceFragmentCompat {
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/*");
getActivity().startActivityForResult(intent, OPEN_OPML_FILE_REQUEST);
startActivityForResult(intent, OPEN_OPML_FILE_REQUEST);
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (requestCode == OPEN_OPML_FILE_REQUEST && requestCode == RESULT_OK && data != null) {
if (requestCode == OPEN_OPML_FILE_REQUEST && resultCode == RESULT_OK && data != null) {
Uri uri = data.getData();
parseOPMLFile(uri);
}
super.onActivityResult(requestCode, resultCode, data);
}
private void parseOPMLFile(Uri uri) {
OpmlParser.parse(uri, getContext())
.flatMapCompletable(opml -> viewModel.insertOPMLFoldersAndFeeds(opml))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableCompletableObserver() {
@Override
public void onComplete() {
Log.d("", "onComplete: ");
}
@Override
public void onError(Throwable e) {
Log.d("", "onError: ");
}
});
}
}

View File

@ -12,13 +12,14 @@ import com.readrops.app.database.entities.Folder;
import com.readrops.app.database.entities.Item;
import com.readrops.app.database.entities.account.Account;
import com.readrops.app.database.entities.account.AccountType;
import com.readrops.app.utils.feedscolors.FeedsColorsIntentService;
import com.readrops.app.utils.FeedInsertionResult;
import com.readrops.app.utils.ParsingResult;
import com.readrops.app.utils.feedscolors.FeedColorsKt;
import com.readrops.app.utils.feedscolors.FeedsColorsIntentService;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import io.reactivex.Completable;
import io.reactivex.Observable;
@ -48,6 +49,36 @@ public abstract class ARepository<T> {
public abstract Single<List<FeedInsertionResult>> addFeeds(List<ParsingResult> results);
public Completable insertOPMLFoldersAndFeeds(Map<Folder, List<Feed>> foldersAndFeeds) {
List<Completable> completableList = new ArrayList<>();
for (Map.Entry<Folder, List<Feed>> entry : foldersAndFeeds.entrySet()) {
Folder folder = entry.getKey();
folder.setAccountId(account.getId());
Completable completable = Single.<Integer>create(emitter -> {
Folder dbFolder = database.folderDao().getFolderByName(folder.getName(), account.getId());
if (dbFolder != null)
emitter.onSuccess(dbFolder.getId());
else
emitter.onSuccess((int) database.folderDao().compatInsert(folder));
}).flatMap(folderId -> {
List<Feed> feeds = entry.getValue();
for (Feed feed : feeds) {
feed.setFolderId(folderId);
}
List<ParsingResult> parsingResults = ParsingResult.toParsingResults(feeds);
return addFeeds(parsingResults);
}).flatMapCompletable(feedInsertionResults -> Completable.complete());
completableList.add(completable);
}
return Completable.concat(completableList);
}
public Completable updateFeed(Feed feed) {
return Completable.create(emitter -> {
database.feedDao().updateFeedFields(feed.getId(), feed.getName(), feed.getUrl(), feed.getFolderId());

View File

@ -122,7 +122,7 @@ public class LocalFeedRepository extends ARepository<Void> {
RSSQueryResult queryResult = rssNet.queryUrl(parsingResult.getUrl(), new HashMap<>());
if (queryResult != null && queryResult.getException() == null) {
Feed feed = insertFeed(queryResult.getFeed(), queryResult.getRssType());
Feed feed = insertFeed(queryResult.getFeed(), queryResult.getRssType(), parsingResult);
if (feed != null) {
insertionResult.setFeed(feed);
insertionResult.setParsingResult(parsingResult);
@ -150,8 +150,8 @@ public class LocalFeedRepository extends ARepository<Void> {
}
private void insertNewItems(AFeed feed, RSSQuery.RSSType type) throws ParseException {
Feed dbFeed = null;
List<Item> items = null;
Feed dbFeed;
List<Item> items;
switch (type) {
case RSS_2:
@ -166,6 +166,8 @@ public class LocalFeedRepository extends ARepository<Void> {
dbFeed = database.feedDao().getFeedByUrl(((JSONFeed) feed).getFeedUrl(), account.getId());
items = ItemMatcher.itemsFromJSON(((JSONFeed) feed).getItems(), dbFeed);
break;
default:
throw new IllegalArgumentException("Unknown RSS type");
}
database.feedDao().updateHeaders(dbFeed.getEtag(), dbFeed.getLastModified(), dbFeed.getId());
@ -178,8 +180,8 @@ public class LocalFeedRepository extends ARepository<Void> {
insertItems(items, dbFeed);
}
private Feed insertFeed(AFeed feed, RSSQuery.RSSType type) throws IOException {
Feed dbFeed = null;
private Feed insertFeed(AFeed feed, RSSQuery.RSSType type, ParsingResult parsingResult) {
Feed dbFeed;
switch (type) {
case RSS_2:
dbFeed = FeedMatcher.feedFromRSS((RSSFeed) feed);
@ -190,8 +192,12 @@ public class LocalFeedRepository extends ARepository<Void> {
case RSS_JSON:
dbFeed = FeedMatcher.feedFromJSON((JSONFeed) feed);
break;
default:
throw new IllegalArgumentException("Unknown RSS type");
}
dbFeed.setFolderId(parsingResult.getFolderId());
if (database.feedDao().feedExists(dbFeed.getUrl(), account.getId()))
return null; // feed already inserted

View File

@ -1,16 +1,25 @@
package com.readrops.app.utils;
import com.readrops.app.database.entities.account.Account;
import com.readrops.app.database.entities.Feed;
import com.readrops.app.database.entities.Folder;
import com.readrops.app.database.entities.account.Account;
import com.readrops.readropslibrary.localfeed.atom.ATOMFeed;
import com.readrops.readropslibrary.localfeed.json.JSONFeed;
import com.readrops.readropslibrary.localfeed.rss.RSSChannel;
import com.readrops.readropslibrary.localfeed.rss.RSSFeed;
import com.readrops.readropslibrary.opml.model.Body;
import com.readrops.readropslibrary.opml.model.Opml;
import com.readrops.readropslibrary.opml.model.Outline;
import com.readrops.readropslibrary.services.freshrss.json.FreshRSSFeed;
import com.readrops.readropslibrary.services.nextcloudnews.json.NextNewsFeed;
import org.jsoup.Jsoup;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class FeedMatcher {
public static Feed nextNewsFeedToFeed(NextNewsFeed feed, Account account) {
@ -100,4 +109,27 @@ public final class FeedMatcher {
return feed;
}
public static Map<Folder, List<Feed>> feedsAndFoldersFromOPML(Opml opml) {
Map<Folder, List<Feed>> foldersAndFeeds = new HashMap<>();
Body body = opml.getBody();
for (Outline outline : body.getOutlines()) {
Folder folder = new Folder(outline.getTitle());
List<Feed> feeds = new ArrayList<>();
for (Outline feedOutline : outline.getOutlines()) {
Feed feed = new Feed();
feed.setName(feedOutline.getTitle());
feed.setUrl(feedOutline.getXmlUrl());
feed.setSiteUrl(feedOutline.getHtmlUrl());
feeds.add(feed);
}
foldersAndFeeds.put(folder, feeds);
}
return foldersAndFeeds;
}
}

View File

@ -9,9 +9,11 @@ import androidx.annotation.NonNull;
import com.mikepenz.fastadapter.FastAdapter;
import com.mikepenz.fastadapter.items.AbstractItem;
import com.readrops.app.R;
import com.readrops.app.database.entities.Feed;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class ParsingResult extends AbstractItem<ParsingResult, ParsingResult.ParsingResultViewHolder> {
@ -22,6 +24,8 @@ public class ParsingResult extends AbstractItem<ParsingResult, ParsingResult.Par
private boolean checked;
private Integer folderId;
public ParsingResult(String url, String label) {
this.url = url;
this.label = label;
@ -39,6 +43,18 @@ public class ParsingResult extends AbstractItem<ParsingResult, ParsingResult.Par
return label;
}
public static List<ParsingResult> toParsingResults(List<Feed> feeds) {
List<ParsingResult> parsingResults = new ArrayList<>();
for (Feed feed : feeds) {
ParsingResult parsingResult = new ParsingResult(feed.getUrl(), null);
parsingResult.setFolderId(feed.getFolderId());
parsingResults.add(parsingResult);
}
return parsingResults;
}
public void setLabel(String label) {
this.label = label;
}
@ -51,6 +67,14 @@ public class ParsingResult extends AbstractItem<ParsingResult, ParsingResult.Par
return checked;
}
public Integer getFolderId() {
return folderId;
}
public void setFolderId(Integer folderId) {
this.folderId = folderId;
}
@Override
public boolean isSelectable() {
return true;

View File

@ -1,20 +1,30 @@
package com.readrops.app.viewmodels;
import android.app.Application;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import com.readrops.app.database.Database;
import com.readrops.app.database.entities.Feed;
import com.readrops.app.database.entities.Folder;
import com.readrops.app.database.entities.account.Account;
import com.readrops.app.database.entities.account.AccountType;
import com.readrops.app.repositories.ARepository;
import com.readrops.app.utils.FeedMatcher;
import com.readrops.readropslibrary.opml.model.Opml;
import java.util.List;
import java.util.Map;
import io.reactivex.Completable;
import io.reactivex.Single;
public class AccountViewModel extends AndroidViewModel {
private static final String TAG = AccountViewModel.class.getSimpleName();
private ARepository repository;
private Database database;
@ -28,6 +38,14 @@ public class AccountViewModel extends AndroidViewModel {
repository = ARepository.repositoryFactory(null, accountType, getApplication());
}
public void setAccount(Account account) {
try {
repository = ARepository.repositoryFactory(account, getApplication());
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
public Single<Boolean> login(Account account, boolean insert) {
return repository.login(account, insert);
}
@ -47,4 +65,10 @@ public class AccountViewModel extends AndroidViewModel {
public Single<Integer> getAccountCount() {
return database.accountDao().getAccountCount();
}
public Completable insertOPMLFoldersAndFeeds(Opml opml) {
Map<Folder, List<Feed>> foldersAndFeeds = FeedMatcher.feedsAndFoldersFromOPML(opml);
return repository.insertOPMLFoldersAndFeeds(foldersAndFeeds);
}
}