mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-29 22:03:55 +01:00
With lazy loading of contributions (#3566)
This commit is contained in:
parent
c216fdf0d4
commit
d863a404f1
34 changed files with 1397 additions and 928 deletions
|
|
@ -2,7 +2,6 @@ package fr.free.nrw.commons.contributions;
|
|||
|
||||
import android.os.Parcel;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.PrimaryKey;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.upload.UploadMediaDetail;
|
||||
|
|
@ -13,7 +12,7 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryLogEvent;
|
||||
import java.util.Objects;
|
||||
|
||||
@Entity(tableName = "contribution")
|
||||
public class Contribution extends Media {
|
||||
|
|
@ -24,24 +23,21 @@ public class Contribution extends Media {
|
|||
public static final int STATE_QUEUED = 2;
|
||||
public static final int STATE_IN_PROGRESS = 3;
|
||||
|
||||
@PrimaryKey (autoGenerate = true)
|
||||
private long _id;
|
||||
private int state;
|
||||
private long transferred;
|
||||
private String decimalCoords;
|
||||
private String dateCreatedSource;
|
||||
private WikidataPlace wikidataPlace;
|
||||
/**
|
||||
* Each depiction loaded in depictions activity is associated with a wikidata entity id,
|
||||
* this Id is in turn used to upload depictions to wikibase
|
||||
* Each depiction loaded in depictions activity is associated with a wikidata entity id, this Id
|
||||
* is in turn used to upload depictions to wikibase
|
||||
*/
|
||||
private List<DepictedItem> depictedItems = new ArrayList<>();
|
||||
private String mimeType;
|
||||
/**
|
||||
* This hasmap stores the list of multilingual captions, where
|
||||
* key of the HashMap is the language and value is the caption in the corresponding language
|
||||
* Ex: key = "en", value: "<caption in short in English>"
|
||||
* key = "de" , value: "<caption in german>"
|
||||
* This hasmap stores the list of multilingual captions, where key of the HashMap is the language
|
||||
* and value is the caption in the corresponding language Ex: key = "en", value: "<caption in
|
||||
* short in English>" key = "de" , value: "<caption in german>"
|
||||
*/
|
||||
private Map<String, String> 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
|
||||
* <p>
|
||||
* key of the HashMap is the language and value is the caption in the corresponding language
|
||||
*
|
||||
* <p>
|
||||
* returns list of captions stored in hashmap
|
||||
*/
|
||||
public Map<String, String> getCaptions() {
|
||||
return captions;
|
||||
return captions;
|
||||
}
|
||||
|
||||
public void setCaptions(Map<String, String> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Contribution>() {
|
||||
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<Media?> ->
|
||||
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<Contribution>) {
|
||||
compositeDisposable.add(
|
||||
repository.save(contributions)
|
||||
.subscribeOn(ioThreadScheduler)
|
||||
.subscribe { longs: List<Long?>? ->
|
||||
repository["last_fetch_timestamp"] = System.currentTimeMillis()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<List<Contribution>> fetchContributions();
|
||||
@Query("SELECT * FROM contribution order by dateUploaded DESC")
|
||||
abstract DataSource.Factory<Integer, Contribution> fetchContributions();
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract Single<Long> save(Contribution contribution);
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract void saveSynchronous(Contribution contribution);
|
||||
|
||||
public Completable deleteAllAndSave(List<Contribution> contributions){
|
||||
return Completable.fromAction(() -> deleteAllAndSaveTransaction(contributions));
|
||||
}
|
||||
public Completable save(final Contribution contribution) {
|
||||
return Completable
|
||||
.fromAction(() -> saveSynchronous(contribution));
|
||||
}
|
||||
|
||||
@Transaction
|
||||
public void deleteAllAndSaveTransaction(List<Contribution> 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> contribution);
|
||||
public Completable saveAndDelete(final Contribution oldContribution,
|
||||
final Contribution newContribution) {
|
||||
return Completable
|
||||
.fromAction(() -> deleteAndSaveContribution(oldContribution, newContribution));
|
||||
}
|
||||
|
||||
@Delete
|
||||
public abstract Single<Integer> delete(Contribution contribution);
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract Single<List<Long>> save(List<Contribution> contribution);
|
||||
|
||||
@Query("SELECT * from contribution WHERE filename=:fileName")
|
||||
public abstract List<Contribution> getContributionWithTitle(String fileName);
|
||||
@Delete
|
||||
public abstract void deleteSynchronous(Contribution contribution);
|
||||
|
||||
@Query("UPDATE contribution SET state=:state WHERE state in (:toUpdateStates)")
|
||||
public abstract Single<Integer> 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<Contribution> 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<Integer> updateStates(int state, int[] toUpdateStates);
|
||||
|
||||
@Update
|
||||
public abstract Single<Integer> 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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<Contribution> 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);
|
||||
|
|
|
|||
|
|
@ -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<Contribution> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ContributionViewHolder> {
|
||||
public class ContributionsListAdapter extends
|
||||
PagedListAdapter<Contribution, ContributionViewHolder> {
|
||||
|
||||
private Callback callback;
|
||||
private final Callback callback;
|
||||
private final MediaClient mediaClient;
|
||||
private List<Contribution> 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<Contribution> DIFF_CALLBACK =
|
||||
new DiffUtil.ItemCallback<Contribution>() {
|
||||
@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<Contribution> 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<ContributionV
|
|||
void deleteUpload(Contribution contribution);
|
||||
|
||||
void openMediaDetail(int contribution);
|
||||
|
||||
Contribution getContributionForPosition(int position);
|
||||
|
||||
void fetchMediaUriFor(Contribution contribution);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package fr.free.nrw.commons.contributions;
|
||||
|
||||
import fr.free.nrw.commons.BasePresenter;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The contract for Contributions list View & Presenter
|
||||
*/
|
||||
public class ContributionsListContract {
|
||||
|
||||
public interface View {
|
||||
|
||||
void showWelcomeTip(boolean numberOfUploads);
|
||||
|
||||
void showProgress(boolean shouldShow);
|
||||
|
||||
void showNoContributionsUI(boolean shouldShow);
|
||||
}
|
||||
|
||||
public interface UserActionListener extends BasePresenter<View> {
|
||||
|
||||
void deleteUpload(Contribution contribution);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Contribution> 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<Contribution> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<PagedList<Contribution>> 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<Integer> deleteContribution(Contribution contribution) {
|
||||
public Completable deleteContribution(Contribution contribution) {
|
||||
return contributionDao.delete(contribution);
|
||||
}
|
||||
|
||||
public LiveData<List<Contribution>> getContributions() {
|
||||
public Factory<Integer, Contribution> getContributions() {
|
||||
return contributionDao.fetchContributions();
|
||||
}
|
||||
|
||||
public Completable saveContributions(List<Contribution> contributions) {
|
||||
return contributionDao.deleteAllAndSave(contributions);
|
||||
public Single<List<Long>> saveContributions(List<Contribution> contributions) {
|
||||
return contributionDao.save(contributions);
|
||||
}
|
||||
|
||||
public void set(String key, long value) {
|
||||
defaultKVStore.putLong(key,value);
|
||||
}
|
||||
|
||||
public Single<Integer> updateContribution(Contribution contribution) {
|
||||
public Completable updateContribution(Contribution contribution) {
|
||||
return contributionDao.update(contribution);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Contribution> 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<List<Contribution>> 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<Contribution> 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<Contribution> 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
|
||||
|
|
|
|||
|
|
@ -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<Integer> 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<List<Contribution>> fetchContributions() {
|
||||
public Factory<Integer, Contribution> fetchContributions() {
|
||||
return localDataSource.getContributions();
|
||||
}
|
||||
|
||||
public Completable save(List<Contribution> contributions) {
|
||||
public Single<List<Long>> save(List<Contribution> 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<Integer> updateContribution(Contribution contribution) {
|
||||
public Completable updateContribution(Contribution contribution) {
|
||||
return localDataSource.updateContribution(contribution);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue