Parse OPML file to get feeds and folders and insert them, for local accounts only
This commit is contained in:
parent
d4228ee953
commit
ad28e44268
@ -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
|
||||
*
|
||||
|
@ -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: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user