Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class CategoriesModel{
private static final int SEARCH_CATS_LIMIT = 25;

private final MediaWikiApi mwApi;
private final CategoryClient categoryClient;
private final CategoryDao categoryDao;
private final JsonKvStore directKvStore;

Expand All @@ -32,9 +33,11 @@ public class CategoriesModel{
@Inject GpsCategoryModel gpsCategoryModel;
@Inject
public CategoriesModel(MediaWikiApi mwApi,
CategoryClient categoryClient,
CategoryDao categoryDao,
@Named("default_preferences") JsonKvStore directKvStore) {
this.mwApi = mwApi;
this.categoryClient = categoryClient;
this.categoryDao = categoryDao;
this.directKvStore = directKvStore;
this.categoriesCache = new HashMap<>();
Expand Down Expand Up @@ -121,8 +124,8 @@ public Observable<CategoryItem> searchAll(String term, List<String> imageTitleLi
}

//otherwise, search API for matching categories
return mwApi
.allCategories(term, SEARCH_CATS_LIMIT)
return categoryClient
.searchCategoriesForPrefix(term, SEARCH_CATS_LIMIT)
.map(name -> new CategoryItem(name, false));
}

Expand Down Expand Up @@ -185,7 +188,7 @@ private Observable<CategoryItem> titleCategories(List<String> titleList) {
* @return
*/
private Observable<CategoryItem> getTitleCategories(String title) {
return mwApi.searchTitles(title, SEARCH_CATS_LIMIT)
return categoryClient.searchCategories(title, SEARCH_CATS_LIMIT)
.map(name -> new CategoryItem(name, false));
}

Expand Down
97 changes: 97 additions & 0 deletions app/src/main/java/fr/free/nrw/commons/category/CategoryClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package fr.free.nrw.commons.category;


import org.wikipedia.dataclient.mwapi.MwQueryPage;
import org.wikipedia.dataclient.mwapi.MwQueryResponse;

import java.util.List;

import javax.inject.Inject;
import javax.inject.Singleton;

import io.reactivex.Observable;
import timber.log.Timber;

/**
* Category Client to handle custom calls to Commons MediaWiki APIs
*/
@Singleton
public class CategoryClient {

private final CategoryInterface CategoryInterface;

@Inject
public CategoryClient(CategoryInterface CategoryInterface) {
this.CategoryInterface = CategoryInterface;
}

/**
* Searches for categories containing the specified string.
*
* @param filter The string to be searched
* @param itemLimit How many results are returned
* @param offset Starts returning items from the nth result. If offset is 9, the response starts with the 9th item of the search result
* @return
*/
public Observable<String> searchCategories(String filter, int itemLimit, int offset) {
return responseToCategoryName(CategoryInterface.searchCategories(filter, itemLimit, offset));

}

/**
* Searches for categories containing the specified string.
*
* @param filter The string to be searched
* @param itemLimit How many results are returned
* @return
*/
public Observable<String> searchCategories(String filter, int itemLimit) {
return searchCategories(filter, itemLimit, 0);

}

/**
* Searches for categories starting with the specified string.
*
* @param prefix The prefix to be searched
* @param itemLimit How many results are returned
* @param offset Starts returning items from the nth result. If offset is 9, the response starts with the 9th item of the search result
* @return
*/
public Observable<String> searchCategoriesForPrefix(String prefix, int itemLimit, int offset) {
return responseToCategoryName(CategoryInterface.searchCategoriesForPrefix(prefix, itemLimit, offset));
}

/**
* Searches for categories starting with the specified string.
*
* @param prefix The prefix to be searched
* @param itemLimit How many results are returned
* @return
*/
public Observable<String> searchCategoriesForPrefix(String prefix, int itemLimit) {
return searchCategoriesForPrefix(prefix, itemLimit, 0);
}


/**
* Internal function to reduce code reuse. Extracts the categories returned from MwQueryResponse.
*
* @param responseObservable The query response observable
* @return Observable emitting the categories returned. If our search yielded "Category:Test", "Test" is emitted.
*/
private Observable<String> responseToCategoryName(Observable<MwQueryResponse> responseObservable) {
return responseObservable
.flatMap(mwQueryResponse -> {
List<MwQueryPage> pages = mwQueryResponse.query().pages();
if (pages != null)
return Observable.fromIterable(pages);
else
Timber.d("No categories returned.");
return Observable.empty();
})
.map(MwQueryPage::title)
.doOnEach(s -> Timber.d("Category returned: %s", s))
.map(cat -> cat.replace("Category:", ""));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package fr.free.nrw.commons.category;

import org.wikipedia.dataclient.mwapi.MwQueryResponse;

import io.reactivex.Observable;
import retrofit2.http.GET;
import retrofit2.http.Query;

/**
* Interface for interacting with Commons category related APIs
*/
public interface CategoryInterface {

/**
* Searches for categories with the specified name.
* Replaces ApacheHttpClientMediaWikiApi#allCategories
*
* @param filter The string to be searched
* @param itemLimit How many results are returned
* @return
*/
@GET("w/api.php?action=query&format=json&formatversion=2"
+ "&generator=search&gsrnamespace=14")
Observable<MwQueryResponse> searchCategories(@Query("gsrsearch") String filter,
@Query("gsrlimit") int itemLimit, @Query("gsroffset") int offset);

@GET("w/api.php?action=query&format=json&formatversion=2"
+ "&generator=allcategories")
Observable<MwQueryResponse> searchCategoriesForPrefix(@Query("gacprefix") String prefix,
@Query("gaclimit") int itemLimit, @Query("gacoffset") int offset);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import dagger.Module;
import dagger.Provides;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.category.CategoryInterface;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.media.MediaInterface;
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
Expand Down Expand Up @@ -132,4 +133,10 @@ public ReviewInterface provideReviewInterface(@Named(NAMED_COMMONS_WIKI_SITE) Wi
public MediaInterface provideMediaInterface(@Named(NAMED_COMMONS_WIKI_SITE) WikiSite commonsWikiSite) {
return ServiceFactory.get(commonsWikiSite, BuildConfig.COMMONS_URL, MediaInterface.class);
}

@Provides
@Singleton
public CategoryInterface provideCategoryInterface(@Named(NAMED_COMMONS_WIKI_SITE) WikiSite commonsWikiSite) {
return ServiceFactory.get(commonsWikiSite, BuildConfig.COMMONS_URL, CategoryInterface.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.category.CategoryClient;
import fr.free.nrw.commons.category.CategoryDetailsActivity;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.explore.recentsearches.RecentSearch;
Expand Down Expand Up @@ -58,9 +59,12 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment {
String query;
@BindView(R.id.bottomProgressBar)
ProgressBar bottomProgressBar;
boolean isLoadingCategories;

@Inject RecentSearchesDao recentSearchesDao;
@Inject MediaWikiApi mwApi;
@Inject CategoryClient categoryClient;

@Inject
@Named("default_preferences")
JsonKvStore basicKvStore;
Expand Down Expand Up @@ -135,48 +139,51 @@ public void updateCategoryList(String query) {
progressBar.setVisibility(GONE);
queryList.clear();
categoriesAdapter.clear();
compositeDisposable.add(Observable.fromCallable(() -> mwApi.searchCategory(query,queryList.size()))
compositeDisposable.add(categoryClient.searchCategories(query,25)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
.doOnSubscribe(disposable -> saveQuery(query))
.collect(ArrayList<String>::new, ArrayList::add)
.subscribe(this::handleSuccess, this::handleError));
}


/**
* Adds more results to existing search results
* Adds 25 more results to existing search results
*/
public void addCategoriesToList(String query) {
if(isLoadingCategories) return;
isLoadingCategories=true;
this.query = query;
bottomProgressBar.setVisibility(View.VISIBLE);
progressBar.setVisibility(GONE);
compositeDisposable.add(Observable.fromCallable(() -> mwApi.searchCategory(query,queryList.size()))
compositeDisposable.add(categoryClient.searchCategories(query,25, queryList.size())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
.collect(ArrayList<String>::new, ArrayList::add)
.subscribe(this::handlePaginationSuccess, this::handleError));
}

/**
* Handles the success scenario
* it initializes the recycler view by adding items to the adapter
* @param mediaList
*/
private void handlePaginationSuccess(List<String> mediaList) {
queryList.addAll(mediaList);
progressBar.setVisibility(View.GONE);
bottomProgressBar.setVisibility(GONE);
categoriesAdapter.addAll(mediaList);
categoriesAdapter.notifyDataSetChanged();
isLoadingCategories=false;
}



/**
* Handles the success scenario
* it initializes the recycler view by adding items to the adapter
* @param mediaList
*/
private void handleSuccess(List<String> mediaList) {
queryList = mediaList;
Expand All @@ -194,7 +201,6 @@ private void handleSuccess(List<String> mediaList) {

/**
* Logs and handles API error scenario
* @param throwable
*/
private void handleError(Throwable throwable) {
Timber.e(throwable, "Error occurred while loading queried categories");
Expand All @@ -213,7 +219,7 @@ private void handleError(Throwable throwable) {
private void initErrorView() {
progressBar.setVisibility(GONE);
categoriesNotFoundView.setVisibility(VISIBLE);
categoriesNotFoundView.setText(getString(R.string.categories_not_found, query));
categoriesNotFoundView.setText(getString(R.string.categories_not_found));
}

/**
Expand Down
Loading