captions = new HashMap<>();
@@ -55,20 +51,13 @@ public class Contribution extends Media {
UploadMediaDetail.formatList(item.getUploadMediaDetails()),
sessionManager.getAuthorName(),
categories);
- captions = UploadMediaDetail.formatCaptions(item.getUploadMediaDetails());
+ captions = UploadMediaDetail.formatCaptions(item.getUploadMediaDetails());
decimalCoords = item.getGpsCoords().getDecimalCoords();
dateCreatedSource = "";
this.depictedItems = depictedItems;
wikidataPlace = WikidataPlace.from(item.getPlace());
}
- public Contribution(final MwQueryLogEvent queryLogEvent, final String user) {
- super(queryLogEvent.title(),queryLogEvent.date(), user);
- decimalCoords = "";
- dateCreatedSource = "";
- state = STATE_COMPLETED;
- }
-
public void setDateCreatedSource(final String dateCreatedSource) {
this.dateCreatedSource = dateCreatedSource;
}
@@ -108,14 +97,6 @@ public class Contribution extends Media {
return wikidataPlace;
}
- public long get_id() {
- return _id;
- }
-
- public void set_id(final long _id) {
- this._id = _id;
- }
-
public String getDecimalCoords() {
return decimalCoords;
}
@@ -128,29 +109,30 @@ public class Contribution extends Media {
this.depictedItems = depictedItems;
}
- public void setMimeType(String mimeType) {
- this.mimeType = mimeType;
+ public String getMimeType() {
+ return mimeType;
}
- public String getMimeType() {
- return mimeType;
+ public void setMimeType(final String mimeType) {
+ this.mimeType = mimeType;
}
/**
- * Captions are a feature part of Structured data. They are meant to store short, multilingual descriptions about files
- * This is a replacement of the previously used titles for images (titles were not multilingual)
- * Also now captions replace the previous convention of using title for filename
- *
+ * Captions are a feature part of Structured data. They are meant to store short, multilingual
+ * descriptions about files This is a replacement of the previously used titles for images (titles
+ * were not multilingual) Also now captions replace the previous convention of using title for
+ * filename
+ *
* key of the HashMap is the language and value is the caption in the corresponding language
- *
+ *
* returns list of captions stored in hashmap
*/
public Map getCaptions() {
- return captions;
+ return captions;
}
public void setCaptions(Map captions) {
- this.captions = captions;
+ this.captions = captions;
}
@Override
@@ -161,7 +143,6 @@ public class Contribution extends Media {
@Override
public void writeToParcel(final Parcel dest, final int flags) {
super.writeToParcel(dest, flags);
- dest.writeLong(_id);
dest.writeInt(state);
dest.writeLong(transferred);
dest.writeString(decimalCoords);
@@ -169,9 +150,24 @@ public class Contribution extends Media {
dest.writeSerializable((HashMap) captions);
}
+ /**
+ * Constructor that takes Media object and state as parameters and builds a new Contribution object
+ * @param media
+ * @param state
+ */
+ public Contribution(Media media, int state) {
+ super(media.getPageId(),
+ media.getLocalUri(), media.getThumbUrl(), media.getImageUrl(), media.getFilename(),
+ media.getDescription(),
+ media.getDiscussion(),
+ media.getDataLength(), media.getDateCreated(), media.getDateUploaded(),
+ media.getLicense(), media.getLicenseUrl(), media.getCreator(), media.getCategories(),
+ media.isRequestedDeletion(), media.getCoordinates());
+ this.state = state;
+ }
+
protected Contribution(final Parcel in) {
super(in);
- _id = in.readLong();
state = in.readInt();
transferred = in.readLong();
decimalCoords = in.readString();
@@ -190,4 +186,35 @@ public class Contribution extends Media {
return new Contribution[size];
}
};
+
+ /**
+ * Equals implementation of Contributions that compares all parameters for checking equality
+ */
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Contribution)) {
+ return false;
+ }
+ final Contribution that = (Contribution) o;
+ return getState() == that.getState() && getTransferred() == that.getTransferred() && Objects
+ .equals(getDecimalCoords(), that.getDecimalCoords()) && Objects
+ .equals(getDateCreatedSource(), that.getDateCreatedSource()) && Objects
+ .equals(getWikidataPlace(), that.getWikidataPlace()) && Objects
+ .equals(getDepictedItems(), that.getDepictedItems()) && Objects
+ .equals(getMimeType(), that.getMimeType()) && Objects
+ .equals(getCaptions(), that.getCaptions());
+ }
+
+ /**
+ * Hash code implementation of contributions that considers all parameters for calculating hash.
+ */
+ @Override
+ public int hashCode() {
+ return Objects
+ .hash(getState(), getTransferred(), getDecimalCoords(), getDateCreatedSource(),
+ getWikidataPlace(), getDepictedItems(), getMimeType(), getCaptions());
+ }
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt
new file mode 100644
index 000000000..ba5d1ed7f
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt
@@ -0,0 +1,89 @@
+package fr.free.nrw.commons.contributions
+
+import androidx.paging.PagedList.BoundaryCallback
+import fr.free.nrw.commons.Media
+import fr.free.nrw.commons.auth.SessionManager
+import fr.free.nrw.commons.di.CommonsApplicationModule
+import fr.free.nrw.commons.media.MediaClient
+import io.reactivex.Scheduler
+import io.reactivex.disposables.CompositeDisposable
+import timber.log.Timber
+import java.util.*
+import javax.inject.Inject
+import javax.inject.Named
+
+/**
+ * Class that extends PagedList.BoundaryCallback for contributions list It defines the action that
+ * is triggered for various boundary conditions in the list
+ */
+class ContributionBoundaryCallback @Inject constructor(
+ private val repository: ContributionsRepository,
+ private val sessionManager: SessionManager,
+ private val mediaClient: MediaClient,
+ @param:Named(CommonsApplicationModule.IO_THREAD) private val ioThreadScheduler: Scheduler
+) : BoundaryCallback() {
+ private val compositeDisposable: CompositeDisposable = CompositeDisposable()
+
+ /**
+ * It is triggered when the list has no items User's Contributions are then fetched from the
+ * network
+ */
+ override fun onZeroItemsLoaded() {
+ fetchContributions()
+ }
+
+ /**
+ * It is triggered when the user scrolls to the top of the list User's Contributions are then
+ * fetched from the network
+ * */
+ override fun onItemAtFrontLoaded(itemAtFront: Contribution) {
+ fetchContributions()
+ }
+
+ /**
+ * It is triggered when the user scrolls to the end of the list. User's Contributions are then
+ * fetched from the network
+ */
+ override fun onItemAtEndLoaded(itemAtEnd: Contribution) {
+ fetchContributions()
+ }
+
+ /**
+ * Fetches contributions using the MediaWiki API
+ */
+ fun fetchContributions() {
+ if (mediaClient.doesMediaListForUserHaveMorePages(sessionManager.userName).not()) {
+ return
+ }
+ compositeDisposable.add(
+ mediaClient.getMediaListForUser(sessionManager.userName)
+ .map { mediaList: List ->
+ mediaList.map {
+ Contribution(it, Contribution.STATE_COMPLETED)
+ }
+ }
+ .subscribeOn(ioThreadScheduler)
+ .subscribe(
+ ::saveContributionsToDB
+ ) { error: Throwable ->
+ Timber.e(
+ "Failed to fetch contributions: %s",
+ error.message
+ )
+ }
+ )
+ }
+
+ /**
+ * Saves the contributions the the local DB
+ */
+ private fun saveContributionsToDB(contributions: List) {
+ compositeDisposable.add(
+ repository.save(contributions)
+ .subscribeOn(ioThreadScheduler)
+ .subscribe { longs: List? ->
+ repository["last_fetch_timestamp"] = System.currentTimeMillis()
+ }
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
index 3c0ac925f..e288a84af 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
@@ -1,6 +1,6 @@
package fr.free.nrw.commons.contributions;
-import androidx.lifecycle.LiveData;
+import androidx.paging.DataSource;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
@@ -15,40 +15,55 @@ import java.util.List;
@Dao
public abstract class ContributionDao {
- @Query("SELECT * FROM contribution order by dateUploaded DESC")
- abstract LiveData> fetchContributions();
+ @Query("SELECT * FROM contribution order by dateUploaded DESC")
+ abstract DataSource.Factory fetchContributions();
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- public abstract Single save(Contribution contribution);
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ public abstract void saveSynchronous(Contribution contribution);
- public Completable deleteAllAndSave(List contributions){
- return Completable.fromAction(() -> deleteAllAndSaveTransaction(contributions));
- }
+ public Completable save(final Contribution contribution) {
+ return Completable
+ .fromAction(() -> saveSynchronous(contribution));
+ }
- @Transaction
- public void deleteAllAndSaveTransaction(List contributions){
- deleteAll(Contribution.STATE_COMPLETED);
- save(contributions);
- }
+ @Transaction
+ public void deleteAndSaveContribution(final Contribution oldContribution,
+ final Contribution newContribution) {
+ deleteSynchronous(oldContribution);
+ saveSynchronous(newContribution);
+ }
- @Insert
- public abstract void save(List contribution);
+ public Completable saveAndDelete(final Contribution oldContribution,
+ final Contribution newContribution) {
+ return Completable
+ .fromAction(() -> deleteAndSaveContribution(oldContribution, newContribution));
+ }
- @Delete
- public abstract Single delete(Contribution contribution);
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ public abstract Single> save(List contribution);
- @Query("SELECT * from contribution WHERE filename=:fileName")
- public abstract List getContributionWithTitle(String fileName);
+ @Delete
+ public abstract void deleteSynchronous(Contribution contribution);
- @Query("UPDATE contribution SET state=:state WHERE state in (:toUpdateStates)")
- public abstract Single updateStates(int state, int[] toUpdateStates);
+ public Completable delete(final Contribution contribution) {
+ return Completable
+ .fromAction(() -> deleteSynchronous(contribution));
+ }
- @Query("Delete FROM contribution")
- public abstract void deleteAll();
+ @Query("SELECT * from contribution WHERE filename=:fileName")
+ public abstract List getContributionWithTitle(String fileName);
- @Query("Delete FROM contribution WHERE state = :state")
- public abstract void deleteAll(int state);
+ @Query("UPDATE contribution SET state=:state WHERE state in (:toUpdateStates)")
+ public abstract Single updateStates(int state, int[] toUpdateStates);
- @Update
- public abstract Single update(Contribution contribution);
+ @Query("Delete FROM contribution")
+ public abstract void deleteAll();
+
+ @Update
+ public abstract void updateSynchronous(Contribution contribution);
+
+ public Completable update(final Contribution contribution) {
+ return Completable
+ .fromAction(() -> updateSynchronous(contribution));
+ }
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.java
index 501f1fe58..a5de53bb1 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.java
@@ -22,7 +22,6 @@ import fr.free.nrw.commons.media.MediaClient;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
-import java.util.Random;
import timber.log.Timber;
public class ContributionViewHolder extends RecyclerView.ViewHolder {
@@ -39,23 +38,22 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
private int position;
private Contribution contribution;
- private Random random = new Random();
- private CompositeDisposable compositeDisposable = new CompositeDisposable();
+ private final CompositeDisposable compositeDisposable = new CompositeDisposable();
private final MediaClient mediaClient;
- ContributionViewHolder(View parent, Callback callback,
- MediaClient mediaClient) {
+ ContributionViewHolder(final View parent, final Callback callback,
+ final MediaClient mediaClient) {
super(parent);
this.mediaClient = mediaClient;
ButterKnife.bind(this, parent);
this.callback=callback;
}
- public void init(int position, Contribution contribution) {
+ public void init(final int position, final Contribution contribution) {
this.contribution = contribution;
fetchAndDisplayCaption(contribution);
this.position = position;
- String imageSource = chooseImageSource(contribution.getThumbUrl(), contribution.getLocalUri());
+ final String imageSource = chooseImageSource(contribution.getThumbUrl(), contribution.getLocalUri());
if (!TextUtils.isEmpty(imageSource)) {
final ImageRequest imageRequest =
ImageRequestBuilder.newBuilderWithSource(Uri.parse(imageSource))
@@ -84,8 +82,8 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
stateView.setVisibility(View.GONE);
progressView.setVisibility(View.VISIBLE);
failedImageOptions.setVisibility(View.GONE);
- long total = contribution.getDataLength();
- long transferred = contribution.getTransferred();
+ final long total = contribution.getDataLength();
+ final long transferred = contribution.getTransferred();
if (transferred == 0 || transferred >= total) {
progressView.setIndeterminate(true);
} else {
@@ -107,14 +105,14 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
*
* @param contribution
*/
- private void fetchAndDisplayCaption(Contribution contribution) {
+ private void fetchAndDisplayCaption(final Contribution contribution) {
if ((contribution.getState() != Contribution.STATE_COMPLETED)) {
titleView.setText(contribution.getDisplayTitle());
} else {
final String pageId = contribution.getPageId();
if (pageId != null) {
Timber.d("Fetching caption for %s", contribution.getFilename());
- String wikibaseMediaId = PAGE_ID_PREFIX
+ final String wikibaseMediaId = PAGE_ID_PREFIX
+ pageId; // Create Wikibase media id from the page id. Example media id: M80618155 for https://commons.wikimedia.org/wiki/File:Tantanmen.jpeg with has the pageid 80618155
compositeDisposable.add(mediaClient.getCaptionByWikibaseIdentifier(wikibaseMediaId)
.subscribeOn(Schedulers.io())
@@ -141,7 +139,7 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
* @return
*/
@Nullable
- private String chooseImageSource(String thumbUrl, Uri localUri) {
+ private String chooseImageSource(final String thumbUrl, final Uri localUri) {
return !TextUtils.isEmpty(thumbUrl) ? thumbUrl :
localUri != null ? localUri.toString() :
null;
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java
index 8b0049004..d578d1185 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java
@@ -12,16 +12,6 @@ public class ContributionsContract {
public interface View {
- void showWelcomeTip(boolean numberOfUploads);
-
- void showProgress(boolean shouldShow);
-
- void showNoContributionsUI(boolean shouldShow);
-
- void setUploadCount(int count);
-
- void showContributions(List contributionList);
-
void showMessage(String localizedMessage);
}
@@ -31,8 +21,6 @@ public class ContributionsContract {
void deleteUpload(Contribution contribution);
- Media getItemAtPosition(int i);
-
void updateContribution(Contribution contribution);
void fetchMediaDetails(Contribution contribution);
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java
index 9022bbfb9..8e3221ea9 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java
@@ -19,7 +19,6 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentManager.OnBackStackChangedListener;
import androidx.fragment.app.FragmentTransaction;
import butterknife.BindView;
@@ -30,8 +29,7 @@ import fr.free.nrw.commons.campaigns.Campaign;
import fr.free.nrw.commons.campaigns.CampaignView;
import fr.free.nrw.commons.campaigns.CampaignsPresenter;
import fr.free.nrw.commons.campaigns.ICampaignsView;
-import fr.free.nrw.commons.contributions.ContributionsListAdapter.Callback;
-import fr.free.nrw.commons.contributions.ContributionsListFragment.SourceRefresher;
+import fr.free.nrw.commons.contributions.ContributionsListFragment.Callback;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.location.LatLng;
@@ -54,7 +52,6 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
-import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import timber.log.Timber;
@@ -62,11 +59,10 @@ import timber.log.Timber;
public class ContributionsFragment
extends CommonsDaggerSupportFragment
implements
- MediaDetailProvider,
OnBackStackChangedListener,
- SourceRefresher,
LocationUpdateListener,
- ICampaignsView, ContributionsContract.View {
+ MediaDetailProvider,
+ ICampaignsView, ContributionsContract.View, Callback {
@Inject @Named("default_preferences") JsonKvStore store;
@Inject NearbyController nearbyController;
@Inject OkHttpJsonApiClient okHttpJsonApiClient;
@@ -78,8 +74,8 @@ public class ContributionsFragment
private CompositeDisposable compositeDisposable = new CompositeDisposable();
private ContributionsListFragment contributionsListFragment;
- private MediaDetailPagerFragment mediaDetailPagerFragment;
private static final String CONTRIBUTION_LIST_FRAGMENT_TAG = "ContributionListFragmentTag";
+ private MediaDetailPagerFragment mediaDetailPagerFragment;
static final String MEDIA_DETAIL_PAGER_FRAGMENT_TAG = "MediaDetailFragmentTag";
@BindView(R.id.card_view_nearby) public NearbyNotificationCardView nearbyNotificationCardView;
@@ -113,7 +109,6 @@ public class ContributionsFragment
}
};
private boolean shouldShowMediaDetailsFragment;
- private int numberOfContributions;
private boolean isAuthCookieAcquired;
@Override
@@ -128,7 +123,6 @@ public class ContributionsFragment
ButterKnife.bind(this, view);
presenter.onAttachView(this);
contributionsPresenter.onAttachView(this);
- contributionsPresenter.setLifeCycleOwner(this.getViewLifecycleOwner());
campaignView.setVisibility(View.GONE);
checkBoxView = View.inflate(getActivity(), R.layout.nearby_permission_dialog, null);
checkBox = (CheckBox) checkBoxView.findViewById(R.id.never_ask_again);
@@ -141,103 +135,21 @@ public class ContributionsFragment
if (savedInstanceState != null) {
mediaDetailPagerFragment = (MediaDetailPagerFragment) getChildFragmentManager()
- .findFragmentByTag(MEDIA_DETAIL_PAGER_FRAGMENT_TAG);
+ .findFragmentByTag(MEDIA_DETAIL_PAGER_FRAGMENT_TAG);
contributionsListFragment = (ContributionsListFragment) getChildFragmentManager()
- .findFragmentByTag(CONTRIBUTION_LIST_FRAGMENT_TAG);
+ .findFragmentByTag(CONTRIBUTION_LIST_FRAGMENT_TAG);
shouldShowMediaDetailsFragment = savedInstanceState.getBoolean("mediaDetailsVisible");
}
initFragments();
- if(shouldShowMediaDetailsFragment){
- showMediaDetailPagerFragment();
- }else{
- showContributionsListFragment();
- }
-
if (!ConfigUtils.isBetaFlavour()) {
setUploadCount();
}
- getChildFragmentManager().registerFragmentLifecycleCallbacks(
- new FragmentManager.FragmentLifecycleCallbacks() {
- @Override public void onFragmentResumed(FragmentManager fm, Fragment f) {
- super.onFragmentResumed(fm, f);
- //If media detail pager fragment is visible, hide the campaigns view [might not be the best way to do, this but yeah, this proves to work for now]
- Timber.e("onFragmentResumed %s", f.getClass().getName());
- if (f instanceof MediaDetailPagerFragment) {
- campaignView.setVisibility(View.GONE);
- }
- }
-
- @Override public void onFragmentDetached(FragmentManager fm, Fragment f) {
- super.onFragmentDetached(fm, f);
- Timber.e("onFragmentDetached %s", f.getClass().getName());
- //If media detail pager fragment is detached, ContributionsList fragment is gonna be visible, [becomes tightly coupled though]
- if (f instanceof MediaDetailPagerFragment) {
- fetchCampaigns();
- }
- }
- }, true);
-
return view;
}
- /**
- * Initialose the ContributionsListFragment and MediaDetailPagerFragment fragment
- */
- private void initFragments() {
- if (null == contributionsListFragment) {
- contributionsListFragment = new ContributionsListFragment();
- }
-
- contributionsListFragment.setCallback(new Callback() {
- @Override
- public void retryUpload(Contribution contribution) {
- ContributionsFragment.this.retryUpload(contribution);
- }
-
- @Override
- public void deleteUpload(Contribution contribution) {
- contributionsPresenter.deleteUpload(contribution);
- }
-
- @Override
- public void openMediaDetail(int position) {
- showDetail(position);
- }
-
- @Override
- public Contribution getContributionForPosition(int position) {
- return (Contribution) contributionsPresenter.getItemAtPosition(position);
- }
-
- @Override
- public void fetchMediaUriFor(Contribution contribution) {
- Timber.d("Fetching thumbnail for %s", contribution.getFilename());
- contributionsPresenter.fetchMediaDetails(contribution);
- }
- });
-
- if(null==mediaDetailPagerFragment){
- mediaDetailPagerFragment=new MediaDetailPagerFragment();
- }
- }
-
-
- /**
- * Replaces the root frame layout with the given fragment
- * @param fragment
- * @param tag
- */
- private void showFragment(Fragment fragment, String tag) {
- FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
- transaction.replace(R.id.root_frame, fragment, tag);
- transaction.addToBackStack(CONTRIBUTION_LIST_FRAGMENT_TAG);
- transaction.commit();
- getChildFragmentManager().executePendingTransactions();
- }
-
@Override
public void onAttach(Context context) {
super.onAttach(context);
@@ -265,7 +177,7 @@ public class ContributionsFragment
if (nearbyNotificationCardView != null) {
if (store.getBoolean("displayNearbyCardView", true)) {
if (nearbyNotificationCardView.cardViewVisibilityState
- == NearbyNotificationCardView.CardViewVisibilityState.READY) {
+ == NearbyNotificationCardView.CardViewVisibilityState.READY) {
nearbyNotificationCardView.setVisibility(View.VISIBLE);
}
} else {
@@ -275,20 +187,22 @@ public class ContributionsFragment
showFragment(contributionsListFragment, CONTRIBUTION_LIST_FRAGMENT_TAG);
}
- /**
- * Replace FrameLayout with MediaDetailPagerFragment, user will see details of selected media.
- * Creates new one if null.
- */
private void showMediaDetailPagerFragment() {
// hide tabs on media detail view is visible
- ((MainActivity)getActivity()).hideTabs();
+ ((MainActivity) getActivity()).hideTabs();
// hide nearby card view on media detail is visible
nearbyNotificationCardView.setVisibility(View.GONE);
- showFragment(mediaDetailPagerFragment,MEDIA_DETAIL_PAGER_FRAGMENT_TAG);
+ showFragment(mediaDetailPagerFragment, MEDIA_DETAIL_PAGER_FRAGMENT_TAG);
}
+ private void setupViewForMediaDetails() {
+ campaignView.setVisibility(View.GONE);
+ nearbyNotificationCardView.setVisibility(View.GONE);
+ ((MainActivity)getActivity()).hideTabs();
+ }
+
@Override
public void onBackStackChanged() {
((MainActivity)getActivity()).initBackButton();
@@ -307,43 +221,42 @@ public class ContributionsFragment
}
+ private void initFragments() {
+ if (null == contributionsListFragment) {
+ contributionsListFragment = new ContributionsListFragment(this);
+ }
+
+ if (shouldShowMediaDetailsFragment) {
+ showMediaDetailPagerFragment();
+ } else {
+ showContributionsListFragment();
+ }
+
+ showFragment(contributionsListFragment, CONTRIBUTION_LIST_FRAGMENT_TAG);
+ }
+
+ /**
+ * Replaces the root frame layout with the given fragment
+ *
+ * @param fragment
+ * @param tag
+ */
+ private void showFragment(Fragment fragment, String tag) {
+ FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
+ transaction.replace(R.id.root_frame, fragment, tag);
+ transaction.addToBackStack(CONTRIBUTION_LIST_FRAGMENT_TAG);
+ transaction.commit();
+ getChildFragmentManager().executePendingTransactions();
+ }
+
public Intent getUploadServiceIntent(){
Intent intent = new Intent(getActivity(), UploadService.class);
intent.setAction(UploadService.ACTION_START_SERVICE);
return intent;
}
- /**
- * Replace whatever is in the current contributionsFragmentContainer view with
- * mediaDetailPagerFragment, and preserve previous state in back stack.
- * Called when user selects a contribution.
- */
- private void showDetail(int i) {
- if (mediaDetailPagerFragment == null || !mediaDetailPagerFragment.isVisible()) {
- mediaDetailPagerFragment = new MediaDetailPagerFragment();
- showMediaDetailPagerFragment();
- }
- mediaDetailPagerFragment.showImage(i);
- }
-
- @Override
- public void refreshSource() {
- contributionsPresenter.fetchContributions();
- }
-
- @Override
- public Media getMediaAtPosition(int i) {
- return contributionsPresenter.getItemAtPosition(i);
- }
-
- @Override
- public int getTotalMediaCount() {
- return numberOfContributions;
- }
-
@SuppressWarnings("ConstantConditions")
private void setUploadCount() {
-
compositeDisposable.add(okHttpJsonApiClient
.getUploadCount(((MainActivity)getActivity()).sessionManager.getCurrentAccount().name)
.subscribeOn(Schedulers.io())
@@ -373,8 +286,6 @@ public class ContributionsFragment
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- boolean mediaDetailsVisible = mediaDetailPagerFragment != null && mediaDetailPagerFragment.isVisible();
- outState.putBoolean("mediaDetailsVisible", mediaDetailsVisible);
}
@Override
@@ -384,13 +295,6 @@ public class ContributionsFragment
firstLocationUpdate = true;
locationManager.addLocationListener(this);
- boolean isSettingsChanged = store.getBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false);
- store.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false);
- if (isSettingsChanged) {
- refreshSource();
- }
-
-
if (store.getBoolean("displayNearbyCardView", true)) {
checkPermissionsAndShowNearbyCardView();
if (nearbyNotificationCardView.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) {
@@ -403,10 +307,6 @@ public class ContributionsFragment
}
fetchCampaigns();
- if(isAuthCookieAcquired){
- contributionsPresenter.fetchContributions();
- }
-
}
private void checkPermissionsAndShowNearbyCardView() {
@@ -463,17 +363,11 @@ public class ContributionsFragment
}
private void updateNearbyNotification(@Nullable NearbyController.NearbyPlacesInfo nearbyPlacesInfo) {
-
if (nearbyPlacesInfo != null && nearbyPlacesInfo.placeList != null && nearbyPlacesInfo.placeList.size() > 0) {
Place closestNearbyPlace = nearbyPlacesInfo.placeList.get(0);
String distance = formatDistanceBetween(curLatLng, closestNearbyPlace.location);
closestNearbyPlace.setDistance(distance);
nearbyNotificationCardView.updateContent(closestNearbyPlace);
- if (mediaDetailPagerFragment != null && mediaDetailPagerFragment.isVisible()) {
- nearbyNotificationCardView.setVisibility(View.GONE);
- }else {
- nearbyNotificationCardView.setVisibility(View.VISIBLE);
- }
} else {
// Means that no close nearby place is found
nearbyNotificationCardView.setVisibility(View.GONE);
@@ -553,37 +447,13 @@ public class ContributionsFragment
presenter.onDetachView();
}
- @Override
- public void showWelcomeTip(boolean shouldShow) {
- contributionsListFragment.showWelcomeTip(shouldShow);
- }
-
- @Override
- public void showProgress(boolean shouldShow) {
- contributionsListFragment.showProgress(shouldShow);
- }
-
- @Override
- public void showNoContributionsUI(boolean shouldShow) {
- contributionsListFragment.showNoContributionsUI(shouldShow);
- }
-
- @Override
- public void setUploadCount(int count) {
- this.numberOfContributions=count;
- }
-
- @Override
- public void showContributions(List contributionList) {
- contributionsListFragment.setContributions(contributionList);
- }
-
/**
* Retry upload when it is failed
*
* @param contribution contribution to be retried
*/
- private void retryUpload(Contribution contribution) {
+ @Override
+ public void retryUpload(Contribution contribution) {
if (NetworkUtils.isInternetConnectionEstablished(getContext())) {
if (contribution.getState() == STATE_FAILED && null != uploadService) {
uploadService.queue(contribution);
@@ -596,5 +466,29 @@ public class ContributionsFragment
}
}
+
+ /**
+ * Replace whatever is in the current contributionsFragmentContainer view with
+ * mediaDetailPagerFragment, and preserve previous state in back stack. Called when user selects a
+ * contribution.
+ */
+ @Override
+ public void showDetail(int position) {
+ if (mediaDetailPagerFragment == null || !mediaDetailPagerFragment.isVisible()) {
+ mediaDetailPagerFragment = new MediaDetailPagerFragment();
+ showMediaDetailPagerFragment();
+ }
+ mediaDetailPagerFragment.showImage(position);
+ }
+
+ @Override
+ public Media getMediaAtPosition(int i) {
+ return contributionsListFragment.getMediaAtPosition(i);
+ }
+
+ @Override
+ public int getTotalMediaCount() {
+ return contributionsListFragment.getTotalMediaCount();
+ }
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.java
index 82daf97f4..a0c8e1088 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.java
@@ -1,68 +1,71 @@
-package fr.free.nrw.commons.contributions;
+ package fr.free.nrw.commons.contributions;
-import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
+import androidx.paging.PagedListAdapter;
+import androidx.recyclerview.widget.DiffUtil;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.media.MediaClient;
-import java.util.ArrayList;
-import java.util.List;
/**
- * Represents The View Adapter for the List of Contributions
+ * Represents The View Adapter for the List of Contributions
*/
-public class ContributionsListAdapter extends RecyclerView.Adapter {
+public class ContributionsListAdapter extends
+ PagedListAdapter {
- private Callback callback;
+ private final Callback callback;
private final MediaClient mediaClient;
- private List contributions;
- public ContributionsListAdapter(Callback callback,
- MediaClient mediaClient) {
+ ContributionsListAdapter(final Callback callback,
+ final MediaClient mediaClient) {
+ super(DIFF_CALLBACK);
this.callback = callback;
this.mediaClient = mediaClient;
- contributions = new ArrayList<>();
}
/**
- * Creates the new View Holder which will be used to display items(contributions)
- * using the onBindViewHolder(viewHolder,position)
+ * Uses DiffUtil to calculate the changes in the list
+ * It has methods that check ID and the content of the items to determine if its a new item
*/
- @NonNull
- @Override
- public ContributionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- ContributionViewHolder viewHolder = new ContributionViewHolder(
- LayoutInflater.from(parent.getContext())
- .inflate(R.layout.layout_contribution, parent, false), callback, mediaClient);
- return viewHolder;
- }
+ private static final DiffUtil.ItemCallback DIFF_CALLBACK =
+ new DiffUtil.ItemCallback() {
+ @Override
+ public boolean areItemsTheSame(final Contribution oldContribution, final Contribution newContribution) {
+ return oldContribution.getPageId().equals(newContribution.getPageId());
+ }
- @Override
- public void onBindViewHolder(@NonNull ContributionViewHolder holder, int position) {
- final Contribution contribution = contributions.get(position);
- if (TextUtils.isEmpty(contribution.getThumbUrl())
- && contribution.getState() == Contribution.STATE_COMPLETED) {
- callback.fetchMediaUriFor(contribution);
- }
+ @Override
+ public boolean areContentsTheSame(final Contribution oldContribution, final Contribution newContribution) {
+ return oldContribution.equals(newContribution);
+ }
+ };
+ /**
+ * Initializes the view holder with contribution data
+ */
+ @Override
+ public void onBindViewHolder(@NonNull final ContributionViewHolder holder, final int position) {
+ final Contribution contribution = getItem(position);
holder.init(position, contribution);
}
- @Override
- public int getItemCount() {
- return contributions.size();
- }
-
- public void setContributions(@NonNull List contributionList) {
- contributions = contributionList;
- notifyDataSetChanged();
+ Contribution getContributionForPosition(final int position) {
+ return getItem(position);
}
+ /**
+ * Creates the new View Holder which will be used to display items(contributions) using the
+ * onBindViewHolder(viewHolder,position)
+ */
+ @NonNull
@Override
- public long getItemId(int position) {
- return contributions.get(position).get_id();
+ public ContributionViewHolder onCreateViewHolder(@NonNull final ViewGroup parent,
+ final int viewType) {
+ final ContributionViewHolder viewHolder = new ContributionViewHolder(
+ LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.layout_contribution, parent, false), callback, mediaClient);
+ return viewHolder;
}
public interface Callback {
@@ -72,9 +75,5 @@ public class ContributionsListAdapter extends RecyclerView.Adapter {
+
+ void deleteUpload(Contribution contribution);
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java
index d7f813c6d..d707409e4 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java
@@ -5,6 +5,7 @@ import static android.view.View.VISIBLE;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.Parcelable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -16,218 +17,217 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.LayoutManager;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
+import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
-import fr.free.nrw.commons.contributions.ContributionsListAdapter.Callback;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
-import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.media.MediaClient;
-import java.util.ArrayList;
-import java.util.List;
+import fr.free.nrw.commons.media.MediaDetailPagerFragment;
import javax.inject.Inject;
-import javax.inject.Named;
/**
* Created by root on 01.06.2018.
*/
-public class ContributionsListFragment extends CommonsDaggerSupportFragment {
+public class ContributionsListFragment extends CommonsDaggerSupportFragment implements
+ ContributionsListContract.View, ContributionsListAdapter.Callback {
- private static final String VISIBLE_ITEM_ID = "visible_item_id";
- @BindView(R.id.contributionsList)
- RecyclerView rvContributionsList;
- @BindView(R.id.loadingContributionsProgressBar)
- ProgressBar progressBar;
- @BindView(R.id.fab_plus)
- FloatingActionButton fabPlus;
- @BindView(R.id.fab_camera)
- FloatingActionButton fabCamera;
- @BindView(R.id.fab_gallery)
- FloatingActionButton fabGallery;
- @BindView(R.id.noContributionsYet)
- TextView noContributionsYet;
- @BindView(R.id.fab_layout)
- LinearLayout fab_layout;
+ private static final String RV_STATE = "rv_scroll_state";
- @Inject @Named("default_preferences") JsonKvStore kvStore;
- @Inject ContributionController controller;
- @Inject MediaClient mediaClient;
+ @BindView(R.id.contributionsList)
+ RecyclerView rvContributionsList;
+ @BindView(R.id.loadingContributionsProgressBar)
+ ProgressBar progressBar;
+ @BindView(R.id.fab_plus)
+ FloatingActionButton fabPlus;
+ @BindView(R.id.fab_camera)
+ FloatingActionButton fabCamera;
+ @BindView(R.id.fab_gallery)
+ FloatingActionButton fabGallery;
+ @BindView(R.id.noContributionsYet)
+ TextView noContributionsYet;
+ @BindView(R.id.fab_layout)
+ LinearLayout fab_layout;
- private Animation fab_close;
- private Animation fab_open;
- private Animation rotate_forward;
- private Animation rotate_backward;
+ @Inject
+ ContributionController controller;
+ @Inject
+ MediaClient mediaClient;
+
+ @Inject
+ ContributionsListPresenter contributionsListPresenter;
+
+ private Animation fab_close;
+ private Animation fab_open;
+ private Animation rotate_forward;
+ private Animation rotate_backward;
- private boolean isFabOpen = false;
+ private boolean isFabOpen;
- private ContributionsListAdapter adapter;
+ private ContributionsListAdapter adapter;
- private Callback callback;
- private String lastVisibleItemID;
+ private final Callback callback;
- private int SPAN_COUNT=3;
- private List contributions=new ArrayList<>();
+ private final int SPAN_COUNT_LANDSCAPE = 3;
+ private final int SPAN_COUNT_PORTRAIT = 1;
- public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.fragment_contributions_list, container, false);
- ButterKnife.bind(this, view);
- initAdapter();
- return view;
+ ContributionsListFragment(final Callback callback) {
+ this.callback = callback;
+ }
+
+ public View onCreateView(
+ final LayoutInflater inflater, @Nullable final ViewGroup container,
+ @Nullable final Bundle savedInstanceState) {
+ final View view = inflater.inflate(R.layout.fragment_contributions_list, container, false);
+ ButterKnife.bind(this, view);
+ contributionsListPresenter.onAttachView(this);
+ initAdapter();
+ return view;
+ }
+
+ private void initAdapter() {
+ adapter = new ContributionsListAdapter(this, mediaClient);
+ }
+
+ @Override
+ public void onViewCreated(final View view, @Nullable final Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ initRecyclerView();
+ initializeAnimations();
+ setListeners();
+ }
+
+ private void initRecyclerView() {
+ final GridLayoutManager layoutManager = new GridLayoutManager(getContext(),
+ getSpanCount(getResources().getConfiguration().orientation));
+ rvContributionsList.setLayoutManager(layoutManager);
+ contributionsListPresenter.setup();
+ contributionsListPresenter.contributionList.observe(this, adapter::submitList);
+ rvContributionsList.setAdapter(adapter);
+ }
+
+ private int getSpanCount(final int orientation) {
+ return orientation == Configuration.ORIENTATION_LANDSCAPE ?
+ SPAN_COUNT_LANDSCAPE : SPAN_COUNT_PORTRAIT;
+ }
+
+ @Override
+ public void onConfigurationChanged(final Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ // check orientation
+ fab_layout.setOrientation(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ?
+ LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
+ rvContributionsList
+ .setLayoutManager(new GridLayoutManager(getContext(), getSpanCount(newConfig.orientation)));
+ }
+
+ private void initializeAnimations() {
+ fab_open = AnimationUtils.loadAnimation(getActivity(), R.anim.fab_open);
+ fab_close = AnimationUtils.loadAnimation(getActivity(), R.anim.fab_close);
+ rotate_forward = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate_forward);
+ rotate_backward = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate_backward);
+ }
+
+ private void setListeners() {
+ fabPlus.setOnClickListener(view -> animateFAB(isFabOpen));
+ fabCamera.setOnClickListener(view -> {
+ controller.initiateCameraPick(getActivity());
+ animateFAB(isFabOpen);
+ });
+ fabGallery.setOnClickListener(view -> {
+ controller.initiateGalleryPick(getActivity(), true);
+ animateFAB(isFabOpen);
+ });
+ }
+
+ private void animateFAB(final boolean isFabOpen) {
+ this.isFabOpen = !isFabOpen;
+ if (fabPlus.isShown()) {
+ if (isFabOpen) {
+ fabPlus.startAnimation(rotate_backward);
+ fabCamera.startAnimation(fab_close);
+ fabGallery.startAnimation(fab_close);
+ fabCamera.hide();
+ fabGallery.hide();
+ } else {
+ fabPlus.startAnimation(rotate_forward);
+ fabCamera.startAnimation(fab_open);
+ fabGallery.startAnimation(fab_open);
+ fabCamera.show();
+ fabGallery.show();
+ }
+ this.isFabOpen = !isFabOpen;
}
+ }
- public void setCallback(Callback callback) {
- this.callback = callback;
+ /**
+ * Shows welcome message if user has no contributions yet i.e. new user.
+ */
+ public void showWelcomeTip(final boolean shouldShow) {
+ noContributionsYet.setVisibility(shouldShow ? VISIBLE : GONE);
+ }
+
+ /**
+ * Responsible to set progress bar invisible and visible
+ *
+ * @param shouldShow True when contributions list should be hidden.
+ */
+ public void showProgress(final boolean shouldShow) {
+ progressBar.setVisibility(shouldShow ? VISIBLE : GONE);
+ }
+
+ public void showNoContributionsUI(final boolean shouldShow) {
+ noContributionsYet.setVisibility(shouldShow ? VISIBLE : GONE);
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ final GridLayoutManager layoutManager = (GridLayoutManager) rvContributionsList.getLayoutManager();
+ outState.putParcelable(RV_STATE, layoutManager.onSaveInstanceState());
+ }
+
+ @Override
+ public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
+ super.onViewStateRestored(savedInstanceState);
+ if (null != savedInstanceState) {
+ final Parcelable savedRecyclerLayoutState = savedInstanceState.getParcelable(RV_STATE);
+ rvContributionsList.getLayoutManager().onRestoreInstanceState(savedRecyclerLayoutState);
}
+ }
- private void initAdapter() {
- adapter = new ContributionsListAdapter(callback, mediaClient);
- adapter.setHasStableIds(true);
- }
+ @Override
+ public void retryUpload(final Contribution contribution) {
+ callback.retryUpload(contribution);
+ }
- @Override
- public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- initRecyclerView();
- initializeAnimations();
- setListeners();
- }
+ @Override
+ public void deleteUpload(final Contribution contribution) {
+ contributionsListPresenter.deleteUpload(contribution);
+ }
- private void initRecyclerView() {
- if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
- rvContributionsList.setLayoutManager(new GridLayoutManager(getContext(),SPAN_COUNT));
- } else {
- rvContributionsList.setLayoutManager(new LinearLayoutManager(getContext()));
- }
+ @Override
+ public void openMediaDetail(final int position) {
+ callback.showDetail(position);
+ }
- rvContributionsList.setAdapter(adapter);
- adapter.setContributions(contributions);
- }
+ public Media getMediaAtPosition(final int i) {
+ return adapter.getContributionForPosition(i);
+ }
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- // check orientation
- if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
- fab_layout.setOrientation(LinearLayout.HORIZONTAL);
- rvContributionsList.setLayoutManager(new GridLayoutManager(getContext(),SPAN_COUNT));
- } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
- fab_layout.setOrientation(LinearLayout.VERTICAL);
- rvContributionsList.setLayoutManager(new LinearLayoutManager(getContext()));
- }
- }
+ public int getTotalMediaCount() {
+ return adapter.getItemCount();
+ }
- private void initializeAnimations() {
- fab_open = AnimationUtils.loadAnimation(getActivity(), R.anim.fab_open);
- fab_close = AnimationUtils.loadAnimation(getActivity(), R.anim.fab_close);
- rotate_forward = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate_forward);
- rotate_backward = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate_backward);
- }
+ public interface Callback {
- private void setListeners() {
- fabPlus.setOnClickListener(view -> animateFAB(isFabOpen));
- fabCamera.setOnClickListener(view -> {
- controller.initiateCameraPick(getActivity());
- animateFAB(isFabOpen);
- });
- fabGallery.setOnClickListener(view -> {
- controller.initiateGalleryPick(getActivity(), true);
- animateFAB(isFabOpen);
- });
- }
-
- private void animateFAB(boolean isFabOpen) {
- this.isFabOpen = !isFabOpen;
- if (fabPlus.isShown()){
- if (isFabOpen) {
- fabPlus.startAnimation(rotate_backward);
- fabCamera.startAnimation(fab_close);
- fabGallery.startAnimation(fab_close);
- fabCamera.hide();
- fabGallery.hide();
- } else {
- fabPlus.startAnimation(rotate_forward);
- fabCamera.startAnimation(fab_open);
- fabGallery.startAnimation(fab_open);
- fabCamera.show();
- fabGallery.show();
- }
- this.isFabOpen=!isFabOpen;
- }
- }
-
- /**
- * Shows welcome message if user has no contributions yet i.e. new user.
- */
- public void showWelcomeTip(boolean shouldShow) {
- noContributionsYet.setVisibility(shouldShow ? VISIBLE : GONE);
- }
-
- /**
- * Responsible to set progress bar invisible and visible
- *
- * @param shouldShow True when contributions list should be hidden.
- */
- public void showProgress(boolean shouldShow) {
- progressBar.setVisibility(shouldShow ? VISIBLE : GONE);
- }
-
- public void showNoContributionsUI(boolean shouldShow) {
- noContributionsYet.setVisibility(shouldShow ? VISIBLE : GONE);
- }
-
- public void setContributions(List contributionList) {
- this.contributions.clear();
- this.contributions.addAll(contributionList);
- adapter.setContributions(contributions);
- }
-
- public interface SourceRefresher {
- void refreshSource();
- }
-
- @Override
- public void onSaveInstanceState(@NonNull Bundle outState) {
- super.onSaveInstanceState(outState);
- LayoutManager layoutManager = rvContributionsList.getLayoutManager();
- int lastVisibleItemPosition=0;
- if(layoutManager instanceof LinearLayoutManager){
- lastVisibleItemPosition= ((LinearLayoutManager) layoutManager).findLastCompletelyVisibleItemPosition();
- }else if(layoutManager instanceof GridLayoutManager){
- lastVisibleItemPosition=((GridLayoutManager)layoutManager).findLastCompletelyVisibleItemPosition();
- }
- String idOfItemWithPosition = findIdOfItemWithPosition(lastVisibleItemPosition);
- if (null != idOfItemWithPosition) {
- outState.putString(VISIBLE_ITEM_ID, idOfItemWithPosition);
- }
- }
-
- @Override
- public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
- super.onViewStateRestored(savedInstanceState);
- if(null!=savedInstanceState){
- lastVisibleItemID =savedInstanceState.getString(VISIBLE_ITEM_ID, null);
- }
- }
-
-
- /**
- * Gets the id of the contribution from the db
- * @param position
- * @return
- */
- @Nullable
- private String findIdOfItemWithPosition(int position) {
- Contribution contributionForPosition = callback.getContributionForPosition(position);
- if (null != contributionForPosition) {
- return contributionForPosition.getFilename();
- }
- return null;
- }
+ void retryUpload(Contribution contribution);
+ void showDetail(int position);
+ }
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.java
new file mode 100644
index 000000000..f09c5f205
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.java
@@ -0,0 +1,71 @@
+package fr.free.nrw.commons.contributions;
+
+import androidx.lifecycle.LiveData;
+import androidx.paging.LivePagedListBuilder;
+import androidx.paging.PagedList;
+import fr.free.nrw.commons.contributions.ContributionsListContract.UserActionListener;
+import fr.free.nrw.commons.di.CommonsApplicationModule;
+import io.reactivex.Scheduler;
+import io.reactivex.disposables.CompositeDisposable;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * The presenter class for Contributions
+ */
+public class ContributionsListPresenter implements UserActionListener {
+
+ private final ContributionBoundaryCallback contributionBoundaryCallback;
+ private final ContributionsRepository repository;
+ private final Scheduler ioThreadScheduler;
+
+ private final CompositeDisposable compositeDisposable;
+
+ LiveData> contributionList;
+
+ @Inject
+ ContributionsListPresenter(
+ final ContributionBoundaryCallback contributionBoundaryCallback,
+ final ContributionsRepository repository,
+ @Named(CommonsApplicationModule.IO_THREAD) final Scheduler ioThreadScheduler) {
+ this.contributionBoundaryCallback = contributionBoundaryCallback;
+ this.repository = repository;
+ this.ioThreadScheduler = ioThreadScheduler;
+ compositeDisposable = new CompositeDisposable();
+ }
+
+ @Override
+ public void onAttachView(final ContributionsListContract.View view) {
+ }
+
+ /**
+ * Setup the paged list. This method sets the configuration for paged list and ties it up with the
+ * live data object. This method can be tweaked to update the lazy loading behavior of the
+ * contributions list
+ */
+ void setup() {
+ final PagedList.Config pagedListConfig =
+ (new PagedList.Config.Builder())
+ .setPrefetchDistance(50)
+ .setPageSize(10).build();
+ contributionList = (new LivePagedListBuilder(repository.fetchContributions(), pagedListConfig)
+ .setBoundaryCallback(contributionBoundaryCallback)).build();
+ }
+
+ @Override
+ public void onDetachView() {
+ compositeDisposable.clear();
+ }
+
+ /**
+ * Delete a failed contribution from the local db
+ */
+ @Override
+ public void deleteUpload(final Contribution contribution) {
+ compositeDisposable.add(repository
+ .deleteContributionFromDB(contribution)
+ .subscribeOn(ioThreadScheduler)
+ .subscribe());
+ }
+
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java
index c16862ea2..3b94d0fa7 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java
@@ -1,14 +1,13 @@
package fr.free.nrw.commons.contributions;
-import androidx.lifecycle.LiveData;
-
+import androidx.paging.DataSource.Factory;
+import io.reactivex.Completable;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import fr.free.nrw.commons.kvstore.JsonKvStore;
-import io.reactivex.Completable;
import io.reactivex.Single;
/**
@@ -59,23 +58,23 @@ class ContributionsLocalDataSource {
* @param contribution
* @return
*/
- public Single deleteContribution(Contribution contribution) {
+ public Completable deleteContribution(Contribution contribution) {
return contributionDao.delete(contribution);
}
- public LiveData> getContributions() {
+ public Factory getContributions() {
return contributionDao.fetchContributions();
}
- public Completable saveContributions(List contributions) {
- return contributionDao.deleteAllAndSave(contributions);
+ public Single> saveContributions(List contributions) {
+ return contributionDao.save(contributions);
}
public void set(String key, long value) {
defaultKVStore.putLong(key,value);
}
- public Single updateContribution(Contribution contribution) {
+ public Completable updateContribution(Contribution contribution) {
return contributionDao.update(contribution);
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java
index 310699cd1..b4725fed8 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java
@@ -9,8 +9,8 @@ import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.MediaDataExtractor;
import fr.free.nrw.commons.auth.SessionManager;
+import fr.free.nrw.commons.MediaDataExtractor;
import fr.free.nrw.commons.contributions.ContributionsContract.UserActionListener;
-import fr.free.nrw.commons.db.AppDatabase;
import fr.free.nrw.commons.di.CommonsApplicationModule;
import fr.free.nrw.commons.mwapi.UserClient;
import fr.free.nrw.commons.utils.NetworkUtils;
@@ -25,6 +25,9 @@ import javax.inject.Inject;
import javax.inject.Named;
import timber.log.Timber;
+import javax.inject.Inject;
+import javax.inject.Named;
+
/**
* The presenter class for Contributions
*/
@@ -35,25 +38,10 @@ public class ContributionsPresenter implements UserActionListener {
private final Scheduler ioThreadScheduler;
private CompositeDisposable compositeDisposable;
private ContributionsContract.View view;
- private List contributionList=new ArrayList<>();
-
- @Inject
- Context context;
-
- @Inject
- UserClient userClient;
-
- @Inject
- AppDatabase appDatabase;
-
- @Inject
- SessionManager sessionManager;
@Inject
MediaDataExtractor mediaDataExtractor;
- private LifecycleOwner lifeCycleOwner;
-
@Inject
ContributionsPresenter(ContributionsRepository repository, @Named(CommonsApplicationModule.MAIN_THREAD) Scheduler mainThreadScheduler,@Named(CommonsApplicationModule.IO_THREAD) Scheduler ioThreadScheduler) {
this.repository = repository;
@@ -61,74 +49,12 @@ public class ContributionsPresenter implements UserActionListener {
this.ioThreadScheduler=ioThreadScheduler;
}
- private String user;
-
@Override
public void onAttachView(ContributionsContract.View view) {
this.view = view;
compositeDisposable=new CompositeDisposable();
}
- public void setLifeCycleOwner(LifecycleOwner lifeCycleOwner){
- this.lifeCycleOwner=lifeCycleOwner;
- }
-
- public void fetchContributions() {
- Timber.d("fetch Contributions");
- LiveData> liveDataContributions = repository.fetchContributions();
- if(null!=lifeCycleOwner) {
- liveDataContributions.observe(lifeCycleOwner, this::showContributions);
- }
-
- if (NetworkUtils.isInternetConnectionEstablished(CommonsApplication.getInstance()) && shouldFetchContributions()) {
- Timber.d("fetching contributions: ");
- view.showProgress(true);
- this.user = sessionManager.getUserName();
- view.showContributions(Collections.emptyList());
- compositeDisposable.add(userClient.logEvents(user)
- .subscribeOn(ioThreadScheduler)
- .observeOn(mainThreadScheduler)
- .doOnNext(mwQueryLogEvent -> Timber.d("Received image %s", mwQueryLogEvent.title()))
- .filter(mwQueryLogEvent -> !mwQueryLogEvent.isDeleted()).doOnNext(mwQueryLogEvent -> Timber.d("Image %s passed filters", mwQueryLogEvent.title()))
- .map(image -> new Contribution(image, user))
- .toList()
- .subscribe(this::saveContributionsToDB, error -> {
- Timber.e("Failed to fetch contributions: %s", error.getMessage());
- }));
- }
- }
-
- private void showContributions(@NonNull List contributions) {
- view.showProgress(false);
- if (contributions.isEmpty()) {
- view.showWelcomeTip(true);
- view.showNoContributionsUI(true);
- } else {
- view.showWelcomeTip(false);
- view.showNoContributionsUI(false);
- view.setUploadCount(contributions.size());
- view.showContributions(contributions);
- this.contributionList.clear();
- this.contributionList.addAll(contributions);
- }
- }
-
- private void saveContributionsToDB(List contributions) {
- Timber.e("Fetched: "+contributions.size()+" contributions "+" saving to db");
- repository.save(contributions).subscribeOn(ioThreadScheduler).subscribe();
- repository.set("last_fetch_timestamp",System.currentTimeMillis());
- }
-
- private boolean shouldFetchContributions() {
- long lastFetchTimestamp = repository.getLong("last_fetch_timestamp");
- Timber.d("last fetch timestamp: %s", lastFetchTimestamp);
- if(lastFetchTimestamp!=0){
- return System.currentTimeMillis()-lastFetchTimestamp>15*60*100;
- }
- Timber.d("should fetch contributions: %s", true);
- return true;
- }
-
@Override
public void onDetachView() {
this.view = null;
@@ -146,24 +72,10 @@ public class ContributionsPresenter implements UserActionListener {
*/
@Override
public void deleteUpload(Contribution contribution) {
- compositeDisposable.add(repository.deleteContributionFromDB(contribution)
- .subscribeOn(ioThreadScheduler)
- .subscribe());
- }
-
- /**
- * Returns a contribution at the specified cursor position
- *
- * @param i
- * @return
- */
- @Nullable
- @Override
- public Media getItemAtPosition(int i) {
- if (i == -1 || contributionList.size() < i+1) {
- return null;
- }
- return contributionList.get(i);
+ compositeDisposable.add(repository
+ .deleteContributionFromDB(contribution)
+ .subscribeOn(ioThreadScheduler)
+ .subscribe());
}
@Override
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java
index 500babaf5..17b004802 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java
@@ -1,12 +1,11 @@
package fr.free.nrw.commons.contributions;
-import androidx.lifecycle.LiveData;
-
+import androidx.paging.DataSource.Factory;
+import io.reactivex.Completable;
import java.util.List;
import javax.inject.Inject;
-import io.reactivex.Completable;
import io.reactivex.Single;
/**
@@ -33,7 +32,7 @@ public class ContributionsRepository {
* @param contribution
* @return
*/
- public Single deleteContributionFromDB(Contribution contribution) {
+ public Completable deleteContributionFromDB(Contribution contribution) {
return localDataSource.deleteContribution(contribution);
}
@@ -46,11 +45,11 @@ public class ContributionsRepository {
return localDataSource.getContributionWithFileName(fileName);
}
- public LiveData> fetchContributions() {
+ public Factory fetchContributions() {
return localDataSource.getContributions();
}
- public Completable save(List contributions) {
+ public Single> save(List contributions) {
return localDataSource.saveContributions(contributions);
}
@@ -58,11 +57,7 @@ public class ContributionsRepository {
localDataSource.set(key,value);
}
- public long getLong(String key) {
- return localDataSource.getLong(key);
- }
-
- public Single updateContribution(Contribution contribution) {
+ public Completable updateContribution(Contribution contribution) {
return localDataSource.updateContribution(contribution);
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java
index 648e21ef6..9606abee4 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java
@@ -2,7 +2,6 @@ package fr.free.nrw.commons.contributions;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
-import android.content.ContentResolver;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
@@ -12,7 +11,6 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
-
import androidx.annotation.Nullable;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
@@ -20,16 +18,9 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
-
-import com.google.android.material.tabs.TabLayout;
-
-import java.util.List;
-
-import javax.inject.Inject;
-
import butterknife.BindView;
import butterknife.ButterKnife;
-import fr.free.nrw.commons.BuildConfig;
+import com.google.android.material.tabs.TabLayout;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.location.LocationServiceManager;
@@ -44,10 +35,10 @@ import fr.free.nrw.commons.upload.UploadService;
import fr.free.nrw.commons.utils.ViewUtil;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
+import java.util.List;
+import javax.inject.Inject;
import timber.log.Timber;
-import static android.content.ContentResolver.requestSync;
-
public class MainActivity extends NavigationBaseActivity implements FragmentManager.OnBackStackChangedListener {
@BindView(R.id.tab_layout)
diff --git a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt
index 01095dd7c..a45fd0c6e 100644
--- a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt
+++ b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt
@@ -10,7 +10,7 @@ import fr.free.nrw.commons.contributions.ContributionDao
* The database for accessing the respective DAOs
*
*/
-@Database(entities = [Contribution::class], version = 1, exportSchema = false)
+@Database(entities = [Contribution::class], version = 2, exportSchema = false)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun contributionDao(): ContributionDao
diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java
index f19ab4f3a..c76c54673 100644
--- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java
+++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java
@@ -224,7 +224,9 @@ public class CommonsApplicationModule {
@Provides
@Singleton
public AppDatabase provideAppDataBase() {
- return Room.databaseBuilder(applicationContext, AppDatabase.class, "commons_room.db").build();
+ return Room.databaseBuilder(applicationContext, AppDatabase.class, "commons_room.db")
+ .fallbackToDestructiveMigration()
+ .build();
}
@Provides
diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaClient.java b/app/src/main/java/fr/free/nrw/commons/media/MediaClient.java
index 3bbe842b5..0ddd9fc70 100644
--- a/app/src/main/java/fr/free/nrw/commons/media/MediaClient.java
+++ b/app/src/main/java/fr/free/nrw/commons/media/MediaClient.java
@@ -32,6 +32,7 @@ public class MediaClient {
//OkHttpJsonApiClient used JsonKvStore for this. I don't know why.
private Map> continuationStore;
+ private Map continuationExists;
public static final String NO_CAPTION = "No caption";
private static final String NO_DEPICTION = "No depiction";
@@ -40,6 +41,7 @@ public class MediaClient {
this.mediaInterface = mediaInterface;
this.mediaDetailInterface = mediaDetailInterface;
this.continuationStore = new HashMap<>();
+ this.continuationExists = new HashMap<>();
}
/**
@@ -83,6 +85,36 @@ public class MediaClient {
}
+ /**
+ * This method takes the userName as input and returns a list of Media objects filtered using
+ * allimages query It uses the allimages query API to get the images contributed by the userName,
+ * 10 at a time.
+ *
+ * @param userName the username
+ * @return
+ */
+ public Single> getMediaListForUser(String userName) {
+ Map continuation =
+ continuationStore.containsKey("user_" + userName)
+ ? continuationStore.get("user_" + userName)
+ : Collections.emptyMap();
+ return responseToMediaList(mediaInterface
+ .getMediaListForUser(userName, 10, continuation), "user_" + userName);
+ }
+
+ /**
+ * Check if media for user has reached the end of the list.
+ * @param userName
+ * @return
+ */
+ public boolean doesMediaListForUserHaveMorePages(String userName) {
+ final String key = "user_" + userName;
+ if(continuationExists.containsKey(key)) {
+ return continuationExists.get(key);
+ }
+ return true;
+ }
+
/**
* This method takes a keyword as input and returns a list of Media objects filtered using image generator query
* It uses the generator query API to get the images searched using a query, 10 at a time.
@@ -106,7 +138,12 @@ public class MediaClient {
|| null == mwQueryResponse.query().pages()) {
return Observable.empty();
}
- continuationStore.put(key, mwQueryResponse.continuation());
+ if(mwQueryResponse.continuation() != null) {
+ continuationStore.put(key, mwQueryResponse.continuation());
+ continuationExists.put(key, true);
+ } else {
+ continuationExists.put(key, false);
+ }
return Observable.fromIterable(mwQueryResponse.query().pages());
})
.map(Media::from)
diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java
index 39c81386e..3e5f3f0fd 100644
--- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java
@@ -23,26 +23,21 @@ import butterknife.ButterKnife;
import com.google.android.material.snackbar.Snackbar;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
-import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.bookmarks.Bookmark;
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider;
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
import fr.free.nrw.commons.category.CategoryImagesCallback;
import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
-import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.utils.DownloadUtils;
import fr.free.nrw.commons.utils.ImageUtils;
import fr.free.nrw.commons.utils.NetworkUtils;
import fr.free.nrw.commons.utils.ViewUtil;
import javax.inject.Inject;
-import javax.inject.Named;
import timber.log.Timber;
public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment implements ViewPager.OnPageChangeListener {
- @Inject SessionManager sessionManager;
- @Inject @Named("default_preferences") JsonKvStore store;
@Inject BookmarkPicturesDao bookmarkDao;
@BindView(R.id.mediaDetailsPager) ViewPager pager;
diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.java b/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.java
index 10dd02a83..e102a8fcb 100644
--- a/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.java
+++ b/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.java
@@ -1,11 +1,9 @@
package fr.free.nrw.commons.media;
-import org.wikipedia.dataclient.mwapi.MwQueryResponse;
-
-import java.util.Map;
-
import fr.free.nrw.commons.depictions.models.DepictionResponse;
import io.reactivex.Observable;
+import java.util.Map;
+import org.wikipedia.dataclient.mwapi.MwQueryResponse;
import retrofit2.http.GET;
import retrofit2.http.Query;
import retrofit2.http.QueryMap;
@@ -17,6 +15,7 @@ public interface MediaInterface {
String MEDIA_PARAMS="&prop=imageinfo&iiprop=url|extmetadata&iiurlwidth=640" +
"&iiextmetadatafilter=DateTime|Categories|GPSLatitude|GPSLongitude|ImageDescription|DateTimeOriginal" +
"|Artist|LicenseShortName|LicenseUrl";
+
/**
* Checks if a page exists or not.
*
@@ -48,6 +47,19 @@ public interface MediaInterface {
MEDIA_PARAMS)
Observable getMediaListFromCategory(@Query("gcmtitle") String category, @Query("gcmlimit") int itemLimit, @QueryMap Map continuation);
+ /**
+ * This method retrieves a list of Media objects for a given user name
+ *
+ * @param username user's Wikimedia Commons username.
+ * @param itemLimit how many images are returned
+ * @param continuation the continuation string from the previous query or empty map
+ * @return
+ */
+ @GET("w/api.php?action=query&format=json&formatversion=2" + //Basic parameters
+ "&generator=allimages&gaisort=timestamp&gaidir=older" + MEDIA_PARAMS)
+ Observable getMediaListForUser(@Query("gaiuser") String username,
+ @Query("gailimit") int itemLimit, @QueryMap(encoded = true) Map continuation);
+
/**
* This method retrieves a list of Media objects filtered using image generator query
*
@@ -86,21 +98,15 @@ public interface MediaInterface {
Observable getPageHtml(@Query("page") String title);
/**
- * Fetches caption using file name
- *
- * @param filename name of the file to be used for fetching captions
- * */
- @GET("w/api.php?action=wbgetentities&props=labels&format=json&languagefallback=1")
- Observable fetchCaptionByFilename(@Query("language") String language, @Query("titles") String filename);
+ * Fetches list of images from a depiction entity
+ *
+ * @param query depictionEntityId
+ * @param sroffset number od depictions already fetched, this is useful in implementing
+ * pagination
+ */
- /**
- * Fetches list of images from a depiction entity
- *
- * @param query depictionEntityId
- * @param sroffset number od depictions already fetched, this is useful in implementing pagination
- */
-
- @GET("w/api.php?action=query&list=search&format=json&srnamespace=6")
- Observable fetchImagesForDepictedItem(@Query("srsearch") String query, @Query("sroffset") String sroffset);
+ @GET("w/api.php?action=query&list=search&format=json&srnamespace=6")
+ Observable fetchImagesForDepictedItem(@Query("srsearch") String query,
+ @Query("sroffset") String sroffset);
}
diff --git a/app/src/main/java/fr/free/nrw/commons/settings/Prefs.java b/app/src/main/java/fr/free/nrw/commons/settings/Prefs.java
index 7ea86a019..8f625205c 100644
--- a/app/src/main/java/fr/free/nrw/commons/settings/Prefs.java
+++ b/app/src/main/java/fr/free/nrw/commons/settings/Prefs.java
@@ -6,7 +6,6 @@ public class Prefs {
public static String TRACKING_ENABLED = "eventLogging";
public static final String DEFAULT_LICENSE = "defaultLicense";
public static final String UPLOADS_SHOWING = "uploadsshowing";
- public static final String IS_CONTRIBUTION_COUNT_CHANGED = "ccontributionCountChanged";
public static final String MANAGED_EXIF_TAGS = "managed_exif_tags";
public static final String KEY_LANGUAGE_VALUE = "languageDescription";
public static final String KEY_THEME_VALUE = "appThemePref";
diff --git a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java
index 00ef4ed4b..53cf8d7fe 100644
--- a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java
@@ -65,43 +65,6 @@ public class SettingsFragment extends PreferenceFragmentCompat {
});
}
- final EditTextPreference uploadLimit = findPreference("uploads");
- int currentUploadLimit = defaultKvStore.getInt(Prefs.UPLOADS_SHOWING, 100);
- uploadLimit.setText(String.valueOf(currentUploadLimit));
-
- uploadLimit.setOnPreferenceChangeListener((preference, newValue) -> {
-
- if (newValue.toString().length() == 0) {
- return false;
- }
-
- int value = Integer.parseInt(newValue.toString());
- if (value > 500) {
- Snackbar error = Snackbar.make(getView(), R.string.maximum_limit_alert, Snackbar.LENGTH_LONG);
- error.show();
- return false;
- } else if (value == 0) {
- Snackbar error = Snackbar.make(getView(), R.string.cannot_be_zero, Snackbar.LENGTH_LONG);
- error.show();
- return false;
- }
- return true;
- });
-
- uploadLimit.setOnBindEditTextListener(editText -> {
-
- editText.setInputType(InputType.TYPE_CLASS_NUMBER);
- editText.selectAll();
- int maxLength = 3; // set maxLength to 3
- editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
-
- int value = Integer.parseInt(editText.getText().toString());
-
- defaultKvStore.putInt(Prefs.UPLOADS_SHOWING, value);
- defaultKvStore.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, true);
- uploadLimit.setText(Integer.toString(value));
- });
-
langListPreference = findPreference("descriptionDefaultLanguagePref");
prepareLanguages();
Preference betaTesterPreference = findPreference("becomeBetaTester");
@@ -121,7 +84,6 @@ public class SettingsFragment extends PreferenceFragmentCompat {
findPreference("displayNearbyCardView").setEnabled(false);
findPreference("displayLocationPermissionForCardView").setEnabled(false);
findPreference("displayCampaignsCardView").setEnabled(false);
- uploadLimit.setEnabled(false);
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java
index 89f7fce12..0f88be382 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java
@@ -23,9 +23,14 @@ import fr.free.nrw.commons.di.CommonsDaggerService;
import fr.free.nrw.commons.media.MediaClient;
import fr.free.nrw.commons.utils.CommonsDateUtil;
import fr.free.nrw.commons.wikidata.WikidataEditService;
+import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.Scheduler;
+import io.reactivex.Single;
import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.functions.Action;
+import io.reactivex.functions.Consumer;
import io.reactivex.processors.PublishProcessor;
import io.reactivex.schedulers.Schedulers;
import java.io.File;
@@ -33,6 +38,7 @@ import java.io.IOException;
import java.text.ParseException;
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
@@ -106,10 +112,10 @@ public class UploadService extends CommonsDaggerService {
notificationManager.notify(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS, curNotification.build());
contribution.setTransferred(transferred);
- compositeDisposable.add(contributionDao.
- save(contribution).subscribeOn(ioThreadScheduler)
- .observeOn(mainThreadScheduler)
- .subscribe());
+
+ compositeDisposable.add(contributionDao.update(contribution)
+ .subscribeOn(ioThreadScheduler)
+ .subscribe());
}
}
@@ -156,14 +162,11 @@ public class UploadService extends CommonsDaggerService {
Timber.d("%d uploads left", toUpload);
notificationManager.notify(contribution.getLocalUri().toString(), NOTIFICATION_UPLOAD_IN_PROGRESS, curNotification.build());
}
+
compositeDisposable.add(contributionDao
.save(contribution)
.subscribeOn(ioThreadScheduler)
- .observeOn(mainThreadScheduler)
- .subscribe(aLong->{
- contribution.set_id(aLong);
- uploadContribution(contribution);
- }, Throwable::printStackTrace));
+ .subscribe(() -> uploadContribution(contribution)));
}
private boolean freshStart = true;
@@ -269,7 +272,7 @@ public class UploadService extends CommonsDaggerService {
}
private void onUpload(Contribution contribution, String notificationTag,
- UploadResult uploadResult) throws ParseException {
+ UploadResult uploadResult) {
Timber.d("Stash upload response 2 is %s", uploadResult.toString());
notificationManager.cancel(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS);
@@ -282,8 +285,7 @@ public class UploadService extends CommonsDaggerService {
}
}
- private void onSuccessfulUpload(Contribution contribution, UploadResult uploadResult)
- throws ParseException {
+ private void onSuccessfulUpload(Contribution contribution, UploadResult uploadResult) {
compositeDisposable
.add(wikidataEditService.addDepictionsAndCaptions(uploadResult, contribution));
WikidataPlace wikidataPlace = contribution.getWikidataPlace();
@@ -293,17 +295,11 @@ public class UploadService extends CommonsDaggerService {
saveCompletedContribution(contribution, uploadResult);
}
- private void saveCompletedContribution(Contribution contribution, UploadResult uploadResult) throws ParseException {
- contribution.setFilename(uploadResult.createCanonicalFileName());
- contribution.setImageUrl(uploadResult.getImageinfo().getOriginalUrl());
- contribution.setState(Contribution.STATE_COMPLETED);
- contribution.setDateUploaded(CommonsDateUtil.getIso8601DateFormatTimestamp()
- .parse(uploadResult.getImageinfo().getTimestamp()));
- compositeDisposable.add(contributionDao
- .save(contribution)
- .subscribeOn(ioThreadScheduler)
- .observeOn(mainThreadScheduler)
- .subscribe());
+ private void saveCompletedContribution(Contribution contribution, UploadResult uploadResult) {
+ compositeDisposable.add(mediaClient.getMedia("File:" + uploadResult.getFilename())
+ .map(media -> new Contribution(media, Contribution.STATE_COMPLETED))
+ .flatMapCompletable(newContribution -> contributionDao.saveAndDelete(contribution, newContribution))
+ .subscribe());
}
@SuppressLint("StringFormatInvalid")
@@ -317,10 +313,11 @@ public class UploadService extends CommonsDaggerService {
notificationManager.notify(contribution.getLocalUri().toString(), NOTIFICATION_UPLOAD_FAILED, curNotification.build());
contribution.setState(Contribution.STATE_FAILED);
- compositeDisposable.add(contributionDao.save(contribution)
- .subscribeOn(ioThreadScheduler)
- .observeOn(mainThreadScheduler)
- .subscribe());
+
+ compositeDisposable.add(contributionDao
+ .update(contribution)
+ .subscribeOn(ioThreadScheduler)
+ .subscribe());
}
private String findUniqueFilename(String fileName) throws IOException {
diff --git a/app/src/main/java/fr/free/nrw/commons/utils/CommonsDateUtil.java b/app/src/main/java/fr/free/nrw/commons/utils/CommonsDateUtil.java
index d03dd8fe4..f831baa0a 100644
--- a/app/src/main/java/fr/free/nrw/commons/utils/CommonsDateUtil.java
+++ b/app/src/main/java/fr/free/nrw/commons/utils/CommonsDateUtil.java
@@ -21,6 +21,16 @@ public class CommonsDateUtil {
return simpleDateFormat;
}
+ /**
+ * Gets SimpleDateFormat for date pattern returned by Media object
+ * @return simpledateformat
+ */
+ public static SimpleDateFormat getMediaSimpleDateFormat() {
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ROOT);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return simpleDateFormat;
+ }
+
/**
* Gets the timestamp pattern for a date
* @return timestamp
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index e58dbefd6..634c147e0 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -26,13 +26,6 @@
android:summary="@string/use_external_storage_summary"
android:title="@string/use_external_storage" />
-
-
()))
+ .thenReturn(Single.just(listOf(1L, 2L)))
+ whenever(sessionManager.userName).thenReturn("Test")
+ whenever(mediaClient.getMediaListForUser(anyString())).thenReturn(
+ Single.just(listOf(mock(Media::class.java)))
+ )
+ whenever(mediaClient.doesMediaListForUserHaveMorePages(anyString()))
+ .thenReturn(true)
+ contributionBoundaryCallback.onZeroItemsLoaded()
+ verify(repository).save(anyList());
+ verify(mediaClient).getMediaListForUser(anyString());
+ }
+
+ @Test
+ fun testOnLastItemLoaded() {
+ whenever(repository.save(anyList()))
+ .thenReturn(Single.just(listOf(1L, 2L)))
+ whenever(sessionManager.userName).thenReturn("Test")
+ whenever(mediaClient.getMediaListForUser(anyString())).thenReturn(
+ Single.just(listOf(mock(Media::class.java)))
+ )
+ whenever(mediaClient.doesMediaListForUserHaveMorePages(anyString()))
+ .thenReturn(true)
+ contributionBoundaryCallback.onItemAtEndLoaded(mock(Contribution::class.java))
+ verify(repository).save(anyList());
+ verify(mediaClient).getMediaListForUser(anyString());
+ }
+
+ @Test
+ fun testOnFrontItemLoaded() {
+ whenever(repository.save(anyList()))
+ .thenReturn(Single.just(listOf(1L, 2L)))
+ whenever(sessionManager.userName).thenReturn("Test")
+ whenever(mediaClient.getMediaListForUser(anyString())).thenReturn(
+ Single.just(listOf(mock(Media::class.java)))
+ )
+ whenever(mediaClient.doesMediaListForUserHaveMorePages(anyString()))
+ .thenReturn(true)
+ contributionBoundaryCallback.onItemAtFrontLoaded(mock(Contribution::class.java))
+ verify(repository).save(anyList());
+ verify(mediaClient).getMediaListForUser(anyString());
+ }
+
+ @Test
+ fun testFetchContributions() {
+ whenever(repository.save(anyList()))
+ .thenReturn(Single.just(listOf(1L, 2L)))
+ whenever(sessionManager.userName).thenReturn("Test")
+ whenever(mediaClient.getMediaListForUser(anyString())).thenReturn(
+ Single.just(listOf(mock(Media::class.java)))
+ )
+ whenever(mediaClient.doesMediaListForUserHaveMorePages(anyString()))
+ .thenReturn(true)
+ contributionBoundaryCallback.fetchContributions()
+ verify(repository).save(anyList());
+ verify(mediaClient).getMediaListForUser(anyString());
+ }
+
+ @Test
+ fun testFetchContributionsForEndOfList() {
+ whenever(sessionManager.userName).thenReturn("Test")
+ whenever(mediaClient.doesMediaListForUserHaveMorePages(anyString()))
+ .thenReturn(false)
+ contributionBoundaryCallback.fetchContributions()
+ verify(mediaClient, times(0)).getMediaListForUser(anyString())
+ verifyNoMoreInteractions(repository)
+ }
+
+ @Test
+ fun testFetchContributionsFailed() {
+ whenever(sessionManager.userName).thenReturn("Test")
+ whenever(mediaClient.doesMediaListForUserHaveMorePages(anyString()))
+ .thenReturn(true)
+ whenever(mediaClient.getMediaListForUser(anyString())).thenReturn(Single.error(Exception("Error")))
+ contributionBoundaryCallback.fetchContributions()
+ verifyZeroInteractions(repository);
+ verify(mediaClient).getMediaListForUser(anyString());
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsListPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsListPresenterTest.kt
new file mode 100644
index 000000000..7e9b723a7
--- /dev/null
+++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsListPresenterTest.kt
@@ -0,0 +1,63 @@
+package fr.free.nrw.commons.contributions
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import com.nhaarman.mockitokotlin2.times
+import com.nhaarman.mockitokotlin2.verify
+import com.nhaarman.mockitokotlin2.verifyZeroInteractions
+import com.nhaarman.mockitokotlin2.whenever
+import fr.free.nrw.commons.Media
+import fr.free.nrw.commons.auth.SessionManager
+import fr.free.nrw.commons.media.MediaClient
+import io.reactivex.Completable
+import io.reactivex.Scheduler
+import io.reactivex.Single
+import io.reactivex.schedulers.Schedulers
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
+
+/**
+ * The unit test class for ContributionsListPresenterTest
+ */
+class ContributionsListPresenterTest {
+ @Mock
+ internal lateinit var contributionBoundaryCallback: ContributionBoundaryCallback
+
+ @Mock
+ internal lateinit var repository: ContributionsRepository
+
+ @Rule
+ @JvmField
+ var instantTaskExecutorRule = InstantTaskExecutorRule()
+
+ lateinit var scheduler: Scheduler
+
+ lateinit var contributionsListPresenter: ContributionsListPresenter
+
+ /**
+ * initial setup
+ */
+ @Before
+ @Throws(Exception::class)
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ scheduler = Schedulers.trampoline()
+ contributionsListPresenter =
+ ContributionsListPresenter(contributionBoundaryCallback, repository, scheduler);
+ }
+
+ @Test
+ fun testDeleteUpload() {
+ whenever(repository.deleteContributionFromDB(any()))
+ .thenReturn(Completable.complete())
+ contributionsListPresenter.deleteUpload(mock(Contribution::class.java))
+ verify(repository, times(1))
+ .deleteContributionFromDB(ArgumentMatchers.any(Contribution::class.java));
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsPresenterTest.kt
index e318b2ea7..05546504a 100644
--- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsPresenterTest.kt
+++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsPresenterTest.kt
@@ -7,9 +7,11 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.loader.content.CursorLoader
import androidx.loader.content.Loader
+import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
+import io.reactivex.Completable
import io.reactivex.Scheduler
import io.reactivex.Single
import io.reactivex.schedulers.TestScheduler
@@ -17,9 +19,11 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.*
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
+import java.util.concurrent.TimeUnit
/**
* The unit test class for ContributionsPresenter
@@ -42,7 +46,7 @@ class ContributionsPresenterTest {
@Rule @JvmField var instantTaskExecutorRule = InstantTaskExecutorRule()
- lateinit var scheduler : Scheduler
+ lateinit var scheduler : TestScheduler
/**
* initial setup
@@ -60,23 +64,13 @@ class ContributionsPresenterTest {
liveData=MutableLiveData()
}
- /**
- * Test fetch contributions
- */
- @Test
- fun testFetchContributions(){
- whenever(repository.getString(ArgumentMatchers.anyString())).thenReturn("10")
- whenever(repository.fetchContributions()).thenReturn(liveData)
- contributionsPresenter.fetchContributions()
- verify(repository).fetchContributions()
- }
-
/**
* Test presenter actions onDeleteContribution
*/
@Test
fun testDeleteContribution() {
- whenever(repository.deleteContributionFromDB(ArgumentMatchers.any(Contribution::class.java))).thenReturn(Single.just(1))
+ whenever(repository.deleteContributionFromDB(ArgumentMatchers.any()))
+ .thenReturn(Completable.complete())
contributionsPresenter.deleteUpload(contribution)
verify(repository).deleteContributionFromDB(contribution)
}
diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsRepositoryTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsRepositoryTest.kt
new file mode 100644
index 000000000..0b11c4bb4
--- /dev/null
+++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsRepositoryTest.kt
@@ -0,0 +1,55 @@
+package fr.free.nrw.commons.contributions
+
+import com.nhaarman.mockitokotlin2.times
+import com.nhaarman.mockitokotlin2.verify
+import com.nhaarman.mockitokotlin2.whenever
+import fr.free.nrw.commons.utils.createMockDataSourceFactory
+import io.reactivex.Scheduler
+import io.reactivex.Single
+import junit.framework.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.mockito.*
+import org.mockito.Mockito.any
+import org.mockito.Mockito.mock
+
+/**
+ * The unit test class for ContributionsRepositoryTest
+ */
+class ContributionsRepositoryTest {
+ @Mock
+ internal lateinit var localDataSource: ContributionsLocalDataSource
+
+ @InjectMocks
+ private lateinit var contributionsRepository: ContributionsRepository
+
+ lateinit var scheduler: Scheduler
+
+ /**
+ * initial setup
+ */
+ @Before
+ @Throws(Exception::class)
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun testFetchContributions() {
+ val contribution = mock(Contribution::class.java)
+ whenever(localDataSource.getContributions())
+ .thenReturn(createMockDataSourceFactory(listOf(contribution)))
+ val contributionsFactory = contributionsRepository.fetchContributions()
+ verify(localDataSource, times(1)).getContributions();
+ }
+
+ @Test
+ fun testSaveContribution() {
+ val contributions = listOf(mock(Contribution::class.java))
+ whenever(localDataSource.saveContributions(ArgumentMatchers.anyList()))
+ .thenReturn(Single.just(listOf(1L)))
+ val save = contributionsRepository.save(contributions).test().assertValueAt(0) {
+ it.size == 1 && it.get(0) == 1L
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/kotlin/fr/free/nrw/commons/media/MediaClientTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/media/MediaClientTest.kt
index 5579ce72f..d594e722b 100644
--- a/app/src/test/kotlin/fr/free/nrw/commons/media/MediaClientTest.kt
+++ b/app/src/test/kotlin/fr/free/nrw/commons/media/MediaClientTest.kt
@@ -1,5 +1,6 @@
package fr.free.nrw.commons.media
+import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.utils.CommonsDateUtil
import io.reactivex.Observable
@@ -7,18 +8,16 @@ import junit.framework.Assert.*
import org.junit.Before
import org.junit.Test
import org.mockito.*
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.mock
import org.wikipedia.dataclient.mwapi.ImageDetails
import org.wikipedia.dataclient.mwapi.MwQueryPage
import org.wikipedia.dataclient.mwapi.MwQueryResponse
import org.wikipedia.dataclient.mwapi.MwQueryResult
import org.wikipedia.gallery.ImageInfo
import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.*
import java.util.*
import org.mockito.Captor
-
-
+import org.mockito.Mockito.*
class MediaClientTest {
@@ -46,9 +45,10 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
- .thenReturn(Observable.just(mockResponse))
+ .thenReturn(Observable.just(mockResponse))
- val checkPageExistsUsingTitle = mediaClient!!.checkPageExistsUsingTitle("File:Test.jpg").blockingGet()
+ val checkPageExistsUsingTitle =
+ mediaClient!!.checkPageExistsUsingTitle("File:Test.jpg").blockingGet()
assertTrue(checkPageExistsUsingTitle)
}
@@ -63,9 +63,10 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
- .thenReturn(Observable.just(mockResponse))
+ .thenReturn(Observable.just(mockResponse))
- val checkPageExistsUsingTitle = mediaClient!!.checkPageExistsUsingTitle("File:Test.jpg").blockingGet()
+ val checkPageExistsUsingTitle =
+ mediaClient!!.checkPageExistsUsingTitle("File:Test.jpg").blockingGet()
assertFalse(checkPageExistsUsingTitle)
}
@@ -80,7 +81,7 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.checkFileExistsUsingSha(ArgumentMatchers.anyString()))
- .thenReturn(Observable.just(mockResponse))
+ .thenReturn(Observable.just(mockResponse))
val checkFileExistsUsingSha = mediaClient!!.checkFileExistsUsingSha("abcde").blockingGet()
assertTrue(checkFileExistsUsingSha)
@@ -97,7 +98,7 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.checkFileExistsUsingSha(ArgumentMatchers.anyString()))
- .thenReturn(Observable.just(mockResponse))
+ .thenReturn(Observable.just(mockResponse))
val checkFileExistsUsingSha = mediaClient!!.checkFileExistsUsingSha("abcde").blockingGet()
assertFalse(checkFileExistsUsingSha)
@@ -117,7 +118,7 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.getMedia(ArgumentMatchers.anyString()))
- .thenReturn(Observable.just(mockResponse))
+ .thenReturn(Observable.just(mockResponse))
assertEquals("Test", mediaClient!!.getMedia("abcde").blockingGet().filename)
}
@@ -136,10 +137,11 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.getMedia(ArgumentMatchers.anyString()))
- .thenReturn(Observable.just(mockResponse))
+ .thenReturn(Observable.just(mockResponse))
assertEquals(Media.EMPTY, mediaClient!!.getMedia("abcde").blockingGet())
}
+
@Captor
private val filenameCaptor: ArgumentCaptor? = null
@@ -159,18 +161,18 @@ class MediaClientTest {
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.getMediaWithGenerator(filenameCaptor!!.capture()))
- .thenReturn(Observable.just(mockResponse))
+ .thenReturn(Observable.just(mockResponse))
assertEquals("Test", mediaClient!!.getPictureOfTheDay().blockingGet().filename)
assertEquals(template, filenameCaptor.value);
}
-
+
@Captor
private val continuationCaptor: ArgumentCaptor