From e12cf1ec7f205869c2e97e6f14df723b3120c22c Mon Sep 17 00:00:00 2001 From: Shinokuni Date: Sun, 23 Jun 2019 13:33:10 +0200 Subject: [PATCH] Add nextcloud news feed insertion and account choice in the add feed activity --- .../app/activities/AddFeedActivity.java | 37 +++++++++++-- .../com/readrops/app/database/Database.java | 3 -- .../readrops/app/database/entities/Feed.java | 31 +++++------ .../app/repositories/NextNewsRepository.java | 47 ++++++++++++++-- .../app/utils/FeedInsertionResult.java | 6 +-- .../app/viewmodels/AddFeedsViewModel.java | 22 +++++++- .../app/views/AccountArrayAdapter.java | 54 +++++++++++++++++++ app/src/main/res/layout/activity_add_feed.xml | 17 ++++-- .../services/NextNewsService.java | 4 ++ .../services/nextcloudnews/NextNewsAPI.java | 31 ++++++++--- .../readropslibrary/utils/LibUtils.java | 2 + 11 files changed, 214 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/com/readrops/app/views/AccountArrayAdapter.java diff --git a/app/src/main/java/com/readrops/app/activities/AddFeedActivity.java b/app/src/main/java/com/readrops/app/activities/AddFeedActivity.java index fbcf39bc..c42ef647 100644 --- a/app/src/main/java/com/readrops/app/activities/AddFeedActivity.java +++ b/app/src/main/java/com/readrops/app/activities/AddFeedActivity.java @@ -7,8 +7,10 @@ import android.util.Patterns; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; +import android.widget.AdapterView; import android.widget.Button; import android.widget.ProgressBar; +import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; @@ -27,8 +29,10 @@ import com.readrops.app.database.entities.Account; import com.readrops.app.database.entities.Feed; import com.readrops.app.utils.FeedInsertionResult; import com.readrops.app.utils.ParsingResult; +import com.readrops.app.utils.SharedPreferencesManager; import com.readrops.app.utils.Utils; import com.readrops.app.viewmodels.AddFeedsViewModel; +import com.readrops.app.views.AccountArrayAdapter; import java.util.ArrayList; import java.util.List; @@ -49,6 +53,9 @@ public class AddFeedActivity extends AppCompatActivity implements View.OnClickLi private ProgressBar feedInsertionProgressBar; private RecyclerView insertionResultsRecyclerView; + private Spinner accountSpinner; + private AccountArrayAdapter arrayAdapter; + private ItemAdapter parseItemsAdapter; private ItemAdapter insertionResultsAdapter; @@ -69,6 +76,7 @@ public class AddFeedActivity extends AppCompatActivity implements View.OnClickLi resultsTextView = findViewById(R.id.add_feed_results_text_view); feedInsertionProgressBar = findViewById(R.id.add_feed_insert_progressbar); insertionResultsRecyclerView = findViewById(R.id.add_feed_inserted_results_recyclerview); + accountSpinner = findViewById(R.id.add_feed_account_spinner); load.setOnClickListener(this); validate.setOnClickListener(this); @@ -145,6 +153,25 @@ public class AddFeedActivity extends AppCompatActivity implements View.OnClickLi insertionResultsRecyclerView.setAdapter(FastAdapter.with(insertionResultsAdapter)); insertionResultsRecyclerView.setLayoutManager(layoutManager1); + viewModel.getAccounts().observe(this, accounts -> { + arrayAdapter = new AccountArrayAdapter(this, accounts); + arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + + accountSpinner.setAdapter(arrayAdapter); + + accountSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + }); + feedsToUpdate = new ArrayList<>(); } @@ -233,8 +260,12 @@ public class AddFeedActivity extends AppCompatActivity implements View.OnClickLi feedsToInsert.add(result); } - // TODO : choose the right account - viewModel.addFeeds(feedsToInsert, new Account()) + Account account = (Account) accountSpinner.getSelectedItem(); + + account.setLogin(SharedPreferencesManager.readString(this, account.getLoginKey())); + account.setPassword(SharedPreferencesManager.readString(this, account.getPasswordKey())); + + viewModel.addFeeds(feedsToInsert, account) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new DisposableSingleObserver>() { @@ -245,7 +276,7 @@ public class AddFeedActivity extends AppCompatActivity implements View.OnClickLi @Override public void onError(Throwable e) { - + Toast.makeText(AddFeedActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show(); } }); } diff --git a/app/src/main/java/com/readrops/app/database/Database.java b/app/src/main/java/com/readrops/app/database/Database.java index 684aa977..72091403 100644 --- a/app/src/main/java/com/readrops/app/database/Database.java +++ b/app/src/main/java/com/readrops/app/database/Database.java @@ -45,9 +45,6 @@ public abstract class Database extends RoomDatabase { @Override public void onCreate(@NonNull SupportSQLiteDatabase db) { super.onCreate(db); - - Folder folder = new Folder("reserved"); - //new Thread(() -> database.folderDao().insert(folder)).start(); } @Override 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 index 3d424001..b9922c5d 100644 --- a/app/src/main/java/com/readrops/app/database/entities/Feed.java +++ b/app/src/main/java/com/readrops/app/database/entities/Feed.java @@ -20,8 +20,8 @@ import org.jsoup.Jsoup; @Entity(foreignKeys = {@ForeignKey(entity = Folder.class, parentColumns = "id", childColumns = "folder_id", onDelete = ForeignKey.SET_NULL), - @ForeignKey(entity = Account.class, parentColumns = "id", childColumns = "account_id", - onDelete = ForeignKey.CASCADE)}) + @ForeignKey(entity = Account.class, parentColumns = "id", childColumns = "account_id", + onDelete = ForeignKey.CASCADE)}) public class Feed implements Parcelable { @PrimaryKey(autoGenerate = true) @@ -38,10 +38,12 @@ public class Feed implements Parcelable { private String lastUpdated; @ColumnInfo(name = "text_color") - private @ColorInt int textColor; + private @ColorInt + int textColor; @ColumnInfo(name = "background_color") - private @ColorInt int backgroundColor; + private @ColorInt + int backgroundColor; @ColumnInfo(name = "icon_url") private String iconUrl; @@ -52,7 +54,7 @@ public class Feed implements Parcelable { private String lastModified; @ColumnInfo(name = "folder_id", index = true) - private int folderId; + private Integer folderId; // nullable foreign key so Integer instead of int private int remoteId; @@ -149,7 +151,8 @@ public class Feed implements Parcelable { this.lastUpdated = lastUpdated; } - public @ColorInt int getTextColor() { + public @ColorInt + int getTextColor() { return textColor; } @@ -157,7 +160,8 @@ public class Feed implements Parcelable { this.textColor = textColor; } - public @ColorInt int getBackgroundColor() { + public @ColorInt + int getBackgroundColor() { return backgroundColor; } @@ -189,11 +193,11 @@ public class Feed implements Parcelable { this.lastModified = lastModified; } - public int getFolderId() { + public Integer getFolderId() { return folderId; } - public void setFolderId(int folderId) { + public void setFolderId(Integer folderId) { this.folderId = folderId; } @@ -234,9 +238,7 @@ public class Feed implements Parcelable { feed.setEtag(rssFeed.getEtag()); feed.setLastModified(rssFeed.getLastModified()); - // as sqlite doesn't support null foreign keys, a default folder is linked to the feed - // This default folder was inserted at room db creation (see Database.java) - feed.setFolderId(1); + feed.setFolderId(null); return feed; } @@ -254,7 +256,7 @@ public class Feed implements Parcelable { feed.setEtag(atomFeed.getEtag()); feed.setLastModified(atomFeed.getLastModified()); - feed.setFolderId(1); + feed.setFolderId(null); return feed; } @@ -266,13 +268,12 @@ public class Feed implements Parcelable { feed.setUrl(jsonFeed.getFeedUrl()); feed.setSiteUrl(jsonFeed.getHomePageUrl()); feed.setDescription(jsonFeed.getDescription()); - //feed.setLastUpdated(jsonFeed.); maybe later ? feed.setEtag(jsonFeed.getEtag()); feed.setLastModified(jsonFeed.getLastModified()); feed.setIconUrl(jsonFeed.getFaviconUrl()); - feed.setFolderId(1); + feed.setFolderId(null); return feed; } diff --git a/app/src/main/java/com/readrops/app/repositories/NextNewsRepository.java b/app/src/main/java/com/readrops/app/repositories/NextNewsRepository.java index 05406205..c19b3246 100644 --- a/app/src/main/java/com/readrops/app/repositories/NextNewsRepository.java +++ b/app/src/main/java/com/readrops/app/repositories/NextNewsRepository.java @@ -1,6 +1,7 @@ package com.readrops.app.repositories; import android.app.Application; +import android.database.sqlite.SQLiteConstraintException; import android.util.TimingLogger; import com.readrops.app.database.entities.Account; @@ -18,9 +19,11 @@ import com.readrops.readropslibrary.services.nextcloudnews.NextNewsAPI; import com.readrops.readropslibrary.services.nextcloudnews.SyncData; import com.readrops.readropslibrary.services.nextcloudnews.SyncResult; import com.readrops.readropslibrary.services.nextcloudnews.json.NextNewsFeed; +import com.readrops.readropslibrary.services.nextcloudnews.json.NextNewsFeeds; import com.readrops.readropslibrary.services.nextcloudnews.json.NextNewsFolder; import com.readrops.readropslibrary.services.nextcloudnews.json.NextNewsItem; import com.readrops.readropslibrary.services.nextcloudnews.json.NextNewsUser; +import com.readrops.readropslibrary.utils.UnknownFormatException; import org.joda.time.LocalDateTime; @@ -116,7 +119,41 @@ public class NextNewsRepository extends ARepository { @Override public Single> addFeeds(List results, Account account) { - return null; + return Single.create(emitter -> { + List feedInsertionResults = new ArrayList<>(); + NextNewsAPI newsAPI = new NextNewsAPI(); + + for (ParsingResult result : results) { + FeedInsertionResult insertionResult = new FeedInsertionResult(); + + try { + Credentials credentials = new Credentials(account.getLogin(), account.getPassword(), account.getUrl()); + NextNewsFeeds nextNewsFeeds = newsAPI.createFeed(credentials, result.getUrl(), 0); + + if (nextNewsFeeds != null) { + List newFeeds = insertFeeds(nextNewsFeeds.getFeeds(), account); + + // there is always only one object in the list, see nextcloud news api doc + insertionResult.setFeed(newFeeds.get(0)); + } else + insertionResult.setInsertionError(FeedInsertionResult.FeedInsertionError.UNKNOWN_ERROR); + + } catch (Exception e) { + if (e instanceof IOException) + insertionResult.setInsertionError(FeedInsertionResult.FeedInsertionError.NETWORK_ERROR); + else if (e instanceof UnknownFormatException) + insertionResult.setInsertionError(FeedInsertionResult.FeedInsertionError.FORMAT_ERROR); + else if (e instanceof SQLiteConstraintException) + insertionResult.setInsertionError(FeedInsertionResult.FeedInsertionError.DB_ERROR); + else + insertionResult.setInsertionError(FeedInsertionResult.FeedInsertionError.UNKNOWN_ERROR); + } + + feedInsertionResults.add(insertionResult); + } + + emitter.onSuccess(feedInsertionResults); + }); } @Override @@ -134,7 +171,7 @@ public class NextNewsRepository extends ARepository { return null; } - private void insertFeeds(List feeds, Account account) { + private List insertFeeds(List feeds, Account account) { List newFeeds = new ArrayList<>(); for (NextNewsFeed nextNewsFeed : feeds) { @@ -142,14 +179,14 @@ public class NextNewsRepository extends ARepository { if (!database.feedDao().remoteFeedExists(nextNewsFeed.getId(), account.getId())) { Feed feed = FeedMatcher.nextNewsFeedToFeed(nextNewsFeed, account); - // if the Nextcloud feed has a folder, it is already inserted, so we have to get its local id if (nextNewsFeed.getFolderId() != 0) { int folderId = database.folderDao().getRemoteFolderLocalId(nextNewsFeed.getFolderId(), account.getId()); if (folderId != 0) feed.setFolderId(folderId); - } + } else + feed.setFolderId(null); newFeeds.add(feed); } @@ -168,6 +205,8 @@ public class NextNewsRepository extends ARepository { .doOnNext(feed1 -> database.feedDao().updateColors(feed1.getId(), feed1.getTextColor(), feed1.getBackgroundColor())) .subscribe(); + + return insertedFeeds; } private void insertFolders(List folders, Account account) { diff --git a/app/src/main/java/com/readrops/app/utils/FeedInsertionResult.java b/app/src/main/java/com/readrops/app/utils/FeedInsertionResult.java index 5e0b1c93..ad77ba35 100644 --- a/app/src/main/java/com/readrops/app/utils/FeedInsertionResult.java +++ b/app/src/main/java/com/readrops/app/utils/FeedInsertionResult.java @@ -1,12 +1,12 @@ package com.readrops.app.utils; -import androidx.annotation.NonNull; -import androidx.annotation.StringRes; - import android.view.View; import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; + import com.mikepenz.fastadapter.FastAdapter; import com.mikepenz.fastadapter.items.AbstractItem; import com.readrops.app.R; diff --git a/app/src/main/java/com/readrops/app/viewmodels/AddFeedsViewModel.java b/app/src/main/java/com/readrops/app/viewmodels/AddFeedsViewModel.java index da971ad0..b0f0fc54 100644 --- a/app/src/main/java/com/readrops/app/viewmodels/AddFeedsViewModel.java +++ b/app/src/main/java/com/readrops/app/viewmodels/AddFeedsViewModel.java @@ -3,9 +3,13 @@ package com.readrops.app.viewmodels; import android.app.Application; import androidx.lifecycle.AndroidViewModel; import androidx.annotation.NonNull; +import androidx.lifecycle.LiveData; +import com.readrops.app.database.Database; import com.readrops.app.database.entities.Account; +import com.readrops.app.repositories.ARepository; import com.readrops.app.repositories.LocalFeedRepository; +import com.readrops.app.repositories.NextNewsRepository; import com.readrops.app.utils.FeedInsertionResult; import com.readrops.app.utils.HtmlParser; import com.readrops.app.utils.ParsingResult; @@ -18,15 +22,25 @@ import io.reactivex.Single; public class AddFeedsViewModel extends AndroidViewModel { - private LocalFeedRepository repository; + private ARepository repository; + private Database database; public AddFeedsViewModel(@NonNull Application application) { super(application); - repository = new LocalFeedRepository(application); + database = Database.getInstance(application); } public Single> addFeeds(List results, Account account) { + switch (account.getAccountType()) { + case LOCAL: + repository = new LocalFeedRepository(getApplication()); + break; + case NEXTCLOUD_NEWS: + repository = new NextNewsRepository(getApplication()); + break; + } + return repository.addFeeds(results, account); } @@ -45,4 +59,8 @@ public class AddFeedsViewModel extends AndroidViewModel { emitter.onSuccess(results); }); } + + public LiveData> getAccounts() { + return database.accountDao().selectAll(); + } } diff --git a/app/src/main/java/com/readrops/app/views/AccountArrayAdapter.java b/app/src/main/java/com/readrops/app/views/AccountArrayAdapter.java new file mode 100644 index 00000000..a12e6ad6 --- /dev/null +++ b/app/src/main/java/com/readrops/app/views/AccountArrayAdapter.java @@ -0,0 +1,54 @@ +package com.readrops.app.views; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.readrops.app.R; +import com.readrops.app.database.entities.Account; + +import java.util.List; + +public class AccountArrayAdapter extends ArrayAdapter { + + public AccountArrayAdapter(@NonNull Context context, @NonNull List objects) { + super(context, 0, objects); + + } + + + + @Override + public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { + return createItemView(position, convertView, parent); + } + + @NonNull + @Override + public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { + return createItemView(position, convertView, parent); + } + + private View createItemView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = LayoutInflater.from(getContext()).inflate(R.layout.account_type_item, parent, false); + } + + Account account = getItem(position); + + ImageView accountIcon = convertView.findViewById(R.id.account_type_logo); + TextView accountName = convertView.findViewById(R.id.account_type_name); + + accountIcon.setImageResource(account.getAccountType().getIconRes()); + accountName.setText(account.getAccountType().getName()); + + return convertView; + } +} diff --git a/app/src/main/res/layout/activity_add_feed.xml b/app/src/main/res/layout/activity_add_feed.xml index b9d248a1..ecbb017c 100644 --- a/app/src/main/res/layout/activity_add_feed.xml +++ b/app/src/main/res/layout/activity_add_feed.xml @@ -33,8 +33,8 @@ android:id="@+id/add_feed_text_input" android:layout_width="match_parent" android:layout_height="wrap_content" - android:hint="@string/feed_url" - android:drawableEnd="@drawable/ic_cancel_grey"/> + android:drawableEnd="@drawable/ic_cancel_grey" + android:hint="@string/feed_url" /> @@ -83,17 +83,26 @@ + +