mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 21:03:54 +01:00
parent
34ab6f581b
commit
0e5ba98c2e
82 changed files with 474 additions and 1480 deletions
|
|
@ -51,7 +51,7 @@ import fr.free.nrw.commons.Utils;
|
|||
import fr.free.nrw.commons.WelcomeActivity;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
import fr.free.nrw.commons.explore.categories.ExploreActivity;
|
||||
import fr.free.nrw.commons.explore.ExploreActivity;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
package fr.free.nrw.commons.depictions
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import fr.free.nrw.commons.depictions.Media.DepictedImagesContract
|
||||
import fr.free.nrw.commons.depictions.Media.DepictedImagesPresenter
|
||||
import fr.free.nrw.commons.depictions.subClass.SubDepictionListContract
|
||||
import fr.free.nrw.commons.depictions.subClass.SubDepictionListPresenter
|
||||
|
||||
/**
|
||||
* The Dagger Module for explore:depictions related presenters and (some other objects maybe in future)
|
||||
*/
|
||||
@Module
|
||||
abstract class DepictionModule {
|
||||
@Binds
|
||||
abstract fun SubDepictionListPresenter.bindsSubDepictionListPresenter()
|
||||
: SubDepictionListContract.UserActionListener
|
||||
|
||||
|
||||
@Binds
|
||||
abstract fun DepictedImagesPresenter.bindsDepictedImagesContractPresenter()
|
||||
: DepictedImagesContract.Presenter
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package fr.free.nrw.commons.depictions.Media
|
||||
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.explore.PagingContract
|
||||
|
||||
/**
|
||||
* Contract with which DepictedImagesFragment and its presenter will talk to each other
|
||||
*/
|
||||
interface DepictedImagesContract {
|
||||
interface View : PagingContract.View<Media>
|
||||
interface Presenter : PagingContract.Presenter<Media>
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
package fr.free.nrw.commons.depictions.models;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Model class for object obtained while parsing depiction response
|
||||
*/
|
||||
public class Continue {
|
||||
|
||||
@SerializedName("sroffset")
|
||||
@Expose
|
||||
private Integer sroffset;
|
||||
@SerializedName("continue")
|
||||
@Expose
|
||||
private String _continue;
|
||||
|
||||
/**
|
||||
* No args constructor for use in serialization
|
||||
*
|
||||
*/
|
||||
public Continue() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sroffset
|
||||
* @param _continue
|
||||
*/
|
||||
public Continue(Integer sroffset, String _continue) {
|
||||
super();
|
||||
this.sroffset = sroffset;
|
||||
this._continue = _continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets sroffset from Continue object
|
||||
*/
|
||||
public Integer getSroffset() {
|
||||
return sroffset;
|
||||
}
|
||||
|
||||
public void setSroffset(Integer sroffset) {
|
||||
this.sroffset = sroffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets continue string from Continue object
|
||||
*/
|
||||
public String getContinue() {
|
||||
return _continue;
|
||||
}
|
||||
|
||||
public void setContinue(String _continue) {
|
||||
this._continue = _continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
package fr.free.nrw.commons.depictions.models;
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Model class for object obtained while parsing depiction response
|
||||
*
|
||||
* the getSearch() function is used to parse media
|
||||
*/
|
||||
public class Query {
|
||||
|
||||
@SerializedName("searchinfo")
|
||||
@Expose
|
||||
private Searchinfo searchinfo;
|
||||
@SerializedName("search")
|
||||
@Expose
|
||||
private List<Search> search = null;
|
||||
|
||||
/**
|
||||
* No args constructor for use in serialization
|
||||
*
|
||||
*/
|
||||
public Query() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param search
|
||||
* @param searchinfo
|
||||
*/
|
||||
public Query(Searchinfo searchinfo, List<Search> search) {
|
||||
super();
|
||||
this.searchinfo = searchinfo;
|
||||
this.search = search;
|
||||
}
|
||||
|
||||
/**
|
||||
* return searchInfo
|
||||
*/
|
||||
public Searchinfo getSearchinfo() {
|
||||
return searchinfo;
|
||||
}
|
||||
|
||||
public void setSearchinfo(Searchinfo searchinfo) {
|
||||
this.searchinfo = searchinfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* the getSearch() function is used to parse media
|
||||
*/
|
||||
public List<Search> getSearch() {
|
||||
return search;
|
||||
}
|
||||
|
||||
public void setSearch(List<Search> search) {
|
||||
this.search = search;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
package fr.free.nrw.commons.depictions.models;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Model class for object obtained while parsing depiction response
|
||||
* this class contains all the details of for the media object
|
||||
*/
|
||||
|
||||
public class Search {
|
||||
|
||||
@SerializedName("ns")
|
||||
@Expose
|
||||
private Integer ns;
|
||||
@SerializedName("title")
|
||||
@Expose
|
||||
private String title;
|
||||
@SerializedName("pageid")
|
||||
@Expose
|
||||
private Integer pageid;
|
||||
@SerializedName("size")
|
||||
@Expose
|
||||
private Integer size;
|
||||
@SerializedName("wordcount")
|
||||
@Expose
|
||||
private Integer wordcount;
|
||||
@SerializedName("snippet")
|
||||
@Expose
|
||||
private String snippet;
|
||||
@SerializedName("timestamp")
|
||||
@Expose
|
||||
private String timestamp;
|
||||
|
||||
/**
|
||||
* No args constructor for use in serialization
|
||||
*
|
||||
*/
|
||||
public Search() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param timestamp
|
||||
* @param title
|
||||
* @param ns
|
||||
* @param snippet
|
||||
* @param wordcount
|
||||
* @param size
|
||||
* @param pageid
|
||||
*/
|
||||
public Search(Integer ns, String title, Integer pageid, Integer size, Integer wordcount, String snippet, String timestamp) {
|
||||
super();
|
||||
this.ns = ns;
|
||||
this.title = title;
|
||||
this.pageid = pageid;
|
||||
this.size = size;
|
||||
this.wordcount = wordcount;
|
||||
this.snippet = snippet;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns ns int from Search object
|
||||
*/
|
||||
public Integer getNs() {
|
||||
return ns;
|
||||
}
|
||||
|
||||
public void setNs(Integer ns) {
|
||||
this.ns = ns;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns title string from Search object
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns pageid int from Search object
|
||||
*/
|
||||
public Integer getPageid() {
|
||||
return pageid;
|
||||
}
|
||||
|
||||
public void setPageid(Integer pageid) {
|
||||
this.pageid = pageid;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns size int from Search object
|
||||
*/
|
||||
public Integer getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(Integer size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns wordcount int from Search object
|
||||
*/
|
||||
public Integer getWordcount() {
|
||||
return wordcount;
|
||||
}
|
||||
|
||||
public void setWordcount(Integer wordcount) {
|
||||
this.wordcount = wordcount;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns snippet String from Search object
|
||||
*/
|
||||
public String getSnippet() {
|
||||
return snippet;
|
||||
}
|
||||
|
||||
public void setSnippet(String snippet) {
|
||||
this.snippet = snippet;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns ns int from Search object
|
||||
*/
|
||||
public String getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(String timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
package fr.free.nrw.commons.depictions.models;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Model class for object obtained while parsing query object
|
||||
*/
|
||||
|
||||
public class Searchinfo {
|
||||
|
||||
@SerializedName("totalhits")
|
||||
@Expose
|
||||
private Integer totalhits;
|
||||
|
||||
/**
|
||||
* No args constructor for use in serialization
|
||||
*
|
||||
*/
|
||||
public Searchinfo() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param totalhits
|
||||
*/
|
||||
public Searchinfo(Integer totalhits) {
|
||||
super();
|
||||
this.totalhits = totalhits;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns "totalhint" integer in SearchInfo object
|
||||
*/
|
||||
public Integer getTotalhits() {
|
||||
return totalhits;
|
||||
}
|
||||
|
||||
public void setTotalhits(Integer totalhits) {
|
||||
this.totalhits = totalhits;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package fr.free.nrw.commons.depictions.subClass
|
||||
|
||||
import fr.free.nrw.commons.explore.depictions.depictionDelegate
|
||||
import fr.free.nrw.commons.upload.categories.BaseDelegateAdapter
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
|
||||
class SubDepictionAdapter(clickListener: (DepictedItem) -> Unit) :
|
||||
BaseDelegateAdapter<DepictedItem>(
|
||||
depictionDelegate(clickListener),
|
||||
areItemsTheSame = { oldItem, newItem -> oldItem.id == newItem.id }
|
||||
)
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package fr.free.nrw.commons.depictions.subClass;
|
||||
|
||||
import fr.free.nrw.commons.BasePresenter;
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The contract with which SubDepictionListFragment and its presenter would talk to each other
|
||||
*/
|
||||
public interface SubDepictionListContract {
|
||||
|
||||
interface View {
|
||||
|
||||
void onSuccess(List<DepictedItem> mediaList);
|
||||
|
||||
void initErrorView();
|
||||
|
||||
void showSnackbar();
|
||||
|
||||
void setIsLastPage(boolean b);
|
||||
|
||||
}
|
||||
|
||||
interface UserActionListener extends BasePresenter<View> {
|
||||
|
||||
void initSubDepictionList(String qid, Boolean isParentClass) throws IOException;
|
||||
|
||||
String getQuery();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
package fr.free.nrw.commons.depictions.subClass;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
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 butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import dagger.android.support.DaggerFragment;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.depictions.WikidataItemDetailsActivity;
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||
import fr.free.nrw.commons.utils.NetworkUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.inject.Inject;
|
||||
import kotlin.Unit;
|
||||
|
||||
/**
|
||||
* Fragment for parent classes and child classes of Depicted items in Explore
|
||||
*/
|
||||
public class SubDepictionListFragment extends DaggerFragment implements SubDepictionListContract.View {
|
||||
|
||||
@BindView(R.id.imagesListBox)
|
||||
RecyclerView depictionsRecyclerView;
|
||||
@BindView(R.id.imageSearchInProgress)
|
||||
ProgressBar progressBar;
|
||||
@BindView(R.id.imagesNotFound)
|
||||
TextView depictionNotFound;
|
||||
@BindView(R.id.bottomProgressBar)
|
||||
ProgressBar bottomProgressBar;
|
||||
/**
|
||||
* Keeps a record of whether current instance of the fragment if of SubClass or ParentClass
|
||||
*/
|
||||
private boolean isParentClass = false;
|
||||
private SubDepictionAdapter depictionsAdapter;
|
||||
RecyclerView.LayoutManager layoutManager;
|
||||
/**
|
||||
* Stores entityId for the depiction
|
||||
*/
|
||||
private String entityId;
|
||||
/**
|
||||
* Stores name of the depiction searched
|
||||
*/
|
||||
private String depictsName;
|
||||
|
||||
@Inject SubDepictionListPresenter presenter;
|
||||
|
||||
private void initViews() {
|
||||
if (getArguments() != null) {
|
||||
depictsName = getArguments().getString("wikidataItemName");
|
||||
entityId = getArguments().getString("entityId");
|
||||
isParentClass = getArguments().getBoolean("isParentClass");
|
||||
if (entityId != null) {
|
||||
initList(entityId, isParentClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initList(String qid, Boolean isParentClass) {
|
||||
if (!NetworkUtils.isInternetConnectionEstablished(getContext())) {
|
||||
handleNoInternet();
|
||||
} else {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
try {
|
||||
presenter.initSubDepictionList(qid, isParentClass);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_browse_image, container, false);
|
||||
ButterKnife.bind(this, v);
|
||||
presenter.onAttachView(this);
|
||||
isParentClass = false;
|
||||
depictionNotFound.setVisibility(GONE);
|
||||
if (getActivity().getResources().getConfiguration().orientation
|
||||
== Configuration.ORIENTATION_PORTRAIT) {
|
||||
layoutManager = new LinearLayoutManager(getContext());
|
||||
} else {
|
||||
layoutManager = new GridLayoutManager(getContext(), 2);
|
||||
}
|
||||
initViews();
|
||||
depictionsRecyclerView.setLayoutManager(layoutManager);
|
||||
depictionsAdapter = new SubDepictionAdapter(depictedItem -> {
|
||||
// Open SubDepiction Details page
|
||||
getActivity().finish();
|
||||
WikidataItemDetailsActivity.startYourself(getContext(), depictedItem);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
depictionsRecyclerView.setAdapter(depictionsAdapter);
|
||||
return v;
|
||||
}
|
||||
|
||||
private void handleNoInternet() {
|
||||
progressBar.setVisibility(GONE);
|
||||
ViewUtil.showShortSnackbar(depictionsRecyclerView, R.string.no_internet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(List<DepictedItem> mediaList) {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
depictionNotFound.setVisibility(GONE);
|
||||
bottomProgressBar.setVisibility(GONE);
|
||||
depictionsAdapter.addAll(mediaList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initErrorView() {
|
||||
progressBar.setVisibility(GONE);
|
||||
bottomProgressBar.setVisibility(GONE);
|
||||
depictionNotFound.setVisibility(VISIBLE);
|
||||
String no_depiction = getString(isParentClass? R.string.no_parent_classes: R.string.no_child_classes);
|
||||
depictionNotFound.setText(String.format(Locale.getDefault(), no_depiction, depictsName));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showSnackbar() {
|
||||
ViewUtil.showShortSnackbar(depictionsRecyclerView, R.string.error_loading_depictions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIsLastPage(boolean b) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
package fr.free.nrw.commons.depictions.subClass;
|
||||
|
||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD;
|
||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.MAIN_THREAD;
|
||||
|
||||
import fr.free.nrw.commons.explore.depictions.DepictsClient;
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao;
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Presenter for parent classes and child classes of Depicted items in Explore
|
||||
*/
|
||||
public class SubDepictionListPresenter implements SubDepictionListContract.UserActionListener {
|
||||
|
||||
/**
|
||||
* This creates a dynamic proxy instance of the class,
|
||||
* proxy is to control access to the target object
|
||||
* here our target object is the view.
|
||||
* Thus we when onDettach method of fragment is called we replace the binding of view to our object with the proxy instance
|
||||
*/
|
||||
private static final SubDepictionListContract.View DUMMY = (SubDepictionListContract.View) Proxy
|
||||
.newProxyInstance(
|
||||
SubDepictionListContract.View.class.getClassLoader(),
|
||||
new Class[]{SubDepictionListContract.View.class},
|
||||
(proxy, method, methodArgs) -> null);
|
||||
|
||||
private final Scheduler ioScheduler;
|
||||
private final Scheduler mainThreadScheduler;
|
||||
private SubDepictionListContract.View view = DUMMY;
|
||||
RecentSearchesDao recentSearchesDao;
|
||||
/**
|
||||
* Value of the search query
|
||||
*/
|
||||
public String query;
|
||||
protected CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||
DepictsClient depictsClient;
|
||||
private List<DepictedItem> queryList = new ArrayList<>();
|
||||
OkHttpJsonApiClient okHttpJsonApiClient;
|
||||
|
||||
@Inject
|
||||
public SubDepictionListPresenter(RecentSearchesDao recentSearchesDao, DepictsClient depictsClient, OkHttpJsonApiClient okHttpJsonApiClient, @Named(IO_THREAD) Scheduler ioScheduler,
|
||||
@Named(MAIN_THREAD) Scheduler mainThreadScheduler) {
|
||||
this.recentSearchesDao = recentSearchesDao;
|
||||
this.ioScheduler = ioScheduler;
|
||||
this.mainThreadScheduler = mainThreadScheduler;
|
||||
this.depictsClient = depictsClient;
|
||||
this.okHttpJsonApiClient = okHttpJsonApiClient;
|
||||
}
|
||||
@Override
|
||||
public void onAttachView(SubDepictionListContract.View view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachView() {
|
||||
this.view = DUMMY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSubDepictionList(String qid, Boolean isParentClass) throws IOException {
|
||||
if (isParentClass) {
|
||||
compositeDisposable.add(okHttpJsonApiClient.getParentQIDs(qid)
|
||||
.subscribeOn(ioScheduler)
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribe(this::handleSuccess, this::handleError));
|
||||
} else {
|
||||
compositeDisposable.add(okHttpJsonApiClient.getChildQIDs(qid)
|
||||
.subscribeOn(ioScheduler)
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribe(this::handleSuccess, this::handleError));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the success scenario
|
||||
* it initializes the recycler view by adding items to the adapter
|
||||
*/
|
||||
public void handleSuccess(List<DepictedItem> mediaList) {
|
||||
if (mediaList == null || mediaList.isEmpty()) {
|
||||
if(queryList.isEmpty()){
|
||||
view.initErrorView();
|
||||
}else{
|
||||
view.setIsLastPage(true);
|
||||
}
|
||||
} else {
|
||||
this.queryList.addAll(mediaList);
|
||||
view.onSuccess(mediaList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs and handles API error scenario
|
||||
*/
|
||||
private void handleError(Throwable throwable) {
|
||||
Timber.e(throwable, "Error occurred while loading queried depictions");
|
||||
view.initErrorView();
|
||||
view.showSnackbar();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -11,9 +11,9 @@ import fr.free.nrw.commons.bookmarks.BookmarksActivity;
|
|||
import fr.free.nrw.commons.category.CategoryDetailsActivity;
|
||||
import fr.free.nrw.commons.category.CategoryImagesActivity;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.depictions.WikidataItemDetailsActivity;
|
||||
import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity;
|
||||
import fr.free.nrw.commons.explore.SearchActivity;
|
||||
import fr.free.nrw.commons.explore.categories.ExploreActivity;
|
||||
import fr.free.nrw.commons.explore.ExploreActivity;
|
||||
import fr.free.nrw.commons.notification.NotificationActivity;
|
||||
import fr.free.nrw.commons.review.ReviewActivity;
|
||||
import fr.free.nrw.commons.settings.SettingsActivity;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import dagger.android.support.AndroidSupportInjectionModule;
|
|||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.auth.LoginActivity;
|
||||
import fr.free.nrw.commons.contributions.ContributionsModule;
|
||||
import fr.free.nrw.commons.depictions.DepictionModule;
|
||||
import fr.free.nrw.commons.explore.depictions.DepictionModule;
|
||||
import fr.free.nrw.commons.explore.SearchModule;
|
||||
import fr.free.nrw.commons.review.ReviewController;
|
||||
import fr.free.nrw.commons.settings.SettingsFragment;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ import fr.free.nrw.commons.category.CategoryImagesListFragment;
|
|||
import fr.free.nrw.commons.category.SubCategoryListFragment;
|
||||
import fr.free.nrw.commons.contributions.ContributionsFragment;
|
||||
import fr.free.nrw.commons.contributions.ContributionsListFragment;
|
||||
import fr.free.nrw.commons.depictions.Media.DepictedImagesFragment;
|
||||
import fr.free.nrw.commons.depictions.subClass.SubDepictionListFragment;
|
||||
import fr.free.nrw.commons.explore.depictions.child.ChildDepictionsFragment;
|
||||
import fr.free.nrw.commons.explore.depictions.media.DepictedImagesFragment;
|
||||
import fr.free.nrw.commons.explore.depictions.parent.ParentDepictionsFragment;
|
||||
import fr.free.nrw.commons.explore.categories.SearchCategoryFragment;
|
||||
import fr.free.nrw.commons.explore.depictions.SearchDepictionsFragment;
|
||||
import fr.free.nrw.commons.explore.depictions.search.SearchDepictionsFragment;
|
||||
import fr.free.nrw.commons.explore.media.SearchMediaFragment;
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesFragment;
|
||||
import fr.free.nrw.commons.media.MediaDetailFragment;
|
||||
|
|
@ -51,9 +52,6 @@ public abstract class FragmentBuilderModule {
|
|||
@ContributesAndroidInjector
|
||||
abstract DepictedImagesFragment bindDepictedImagesFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract SubDepictionListFragment bindSubDepictionListFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract SubCategoryListFragment bindSubCategoryListFragment();
|
||||
|
||||
|
|
@ -95,4 +93,10 @@ public abstract class FragmentBuilderModule {
|
|||
|
||||
@ContributesAndroidInjector
|
||||
abstract MediaLicenseFragment bindMediaLicenseFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ParentDepictionsFragment bindParentDepictionsFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ChildDepictionsFragment bindChildDepictionsFragment();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package fr.free.nrw.commons.explore.categories;
|
||||
package fr.free.nrw.commons.explore;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
|
@ -19,8 +19,6 @@ import fr.free.nrw.commons.Media;
|
|||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.category.CategoryImagesCallback;
|
||||
import fr.free.nrw.commons.category.CategoryImagesListFragment;
|
||||
import fr.free.nrw.commons.explore.SearchActivity;
|
||||
import fr.free.nrw.commons.explore.ViewPagerAdapter;
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
package fr.free.nrw.commons.explore
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import fr.free.nrw.commons.R
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.list_item_load_more.*
|
||||
|
||||
class FooterAdapter(private val onRefreshClicked: () -> Unit) :
|
||||
ListAdapter<FooterItem, FooterViewHolder>(object :
|
||||
DiffUtil.ItemCallback<FooterItem>() {
|
||||
override fun areItemsTheSame(oldItem: FooterItem, newItem: FooterItem) = oldItem == newItem
|
||||
|
||||
override fun areContentsTheSame(oldItem: FooterItem, newItem: FooterItem) =
|
||||
oldItem == newItem
|
||||
}) {
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return getItem(position).ordinal
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
when (FooterItem.values()[viewType]) {
|
||||
FooterItem.LoadingItem -> LoadingViewHolder(parent.inflate(R.layout.list_item_progress))
|
||||
FooterItem.RefreshItem -> RefreshViewHolder(
|
||||
parent.inflate(R.layout.list_item_load_more),
|
||||
onRefreshClicked
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: FooterViewHolder, position: Int) {}
|
||||
}
|
||||
|
||||
open class FooterViewHolder(override val containerView: View) :
|
||||
RecyclerView.ViewHolder(containerView),
|
||||
LayoutContainer
|
||||
|
||||
class LoadingViewHolder(containerView: View) : FooterViewHolder(containerView)
|
||||
class RefreshViewHolder(containerView: View, onRefreshClicked: () -> Unit) :
|
||||
FooterViewHolder(containerView) {
|
||||
init {
|
||||
listItemLoadMoreButton.setOnClickListener { onRefreshClicked() }
|
||||
}
|
||||
}
|
||||
|
||||
enum class FooterItem { LoadingItem, RefreshItem }
|
||||
|
||||
fun ViewGroup.inflate(@LayoutRes layoutId: Int, attachToRoot: Boolean = false): View =
|
||||
LayoutInflater.from(context).inflate(layoutId, this, attachToRoot)
|
||||
|
|
@ -20,7 +20,7 @@ import fr.free.nrw.commons.Media;
|
|||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.category.CategoryImagesCallback;
|
||||
import fr.free.nrw.commons.explore.categories.SearchCategoryFragment;
|
||||
import fr.free.nrw.commons.explore.depictions.SearchDepictionsFragment;
|
||||
import fr.free.nrw.commons.explore.depictions.search.SearchDepictionsFragment;
|
||||
import fr.free.nrw.commons.explore.media.SearchMediaFragment;
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearch;
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao;
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
package fr.free.nrw.commons.explore
|
||||
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.explore.categories.SearchCategoriesFragmentContract
|
||||
import fr.free.nrw.commons.explore.categories.PageableCategoriesDataSource
|
||||
import io.reactivex.Scheduler
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
class SearchCategoriesFragmentPresenter @Inject constructor(
|
||||
@Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler,
|
||||
dataSourceFactory: PageableCategoriesDataSource
|
||||
) : BasePagingPresenter<String>(mainThreadScheduler, dataSourceFactory),
|
||||
SearchCategoriesFragmentContract.Presenter
|
||||
|
|
@ -2,11 +2,12 @@ package fr.free.nrw.commons.explore
|
|||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import fr.free.nrw.commons.explore.categories.SearchCategoriesFragmentContract
|
||||
import fr.free.nrw.commons.explore.depictions.SearchDepictionsFragmentContract
|
||||
import fr.free.nrw.commons.explore.depictions.SearchDepictionsFragmentPresenter
|
||||
import fr.free.nrw.commons.explore.media.SearchMediaFragmentContract
|
||||
import fr.free.nrw.commons.explore.categories.SearchCategoriesFragmentPresenter
|
||||
import fr.free.nrw.commons.explore.categories.SearchCategoriesFragmentPresenterImpl
|
||||
import fr.free.nrw.commons.explore.depictions.search.SearchDepictionsFragmentPresenter
|
||||
import fr.free.nrw.commons.explore.depictions.search.SearchDepictionsFragmentPresenterImpl
|
||||
import fr.free.nrw.commons.explore.media.SearchMediaFragmentPresenter
|
||||
import fr.free.nrw.commons.explore.media.SearchMediaFragmentPresenterImpl
|
||||
|
||||
/**
|
||||
* The Dagger Module for explore:depictions related presenters and (some other objects maybe in future)
|
||||
|
|
@ -14,14 +15,14 @@ import fr.free.nrw.commons.explore.media.SearchMediaFragmentPresenter
|
|||
@Module
|
||||
abstract class SearchModule {
|
||||
@Binds
|
||||
abstract fun SearchDepictionsFragmentPresenter.bindsSearchDepictionsFragmentPresenter()
|
||||
: SearchDepictionsFragmentContract.Presenter
|
||||
abstract fun SearchDepictionsFragmentPresenterImpl.bindsSearchDepictionsFragmentPresenter()
|
||||
: SearchDepictionsFragmentPresenter
|
||||
|
||||
@Binds
|
||||
abstract fun SearchCategoriesFragmentPresenter.bindsSearchCategoriesFragmentPresenter()
|
||||
: SearchCategoriesFragmentContract.Presenter
|
||||
abstract fun SearchCategoriesFragmentPresenterImpl.bindsSearchCategoriesFragmentPresenter()
|
||||
: SearchCategoriesFragmentPresenter
|
||||
|
||||
@Binds
|
||||
abstract fun SearchMediaFragmentPresenter.bindsSearchMediaFragmentPresenter()
|
||||
: SearchMediaFragmentContract.Presenter
|
||||
abstract fun SearchMediaFragmentPresenterImpl.bindsSearchMediaFragmentPresenter()
|
||||
: SearchMediaFragmentPresenter
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package fr.free.nrw.commons.explore.categories
|
||||
|
||||
import fr.free.nrw.commons.category.CategoryClient
|
||||
import fr.free.nrw.commons.explore.LiveDataConverter
|
||||
import fr.free.nrw.commons.explore.PageableBaseDataSource
|
||||
import fr.free.nrw.commons.explore.paging.LiveDataConverter
|
||||
import fr.free.nrw.commons.explore.paging.PageableBaseDataSource
|
||||
import javax.inject.Inject
|
||||
|
||||
class PageableCategoriesDataSource @Inject constructor(
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import androidx.paging.PagedListAdapter
|
|||
import androidx.recyclerview.widget.DiffUtil
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.category.CATEGORY_PREFIX
|
||||
import fr.free.nrw.commons.explore.BaseViewHolder
|
||||
import fr.free.nrw.commons.explore.inflate
|
||||
import fr.free.nrw.commons.explore.paging.BaseViewHolder
|
||||
import fr.free.nrw.commons.explore.paging.inflate
|
||||
import kotlinx.android.synthetic.main.item_recent_searches.*
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
package fr.free.nrw.commons.explore.categories
|
||||
|
||||
import fr.free.nrw.commons.explore.PagingContract
|
||||
|
||||
interface SearchCategoriesFragmentContract {
|
||||
interface View : PagingContract.View<String>
|
||||
interface Presenter : PagingContract.Presenter<String>
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package fr.free.nrw.commons.explore.categories
|
||||
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.explore.paging.BasePagingPresenter
|
||||
import fr.free.nrw.commons.explore.paging.PagingContract
|
||||
import io.reactivex.Scheduler
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
interface SearchCategoriesFragmentPresenter : PagingContract.Presenter<String>
|
||||
|
||||
class SearchCategoriesFragmentPresenterImpl @Inject constructor(
|
||||
@Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler,
|
||||
dataSourceFactory: PageableCategoriesDataSource
|
||||
) : BasePagingPresenter<String>(mainThreadScheduler, dataSourceFactory),
|
||||
SearchCategoriesFragmentPresenter
|
||||
|
|
@ -2,8 +2,8 @@ package fr.free.nrw.commons.explore.categories
|
|||
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.category.CategoryDetailsActivity
|
||||
import fr.free.nrw.commons.explore.BasePagingFragment
|
||||
import fr.free.nrw.commons.explore.PagingContract
|
||||
import fr.free.nrw.commons.explore.paging.BasePagingFragment
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
|
@ -11,7 +11,7 @@ import javax.inject.Inject
|
|||
*/
|
||||
class SearchCategoryFragment : BasePagingFragment<String>() {
|
||||
@Inject
|
||||
lateinit var presenter: SearchCategoriesFragmentContract.Presenter
|
||||
lateinit var presenter: SearchCategoriesFragmentPresenter
|
||||
|
||||
override val errorTextId: Int = R.string.error_loading_categories
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import androidx.paging.PagedListAdapter
|
|||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.explore.inflate
|
||||
import fr.free.nrw.commons.explore.paging.inflate
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_depictions.*
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
package fr.free.nrw.commons.explore.depictions
|
||||
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import kotlinx.android.synthetic.main.item_depictions.*
|
||||
|
||||
|
||||
fun depictionDelegate(onDepictionClicked: (DepictedItem) -> Unit) =
|
||||
adapterDelegateLayoutContainer<DepictedItem, DepictedItem>(R.layout.item_depictions) {
|
||||
containerView.setOnClickListener { onDepictionClicked(item) }
|
||||
bind {
|
||||
depicts_label.text = item.name
|
||||
description.text = item.description
|
||||
if (item.imageUrl?.isNotBlank() == true) {
|
||||
depicts_image.setImageURI(item.imageUrl)
|
||||
} else {
|
||||
depicts_image.setActualImageResource(R.drawable.ic_wikidata_logo_24dp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package fr.free.nrw.commons.explore.depictions
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import fr.free.nrw.commons.explore.depictions.child.ChildDepictionsPresenter
|
||||
import fr.free.nrw.commons.explore.depictions.child.ChildDepictionsPresenterImpl
|
||||
import fr.free.nrw.commons.explore.depictions.media.DepictedImagesPresenter
|
||||
import fr.free.nrw.commons.explore.depictions.media.DepictedImagesPresenterImpl
|
||||
import fr.free.nrw.commons.explore.depictions.parent.ParentDepictionsPresenter
|
||||
import fr.free.nrw.commons.explore.depictions.parent.ParentDepictionsPresenterImpl
|
||||
|
||||
/**
|
||||
* The Dagger Module for explore:depictions related presenters and (some other objects maybe in future)
|
||||
*/
|
||||
@Module
|
||||
abstract class DepictionModule {
|
||||
|
||||
@Binds
|
||||
abstract fun ParentDepictionsPresenterImpl.bindsParentDepictionPresenter()
|
||||
: ParentDepictionsPresenter
|
||||
|
||||
@Binds
|
||||
abstract fun ChildDepictionsPresenterImpl.bindsChildDepictionPresenter()
|
||||
: ChildDepictionsPresenter
|
||||
|
||||
@Binds
|
||||
abstract fun DepictedImagesPresenterImpl.bindsDepictedImagesContractPresenter()
|
||||
: DepictedImagesPresenter
|
||||
}
|
||||
|
|
@ -1,31 +1,21 @@
|
|||
package fr.free.nrw.commons.explore.depictions
|
||||
|
||||
import fr.free.nrw.commons.depictions.subClass.models.SparqlResponse
|
||||
import fr.free.nrw.commons.media.MediaInterface
|
||||
import fr.free.nrw.commons.mwapi.SparqlResponse
|
||||
import fr.free.nrw.commons.upload.depicts.DepictsInterface
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import fr.free.nrw.commons.utils.CommonsDateUtil
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import org.wikipedia.wikidata.Entities
|
||||
import java.math.BigInteger
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.text.ParseException
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
const val LARGE_IMAGE_SIZE = "640px"
|
||||
const val THUMB_IMAGE_SIZE = "70px"
|
||||
|
||||
/**
|
||||
* Depicts Client to handle custom calls to Commons Wikibase APIs
|
||||
*/
|
||||
@Singleton
|
||||
class DepictsClient @Inject constructor(
|
||||
private val depictsInterface: DepictsInterface,
|
||||
private val mediaInterface: MediaInterface
|
||||
private val depictsInterface: DepictsInterface
|
||||
) {
|
||||
|
||||
/**
|
||||
|
|
@ -40,10 +30,6 @@ class DepictsClient @Inject constructor(
|
|||
.map { it.entities().values.map(::DepictedItem) }
|
||||
}
|
||||
|
||||
private fun getUrl(title: String): String {
|
||||
return getImageUrl(title, LARGE_IMAGE_SIZE)
|
||||
}
|
||||
|
||||
fun getEntities(ids: String): Single<Entities> {
|
||||
return depictsInterface.getEntities(ids)
|
||||
}
|
||||
|
|
@ -57,63 +43,4 @@ class DepictsClient @Inject constructor(
|
|||
.flatMap { getEntities(it).toObservable() }
|
||||
.map { it.entities().values.map(::DepictedItem) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Get url for the image from media of depictions
|
||||
* Ex: Tiger_Woods
|
||||
* Value: https://upload.wikimedia.org/wikipedia/commons/thumb/6/67/Tiger_Woods.jpg/70px-Tiger_Woods.jpg
|
||||
*/
|
||||
fun getImageUrl(title: String, size: String): String {
|
||||
return title.substringAfter(":")
|
||||
.replace(" ", "_")
|
||||
.let {
|
||||
val MD5Hash = getMd5(it)
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/${MD5Hash[0]}/${MD5Hash[0]}${MD5Hash[1]}/$it/$size-$it"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates MD5 hash for the filename
|
||||
*/
|
||||
fun getMd5(input: String): String {
|
||||
return try {
|
||||
|
||||
// Static getInstance method is called with hashing MD5
|
||||
val md = MessageDigest.getInstance("MD5")
|
||||
|
||||
// digest() method is called to calculate message digest
|
||||
// of an input digest() return array of byte
|
||||
val messageDigest = md.digest(input.toByteArray())
|
||||
|
||||
// Convert byte array into signum representation
|
||||
val no = BigInteger(1, messageDigest)
|
||||
|
||||
// Convert message digest into hex value
|
||||
var hashtext = no.toString(16)
|
||||
while (hashtext.length < 32) {
|
||||
hashtext = "0$hashtext"
|
||||
}
|
||||
hashtext
|
||||
} // For specifying wrong message digest algorithms
|
||||
catch (e: NoSuchAlgorithmException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the date string into the required format
|
||||
* @param dateStr
|
||||
* @return date in the required format
|
||||
*/
|
||||
private fun safeParseDate(dateStr: String): Date? {
|
||||
return try {
|
||||
CommonsDateUtil.getIso8601DateFormatShort().parse(dateStr)
|
||||
} catch (e: ParseException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
package fr.free.nrw.commons.explore.depictions
|
||||
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.explore.paging.BasePagingFragment
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
|
||||
|
||||
abstract class PageableDepictionsFragment : BasePagingFragment<DepictedItem>() {
|
||||
override val errorTextId: Int = R.string.error_loading_depictions
|
||||
override val pagedListAdapter by lazy {
|
||||
DepictionAdapter { WikidataItemDetailsActivity.startYourself(context, it) }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
package fr.free.nrw.commons.explore.depictions
|
||||
|
||||
import androidx.paging.PositionalDataSource
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
data class SearchDepictionsDataSource constructor(
|
||||
private val depictsClient: DepictsClient,
|
||||
private val loadingStates: PublishProcessor<LoadingState>,
|
||||
private val query: String
|
||||
) : PositionalDataSource<DepictedItem>() {
|
||||
|
||||
private var lastExecutedRequest: (() -> Boolean)? = null
|
||||
|
||||
override fun loadInitial(
|
||||
params: LoadInitialParams,
|
||||
callback: LoadInitialCallback<DepictedItem>
|
||||
) {
|
||||
storeAndExecute {
|
||||
loadingStates.offer(LoadingState.InitialLoad)
|
||||
performWithTryCatch {
|
||||
callback.onResult(
|
||||
getItems(query, params.requestedLoadSize, params.requestedStartPosition),
|
||||
params.requestedStartPosition
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<DepictedItem>) {
|
||||
storeAndExecute {
|
||||
loadingStates.offer(LoadingState.Loading)
|
||||
performWithTryCatch {
|
||||
callback.onResult(getItems(query, params.loadSize, params.startPosition))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun retryFailedRequest() {
|
||||
Completable.fromAction { lastExecutedRequest?.invoke() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe()
|
||||
}
|
||||
|
||||
private fun getItems(query: String, limit: Int, offset: Int) =
|
||||
depictsClient.searchForDepictions(query, limit, offset).blockingGet()
|
||||
|
||||
private fun storeAndExecute(function: () -> Boolean) {
|
||||
function.also { lastExecutedRequest = it }.invoke()
|
||||
}
|
||||
|
||||
private fun performWithTryCatch(function: () -> Unit) = try {
|
||||
function.invoke()
|
||||
loadingStates.offer(LoadingState.Complete)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
loadingStates.offer(LoadingState.Error)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package fr.free.nrw.commons.explore.depictions
|
||||
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.depictions.WikidataItemDetailsActivity
|
||||
import fr.free.nrw.commons.explore.BasePagingFragment
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Display depictions in search fragment
|
||||
*/
|
||||
class SearchDepictionsFragment : BasePagingFragment<DepictedItem>(),
|
||||
SearchDepictionsFragmentContract.View {
|
||||
@Inject
|
||||
lateinit var presenter: SearchDepictionsFragmentContract.Presenter
|
||||
|
||||
override val errorTextId: Int = R.string.error_loading_depictions
|
||||
|
||||
override val injectedPresenter
|
||||
get() = presenter
|
||||
|
||||
override val pagedListAdapter by lazy {
|
||||
DepictionAdapter { WikidataItemDetailsActivity.startYourself(context, it) }
|
||||
}
|
||||
|
||||
override fun getEmptyText(query: String) = getString(R.string.depictions_not_found, query)
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package fr.free.nrw.commons.explore.depictions
|
||||
|
||||
import fr.free.nrw.commons.explore.PagingContract
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
|
||||
/**
|
||||
* The contract with with SearchDepictionsFragment and its presenter would talk to each other
|
||||
*/
|
||||
interface SearchDepictionsFragmentContract {
|
||||
interface View : PagingContract.View<DepictedItem>
|
||||
interface Presenter : PagingContract.Presenter<DepictedItem>
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
package fr.free.nrw.commons.explore.depictions
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.paging.Config
|
||||
import androidx.paging.DataSource
|
||||
import androidx.paging.PagedList
|
||||
import androidx.paging.toLiveData
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val PAGE_SIZE = 50
|
||||
private const val INITIAL_LOAD_SIZE = 50
|
||||
|
||||
class SearchableDepictionsDataSourceFactory @Inject constructor(
|
||||
val searchDepictionsDataSourceFactoryFactory: SearchDepictionsDataSourceFactoryFactory,
|
||||
val liveDataConverter: LiveDataConverter
|
||||
) {
|
||||
private val _loadingStates = PublishProcessor.create<LoadingState>()
|
||||
val loadingStates: Flowable<LoadingState> = _loadingStates
|
||||
private val _searchResults = PublishProcessor.create<LiveData<PagedList<DepictedItem>>>()
|
||||
val searchResults: Flowable<LiveData<PagedList<DepictedItem>>> = _searchResults
|
||||
private val _noItemsLoadedEvent = PublishProcessor.create<Unit>()
|
||||
val noItemsLoadedEvent: Flowable<Unit> = _noItemsLoadedEvent
|
||||
|
||||
private var currentFactory: SearchDepictionsDataSourceFactory? = null
|
||||
|
||||
fun onQueryUpdated(query: String) {
|
||||
_searchResults.offer(
|
||||
liveDataConverter.convert(
|
||||
searchDepictionsDataSourceFactoryFactory.create(query, _loadingStates)
|
||||
.also { currentFactory = it }
|
||||
) { _noItemsLoadedEvent.offer(Unit) }
|
||||
)
|
||||
}
|
||||
|
||||
fun retryFailedRequest() {
|
||||
currentFactory?.retryFailedRequest()
|
||||
}
|
||||
}
|
||||
|
||||
class LiveDataConverter @Inject constructor() {
|
||||
fun convert(
|
||||
dataSourceFactory: SearchDepictionsDataSourceFactory,
|
||||
zeroItemsLoadedFunction: () -> Unit
|
||||
): LiveData<PagedList<DepictedItem>> {
|
||||
return dataSourceFactory.toLiveData(
|
||||
Config(
|
||||
pageSize = PAGE_SIZE,
|
||||
initialLoadSizeHint = INITIAL_LOAD_SIZE,
|
||||
enablePlaceholders = false
|
||||
),
|
||||
boundaryCallback = object : PagedList.BoundaryCallback<DepictedItem>() {
|
||||
override fun onZeroItemsLoaded() {
|
||||
zeroItemsLoadedFunction()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface SearchDepictionsDataSourceFactoryFactory {
|
||||
fun create(query: String, loadingStates: PublishProcessor<LoadingState>)
|
||||
: SearchDepictionsDataSourceFactory
|
||||
}
|
||||
|
||||
class SearchDepictionsDataSourceFactory constructor(
|
||||
private val depictsClient: DepictsClient,
|
||||
private val query: String,
|
||||
private val loadingStates: PublishProcessor<LoadingState>
|
||||
) : DataSource.Factory<Int, DepictedItem>() {
|
||||
private var currentDataSource: SearchDepictionsDataSource? = null
|
||||
override fun create() = SearchDepictionsDataSource(depictsClient, loadingStates, query)
|
||||
.also { currentDataSource = it }
|
||||
|
||||
fun retryFailedRequest() {
|
||||
currentDataSource?.retryFailedRequest()
|
||||
}
|
||||
}
|
||||
|
||||
sealed class LoadingState {
|
||||
object InitialLoad : LoadingState()
|
||||
object Loading : LoadingState()
|
||||
object Complete : LoadingState()
|
||||
object Error : LoadingState()
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package fr.free.nrw.commons.depictions;
|
||||
package fr.free.nrw.commons.explore.depictions;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
|
@ -13,8 +13,9 @@ import butterknife.ButterKnife;
|
|||
import com.google.android.material.tabs.TabLayout;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.depictions.Media.DepictedImagesFragment;
|
||||
import fr.free.nrw.commons.depictions.subClass.SubDepictionListFragment;
|
||||
import fr.free.nrw.commons.explore.depictions.child.ChildDepictionsFragment;
|
||||
import fr.free.nrw.commons.explore.depictions.media.DepictedImagesFragment;
|
||||
import fr.free.nrw.commons.explore.depictions.parent.ParentDepictionsFragment;
|
||||
import fr.free.nrw.commons.explore.ViewPagerAdapter;
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
||||
|
|
@ -87,28 +88,23 @@ public class WikidataItemDetailsActivity extends NavigationBaseActivity implemen
|
|||
List<Fragment> fragmentList = new ArrayList<>();
|
||||
List<String> titleList = new ArrayList<>();
|
||||
depictionImagesListFragment = new DepictedImagesFragment();
|
||||
SubDepictionListFragment subDepictionListFragment = new SubDepictionListFragment();
|
||||
SubDepictionListFragment parentDepictionListFragment = new SubDepictionListFragment();
|
||||
ChildDepictionsFragment childDepictionsFragment = new ChildDepictionsFragment();
|
||||
ParentDepictionsFragment parentDepictionsFragment = new ParentDepictionsFragment();
|
||||
wikidataItemName = getIntent().getStringExtra("wikidataItemName");
|
||||
String entityId = getIntent().getStringExtra("entityId");
|
||||
if (getIntent() != null && wikidataItemName != null) {
|
||||
Bundle arguments = new Bundle();
|
||||
arguments.putString("wikidataItemName", wikidataItemName);
|
||||
arguments.putString("entityId", entityId);
|
||||
arguments.putBoolean("isParentClass", false);
|
||||
depictionImagesListFragment.setArguments(arguments);
|
||||
subDepictionListFragment.setArguments(arguments);
|
||||
Bundle parentClassArguments = new Bundle();
|
||||
parentClassArguments.putString("wikidataItemName", wikidataItemName);
|
||||
parentClassArguments.putString("entityId", entityId);
|
||||
parentClassArguments.putBoolean("isParentClass", true);
|
||||
parentDepictionListFragment.setArguments(parentClassArguments);
|
||||
parentDepictionsFragment.setArguments(arguments);
|
||||
childDepictionsFragment.setArguments(arguments);
|
||||
}
|
||||
fragmentList.add(depictionImagesListFragment);
|
||||
titleList.add(getResources().getString(R.string.title_for_media));
|
||||
fragmentList.add(subDepictionListFragment);
|
||||
fragmentList.add(childDepictionsFragment);
|
||||
titleList.add(getResources().getString(R.string.title_for_child_classes));
|
||||
fragmentList.add(parentDepictionListFragment);
|
||||
fragmentList.add(parentDepictionsFragment);
|
||||
titleList.add(getResources().getString(R.string.title_for_parent_classes));
|
||||
viewPagerAdapter.setTabData(fragmentList, titleList);
|
||||
viewPager.setOffscreenPageLimit(2);
|
||||
|
|
@ -183,7 +179,6 @@ public class WikidataItemDetailsActivity extends NavigationBaseActivity implemen
|
|||
*/
|
||||
public static void startYourself(Context context, DepictedItem depictedItem) {
|
||||
Intent intent = new Intent(context, WikidataItemDetailsActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
intent.putExtra("wikidataItemName", depictedItem.getName());
|
||||
intent.putExtra("entityId", depictedItem.getId());
|
||||
context.startActivity(intent);
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package fr.free.nrw.commons.explore.depictions.child
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.explore.depictions.PageableDepictionsFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class ChildDepictionsFragment: PageableDepictionsFragment() {
|
||||
@Inject
|
||||
lateinit var presenter: ChildDepictionsPresenter
|
||||
|
||||
override val injectedPresenter
|
||||
get() = presenter
|
||||
|
||||
override fun getEmptyText(query: String) =
|
||||
getString(R.string.no_child_classes, arguments!!.getString("wikidataItemName")!!)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
onQueryUpdated(arguments!!.getString("entityId")!!)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package fr.free.nrw.commons.explore.depictions.child
|
||||
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.explore.paging.BasePagingPresenter
|
||||
import fr.free.nrw.commons.explore.paging.PagingContract
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import io.reactivex.Scheduler
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
interface ChildDepictionsPresenter : PagingContract.Presenter<DepictedItem>
|
||||
|
||||
class ChildDepictionsPresenterImpl @Inject constructor(
|
||||
@Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler,
|
||||
dataSourceFactory: PageableChildDepictionsDataSource
|
||||
) : BasePagingPresenter<DepictedItem>(mainThreadScheduler, dataSourceFactory),
|
||||
ChildDepictionsPresenter
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package fr.free.nrw.commons.explore.depictions.child
|
||||
|
||||
import fr.free.nrw.commons.explore.paging.LiveDataConverter
|
||||
import fr.free.nrw.commons.explore.paging.PageableBaseDataSource
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class PageableChildDepictionsDataSource @Inject constructor(
|
||||
liveDataConverter: LiveDataConverter,
|
||||
private val okHttpJsonApiClient: OkHttpJsonApiClient
|
||||
) : PageableBaseDataSource<DepictedItem>(liveDataConverter) {
|
||||
override val loadFunction = { limit: Int, startPosition: Int ->
|
||||
okHttpJsonApiClient.getChildDepictions(query, startPosition, limit).blockingFirst()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
package fr.free.nrw.commons.depictions.Media
|
||||
package fr.free.nrw.commons.explore.depictions.media
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import fr.free.nrw.commons.depictions.WikidataItemDetailsActivity
|
||||
import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity
|
||||
import fr.free.nrw.commons.explore.media.PageableMediaFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
class DepictedImagesFragment : PageableMediaFragment(), DepictedImagesContract.View {
|
||||
class DepictedImagesFragment : PageableMediaFragment() {
|
||||
@Inject
|
||||
lateinit var presenter: DepictedImagesContract.Presenter
|
||||
lateinit var presenter: DepictedImagesPresenter
|
||||
|
||||
override val injectedPresenter
|
||||
get() = presenter
|
||||
|
|
@ -1,17 +1,20 @@
|
|||
package fr.free.nrw.commons.depictions.Media
|
||||
package fr.free.nrw.commons.explore.depictions.media
|
||||
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.explore.BasePagingPresenter
|
||||
import fr.free.nrw.commons.explore.paging.BasePagingPresenter
|
||||
import fr.free.nrw.commons.explore.paging.PagingContract
|
||||
import io.reactivex.Scheduler
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
interface DepictedImagesPresenter : PagingContract.Presenter<Media>
|
||||
|
||||
/**
|
||||
* Presenter for DepictedImagesFragment
|
||||
*/
|
||||
class DepictedImagesPresenter @Inject constructor(
|
||||
class DepictedImagesPresenterImpl @Inject constructor(
|
||||
@Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler,
|
||||
dataSourceFactory: PageableDepictedMediaDataSource
|
||||
) : BasePagingPresenter<Media>(mainThreadScheduler, dataSourceFactory),
|
||||
DepictedImagesContract.Presenter
|
||||
DepictedImagesPresenter
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
package fr.free.nrw.commons.depictions.Media
|
||||
package fr.free.nrw.commons.explore.depictions.media
|
||||
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.explore.LiveDataConverter
|
||||
import fr.free.nrw.commons.explore.PageableBaseDataSource
|
||||
import fr.free.nrw.commons.explore.depictions.LoadFunction
|
||||
import fr.free.nrw.commons.explore.paging.LiveDataConverter
|
||||
import fr.free.nrw.commons.explore.paging.PageableBaseDataSource
|
||||
import fr.free.nrw.commons.explore.depictions.search.LoadFunction
|
||||
import fr.free.nrw.commons.media.MediaClient
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package fr.free.nrw.commons.explore.depictions.parent
|
||||
|
||||
import fr.free.nrw.commons.explore.paging.LiveDataConverter
|
||||
import fr.free.nrw.commons.explore.paging.PageableBaseDataSource
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class PageableParentDepictionsDataSource @Inject constructor(
|
||||
liveDataConverter: LiveDataConverter,
|
||||
private val okHttpJsonApiClient: OkHttpJsonApiClient
|
||||
) : PageableBaseDataSource<DepictedItem>(liveDataConverter) {
|
||||
override val loadFunction = { limit: Int, startPosition: Int ->
|
||||
okHttpJsonApiClient.getParentDepictions(query, startPosition, limit).blockingFirst()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package fr.free.nrw.commons.explore.depictions.parent
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.explore.depictions.PageableDepictionsFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class ParentDepictionsFragment : PageableDepictionsFragment() {
|
||||
@Inject
|
||||
lateinit var presenter: ParentDepictionsPresenter
|
||||
|
||||
override val injectedPresenter
|
||||
get() = presenter
|
||||
|
||||
override fun getEmptyText(query: String) =
|
||||
getString(R.string.no_parent_classes, arguments!!.getString("wikidataItemName")!!)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
onQueryUpdated(arguments!!.getString("entityId")!!)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package fr.free.nrw.commons.explore.depictions.parent
|
||||
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.explore.paging.BasePagingPresenter
|
||||
import fr.free.nrw.commons.explore.paging.PagingContract
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import io.reactivex.Scheduler
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
interface ParentDepictionsPresenter : PagingContract.Presenter<DepictedItem>
|
||||
|
||||
class ParentDepictionsPresenterImpl @Inject constructor(
|
||||
@Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler,
|
||||
dataSourceFactory: PageableParentDepictionsDataSource
|
||||
) : BasePagingPresenter<DepictedItem>(mainThreadScheduler, dataSourceFactory),
|
||||
ParentDepictionsPresenter {
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
package fr.free.nrw.commons.explore.depictions
|
||||
package fr.free.nrw.commons.explore.depictions.search
|
||||
|
||||
import fr.free.nrw.commons.explore.LiveDataConverter
|
||||
import fr.free.nrw.commons.explore.LoadingState
|
||||
import fr.free.nrw.commons.explore.PageableBaseDataSource
|
||||
import fr.free.nrw.commons.explore.paging.LiveDataConverter
|
||||
import fr.free.nrw.commons.explore.paging.LoadingState
|
||||
import fr.free.nrw.commons.explore.paging.PageableBaseDataSource
|
||||
import fr.free.nrw.commons.explore.depictions.DepictsClient
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import javax.inject.Inject
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package fr.free.nrw.commons.explore.depictions.search
|
||||
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.explore.depictions.PageableDepictionsFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Display depictions in search fragment
|
||||
*/
|
||||
class SearchDepictionsFragment : PageableDepictionsFragment() {
|
||||
@Inject
|
||||
lateinit var presenter: SearchDepictionsFragmentPresenter
|
||||
|
||||
override val injectedPresenter
|
||||
get() = presenter
|
||||
|
||||
override fun getEmptyText(query: String) = getString(R.string.depictions_not_found, query)
|
||||
}
|
||||
|
|
@ -1,17 +1,19 @@
|
|||
package fr.free.nrw.commons.explore.depictions
|
||||
package fr.free.nrw.commons.explore.depictions.search
|
||||
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.explore.BasePagingPresenter
|
||||
import fr.free.nrw.commons.explore.paging.BasePagingPresenter
|
||||
import fr.free.nrw.commons.explore.paging.PagingContract
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import io.reactivex.Scheduler
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
interface SearchDepictionsFragmentPresenter : PagingContract.Presenter<DepictedItem>
|
||||
/**
|
||||
* The presenter class for SearchDepictionsFragment
|
||||
*/
|
||||
class SearchDepictionsFragmentPresenter @Inject constructor(
|
||||
class SearchDepictionsFragmentPresenterImpl @Inject constructor(
|
||||
@Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler,
|
||||
dataSourceFactory: PageableDepictionsDataSource
|
||||
) : BasePagingPresenter<DepictedItem>(mainThreadScheduler, dataSourceFactory),
|
||||
SearchDepictionsFragmentContract.Presenter
|
||||
SearchDepictionsFragmentPresenter
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
package fr.free.nrw.commons.explore.media
|
||||
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.explore.LiveDataConverter
|
||||
import fr.free.nrw.commons.explore.PageableBaseDataSource
|
||||
import fr.free.nrw.commons.explore.depictions.LoadFunction
|
||||
import fr.free.nrw.commons.explore.paging.LiveDataConverter
|
||||
import fr.free.nrw.commons.explore.paging.PageableBaseDataSource
|
||||
import fr.free.nrw.commons.explore.depictions.search.LoadFunction
|
||||
import fr.free.nrw.commons.media.MediaClient
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import android.os.Bundle
|
|||
import android.view.View
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.explore.BasePagingFragment
|
||||
import fr.free.nrw.commons.explore.paging.BasePagingFragment
|
||||
import kotlinx.android.synthetic.main.fragment_search_paginated.*
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import androidx.paging.PagedListAdapter
|
|||
import androidx.recyclerview.widget.DiffUtil
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.explore.BaseViewHolder
|
||||
import fr.free.nrw.commons.explore.inflate
|
||||
import fr.free.nrw.commons.explore.paging.BaseViewHolder
|
||||
import fr.free.nrw.commons.explore.paging.inflate
|
||||
import kotlinx.android.synthetic.main.layout_category_images.*
|
||||
|
||||
class PagedMediaAdapter(private val onImageClicked: (Int) -> Unit) :
|
||||
|
|
|
|||
|
|
@ -7,15 +7,15 @@ import javax.inject.Inject
|
|||
/**
|
||||
* Displays the image search screen.
|
||||
*/
|
||||
class SearchMediaFragment : PageableMediaFragment(), SearchMediaFragmentContract.View {
|
||||
class SearchMediaFragment : PageableMediaFragment(){
|
||||
@Inject
|
||||
lateinit var presenter: SearchMediaFragmentContract.Presenter
|
||||
lateinit var presenter: SearchMediaFragmentPresenter
|
||||
|
||||
override val injectedPresenter
|
||||
get() = presenter
|
||||
|
||||
override fun onItemClicked(position: Int) {
|
||||
(context as SearchActivity?)!!.onSearchImageClicked(position)
|
||||
(context as SearchActivity).onSearchImageClicked(position)
|
||||
}
|
||||
|
||||
override fun notifyViewPager() {
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
package fr.free.nrw.commons.explore.media
|
||||
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.explore.PagingContract
|
||||
|
||||
|
||||
interface SearchMediaFragmentContract {
|
||||
interface View : PagingContract.View<Media>
|
||||
interface Presenter : PagingContract.Presenter<Media>
|
||||
}
|
||||
|
|
@ -2,13 +2,16 @@ package fr.free.nrw.commons.explore.media
|
|||
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.explore.BasePagingPresenter
|
||||
import fr.free.nrw.commons.explore.paging.BasePagingPresenter
|
||||
import fr.free.nrw.commons.explore.paging.PagingContract
|
||||
import io.reactivex.Scheduler
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
class SearchMediaFragmentPresenter @Inject constructor(
|
||||
interface SearchMediaFragmentPresenter : PagingContract.Presenter<Media>
|
||||
|
||||
class SearchMediaFragmentPresenterImpl @Inject constructor(
|
||||
@Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler,
|
||||
dataSourceFactory: PageableMediaDataSource
|
||||
) : BasePagingPresenter<Media>(mainThreadScheduler, dataSourceFactory),
|
||||
SearchMediaFragmentContract.Presenter
|
||||
SearchMediaFragmentPresenter
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package fr.free.nrw.commons.explore
|
||||
package fr.free.nrw.commons.explore.paging
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package fr.free.nrw.commons.explore
|
||||
package fr.free.nrw.commons.explore.paging
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import fr.free.nrw.commons.upload.depicts.proxy
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package fr.free.nrw.commons.explore
|
||||
package fr.free.nrw.commons.explore.paging
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package fr.free.nrw.commons.explore.depictions
|
||||
package fr.free.nrw.commons.explore.paging
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
|
|
@ -26,7 +26,9 @@ class FooterAdapter(private val onRefreshClicked: () -> Unit) :
|
|||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
when (FooterItem.values()[viewType]) {
|
||||
FooterItem.LoadingItem -> LoadingViewHolder(parent.inflate(R.layout.list_item_progress))
|
||||
FooterItem.LoadingItem -> LoadingViewHolder(
|
||||
parent.inflate(R.layout.list_item_progress)
|
||||
)
|
||||
FooterItem.RefreshItem -> RefreshViewHolder(
|
||||
parent.inflate(R.layout.list_item_load_more),
|
||||
onRefreshClicked
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package fr.free.nrw.commons.explore
|
||||
package fr.free.nrw.commons.explore.paging
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.paging.PagedList
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
package fr.free.nrw.commons.explore
|
||||
package fr.free.nrw.commons.explore.paging
|
||||
|
||||
import androidx.paging.PositionalDataSource
|
||||
import fr.free.nrw.commons.explore.depictions.LoadFunction
|
||||
import fr.free.nrw.commons.explore.depictions.search.LoadFunction
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
|
||||
abstract class SearchDataSource<T>(
|
||||
abstract class PagingDataSource<T>(
|
||||
private val loadingStates: PublishProcessor<LoadingState>
|
||||
) : PositionalDataSource<T>() {
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ abstract class SearchDataSource<T>(
|
|||
fun <T> dataSource(
|
||||
loadingStates: PublishProcessor<LoadingState>,
|
||||
loadFunction: LoadFunction<T>
|
||||
) = object : SearchDataSource<T>(loadingStates) {
|
||||
) = object : PagingDataSource<T>(loadingStates) {
|
||||
override fun getItems(loadSize: Int, startPosition: Int): List<T> {
|
||||
return loadFunction(loadSize, startPosition)
|
||||
}
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
package fr.free.nrw.commons.explore
|
||||
package fr.free.nrw.commons.explore.paging
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.paging.Config
|
||||
import androidx.paging.DataSource
|
||||
import androidx.paging.PagedList
|
||||
import androidx.paging.toLiveData
|
||||
import fr.free.nrw.commons.explore.depictions.LoadFunction
|
||||
import fr.free.nrw.commons.explore.depictions.LoadingStates
|
||||
import fr.free.nrw.commons.explore.depictions.search.LoadFunction
|
||||
import fr.free.nrw.commons.explore.depictions.search.LoadingStates
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import javax.inject.Inject
|
||||
|
|
@ -18,7 +18,10 @@ abstract class PageableBaseDataSource<T>(private val liveDataConverter: LiveData
|
|||
|
||||
lateinit var query: String
|
||||
private val dataSourceFactoryFactory: () -> PagingDataSourceFactory<T> = {
|
||||
dataSourceFactory(_loadingStates, loadFunction)
|
||||
dataSourceFactory(
|
||||
_loadingStates,
|
||||
loadFunction
|
||||
)
|
||||
}
|
||||
private val _loadingStates = PublishProcessor.create<LoadingState>()
|
||||
val loadingStates: Flowable<LoadingState> = _loadingStates
|
||||
|
|
@ -67,11 +70,14 @@ class LiveDataConverter @Inject constructor() {
|
|||
|
||||
abstract class PagingDataSourceFactory<T>(val loadingStates: LoadingStates) :
|
||||
DataSource.Factory<Int, T>() {
|
||||
private var currentDataSource: SearchDataSource<T>? = null
|
||||
private var currentDataSource: PagingDataSource<T>? = null
|
||||
abstract val loadFunction: LoadFunction<T>
|
||||
|
||||
override fun create() =
|
||||
dataSource(loadingStates, loadFunction).also { currentDataSource = it }
|
||||
dataSource(
|
||||
loadingStates,
|
||||
loadFunction
|
||||
).also { currentDataSource = it }
|
||||
|
||||
fun retryFailedRequest() {
|
||||
currentDataSource?.retryFailedRequest()
|
||||
|
|
@ -48,7 +48,7 @@ import fr.free.nrw.commons.category.CategoryDetailsActivity;
|
|||
import fr.free.nrw.commons.contributions.ContributionsFragment;
|
||||
import fr.free.nrw.commons.delete.DeleteHelper;
|
||||
import fr.free.nrw.commons.delete.ReasonBuilder;
|
||||
import fr.free.nrw.commons.depictions.WikidataItemDetailsActivity;
|
||||
import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||
import fr.free.nrw.commons.ui.widget.HtmlTextView;
|
||||
import fr.free.nrw.commons.utils.ViewUtilWrapper;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import com.google.gson.Gson;
|
|||
import fr.free.nrw.commons.achievements.FeaturedImages;
|
||||
import fr.free.nrw.commons.achievements.FeedbackResponse;
|
||||
import fr.free.nrw.commons.campaigns.CampaignResponseDTO;
|
||||
import fr.free.nrw.commons.depictions.subClass.models.SparqlResponse;
|
||||
import fr.free.nrw.commons.explore.depictions.DepictsClient;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.nearby.Place;
|
||||
|
|
@ -210,16 +209,19 @@ public class OkHttpJsonApiClient {
|
|||
* Get the QIDs of all Wikidata items that are subclasses of the given Wikidata item. Example:
|
||||
* bridge -> suspended bridge, aqueduct, etc
|
||||
*/
|
||||
public Observable<List<DepictedItem>> getChildQIDs(String qid) throws IOException {
|
||||
return depictedItemsFrom(sparqlQuery(qid, "/queries/subclasses_query.rq"));
|
||||
public Observable<List<DepictedItem>> getChildDepictions(String qid, int startPosition,
|
||||
int limit) throws IOException {
|
||||
return depictedItemsFrom(sparqlQuery(qid, startPosition, limit,"/queries/subclasses_query.rq"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the QIDs of all Wikidata items that are subclasses of the given Wikidata item. Example:
|
||||
* bridge -> suspended bridge, aqueduct, etc
|
||||
*/
|
||||
public Observable<List<DepictedItem>> getParentQIDs(String qid) throws IOException {
|
||||
return depictedItemsFrom(sparqlQuery(qid, "/queries/parentclasses_query.rq"));
|
||||
public Observable<List<DepictedItem>> getParentDepictions(String qid, int startPosition,
|
||||
int limit) throws IOException {
|
||||
return depictedItemsFrom(sparqlQuery(qid, startPosition, limit,
|
||||
"/queries/parentclasses_query.rq"));
|
||||
}
|
||||
|
||||
private Observable<List<DepictedItem>> depictedItemsFrom(Request request) {
|
||||
|
|
@ -231,10 +233,12 @@ public class OkHttpJsonApiClient {
|
|||
}
|
||||
|
||||
@NotNull
|
||||
private Request sparqlQuery(String qid, String fileName) throws IOException {
|
||||
String query = FileUtils.readFromResource(fileName).
|
||||
replace("${QID}", qid)
|
||||
.replace("${LANG}", "\"" + Locale.getDefault().getLanguage() + "\"");
|
||||
private Request sparqlQuery(String qid, int startPosition, int limit, String fileName) throws IOException {
|
||||
String query = FileUtils.readFromResource(fileName)
|
||||
.replace("${QID}", qid)
|
||||
.replace("${LANG}", "\"" + Locale.getDefault().getLanguage() + "\"")
|
||||
.replace("${LIMIT}",""+ limit)
|
||||
.replace("${OFFSET}",""+ startPosition);
|
||||
HttpUrl.Builder urlBuilder = HttpUrl
|
||||
.parse(sparqlQueryUrl)
|
||||
.newBuilder()
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
package fr.free.nrw.commons.depictions.subClass.models
|
||||
package fr.free.nrw.commons.mwapi
|
||||
|
||||
data class SparqlResponse(val results: Result)
|
||||
|
||||
data class Result(val bindings: List<Binding>)
|
||||
|
||||
data class Binding(
|
||||
val item: SparqInfo,
|
||||
val itemLabel: SparqInfo,
|
||||
val itemDescription: SparqInfo? = null
|
||||
val item: SparqInfo
|
||||
) {
|
||||
val id: String
|
||||
get() = item.value.substringAfterLast("/")
|
||||
|
|
@ -42,7 +42,7 @@ import fr.free.nrw.commons.auth.LoginActivity;
|
|||
import fr.free.nrw.commons.auth.LogoutClient;
|
||||
import fr.free.nrw.commons.bookmarks.BookmarksActivity;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.explore.categories.ExploreActivity;
|
||||
import fr.free.nrw.commons.explore.ExploreActivity;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.logging.CommonsLogSender;
|
||||
import fr.free.nrw.commons.review.ReviewActivity;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package fr.free.nrw.commons.upload.structure.depictions
|
||||
|
||||
import fr.free.nrw.commons.explore.depictions.DepictsClient.Companion.getImageUrl
|
||||
import fr.free.nrw.commons.explore.depictions.THUMB_IMAGE_SIZE
|
||||
import fr.free.nrw.commons.nearby.Place
|
||||
import fr.free.nrw.commons.upload.WikidataItem
|
||||
import fr.free.nrw.commons.wikidata.WikidataProperties
|
||||
|
|
@ -9,8 +7,13 @@ import fr.free.nrw.commons.wikidata.WikidataProperties.*
|
|||
import org.wikipedia.wikidata.DataValue
|
||||
import org.wikipedia.wikidata.Entities
|
||||
import org.wikipedia.wikidata.Statement_partial
|
||||
import java.math.BigInteger
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.util.*
|
||||
|
||||
const val THUMB_IMAGE_SIZE = "70px"
|
||||
|
||||
/**
|
||||
* Model class for Depicted Item in Upload and Explore
|
||||
*/
|
||||
|
|
@ -76,3 +79,40 @@ operator fun Entities.Entity.get(property: WikidataProperties) =
|
|||
|
||||
private fun Map<String, Entities.Label>.byLanguageOrFirstOrEmpty() =
|
||||
let { it[Locale.getDefault().language] ?: it.values.firstOrNull() }?.value() ?: ""
|
||||
|
||||
private fun getImageUrl(title: String, size: String): String {
|
||||
return title.substringAfter(":")
|
||||
.replace(" ", "_")
|
||||
.let {
|
||||
val MD5Hash = getMd5(it)
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/${MD5Hash[0]}/${MD5Hash[0]}${MD5Hash[1]}/$it/$size-$it"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates MD5 hash for the filename
|
||||
*/
|
||||
private fun getMd5(input: String): String {
|
||||
return try {
|
||||
|
||||
// Static getInstance method is called with hashing MD5
|
||||
val md = MessageDigest.getInstance("MD5")
|
||||
|
||||
// digest() method is called to calculate message digest
|
||||
// of an input digest() return array of byte
|
||||
val messageDigest = md.digest(input.toByteArray())
|
||||
|
||||
// Convert byte array into signum representation
|
||||
val no = BigInteger(1, messageDigest)
|
||||
|
||||
// Convert message digest into hex value
|
||||
var hashtext = no.toString(16)
|
||||
while (hashtext.length < 32) {
|
||||
hashtext = "0$hashtext"
|
||||
}
|
||||
hashtext
|
||||
} // For specifying wrong message digest algorithms
|
||||
catch (e: NoSuchAlgorithmException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue