mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-30 22:34:02 +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
	
	 Seán Mac Gillicuddy
						Seán Mac Gillicuddy