mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
#3822 Convert SubCategoryImagesListFragment to use Pagination - convert subcategories - add continuation support in category client - rely on interfaces for callbacks of PageableMediaFragments
This commit is contained in:
parent
0dad78358d
commit
5b87ed569c
26 changed files with 253 additions and 92 deletions
|
|
@ -142,4 +142,9 @@ public class BookmarksActivity extends NavigationBaseActivity
|
|||
|
||||
@Override
|
||||
public void requestMoreImages() { }
|
||||
|
||||
@Override
|
||||
public void onMediaClicked(int position) {
|
||||
//TODO use with pagination
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ class CategoriesModel @Inject constructor(
|
|||
else
|
||||
categoryClient.searchCategoriesForPrefix(term.toLowerCase(), SEARCH_CATS_LIMIT)
|
||||
.map { it.sortedWith(StringSortingUtils.sortBySimilarity(term)) }
|
||||
.toObservable()
|
||||
}
|
||||
|
||||
private fun categoriesFromDepiction(selectedDepictions: List<DepictedItem>) =
|
||||
|
|
@ -126,7 +127,7 @@ class CategoriesModel @Inject constructor(
|
|||
* @return
|
||||
*/
|
||||
private fun getTitleCategories(title: String): Observable<List<String>> {
|
||||
return categoryClient.searchCategories(title.toLowerCase(), SEARCH_CATS_LIMIT)
|
||||
return categoryClient.searchCategories(title.toLowerCase(), SEARCH_CATS_LIMIT).toObservable()
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,22 @@
|
|||
package fr.free.nrw.commons.category
|
||||
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryResponse
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
const val CATEGORY_PREFIX = "Category:"
|
||||
const val SUB_CATEGORY_CONTINUATION_PREFIX = "sub_category_"
|
||||
|
||||
/**
|
||||
* Category Client to handle custom calls to Commons MediaWiki APIs
|
||||
*/
|
||||
@Singleton
|
||||
class CategoryClient @Inject constructor(private val categoryInterface: CategoryInterface) {
|
||||
|
||||
private val continuationStore: MutableMap<String, Map<String, String>?> = mutableMapOf()
|
||||
private val continuationExists: MutableMap<String, Boolean> = mutableMapOf()
|
||||
|
||||
/**
|
||||
* Searches for categories containing the specified string.
|
||||
*
|
||||
|
|
@ -21,8 +27,10 @@ class CategoryClient @Inject constructor(private val categoryInterface: Category
|
|||
*/
|
||||
@JvmOverloads
|
||||
fun searchCategories(filter: String?, itemLimit: Int, offset: Int = 0):
|
||||
Observable<List<String>> {
|
||||
return responseToCategoryName(categoryInterface.searchCategories(filter, itemLimit, offset))
|
||||
Single<List<String>> {
|
||||
return responseToCategoryName(
|
||||
categoryInterface.searchCategories(filter, itemLimit, offset)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -35,7 +43,7 @@ class CategoryClient @Inject constructor(private val categoryInterface: Category
|
|||
*/
|
||||
@JvmOverloads
|
||||
fun searchCategoriesForPrefix(prefix: String?, itemLimit: Int, offset: Int = 0):
|
||||
Observable<List<String>> {
|
||||
Single<List<String>> {
|
||||
return responseToCategoryName(
|
||||
categoryInterface.searchCategoriesForPrefix(prefix, itemLimit, offset)
|
||||
)
|
||||
|
|
@ -48,8 +56,19 @@ class CategoryClient @Inject constructor(private val categoryInterface: Category
|
|||
* @param categoryName Category name as defined on commons
|
||||
* @return Observable emitting the categories returned. If our search yielded "Category:Test", "Test" is emitted.
|
||||
*/
|
||||
fun getSubCategoryList(categoryName: String?): Observable<List<String>> {
|
||||
return responseToCategoryName(categoryInterface.getSubCategoryList(categoryName))
|
||||
fun getSubCategoryList(categoryName: String?): Single<List<String>> {
|
||||
val key = "$SUB_CATEGORY_CONTINUATION_PREFIX$categoryName"
|
||||
return if (hasMorePagesFor(key)) {
|
||||
responseToCategoryName(
|
||||
categoryInterface.getSubCategoryList(
|
||||
categoryName,
|
||||
continuationStore[key] ?: emptyMap()
|
||||
),
|
||||
key
|
||||
)
|
||||
} else {
|
||||
Single.just(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -59,7 +78,7 @@ class CategoryClient @Inject constructor(private val categoryInterface: Category
|
|||
* @param categoryName Category name as defined on commons
|
||||
* @return
|
||||
*/
|
||||
fun getParentCategoryList(categoryName: String?): Observable<List<String>> {
|
||||
fun getParentCategoryList(categoryName: String?): Single<List<String>> {
|
||||
return responseToCategoryName(categoryInterface.getParentCategoryList(categoryName))
|
||||
}
|
||||
|
||||
|
|
@ -69,12 +88,30 @@ class CategoryClient @Inject constructor(private val categoryInterface: Category
|
|||
* @param responseObservable The query response observable
|
||||
* @return Observable emitting the categories returned. If our search yielded "Category:Test", "Test" is emitted.
|
||||
*/
|
||||
private fun responseToCategoryName(responseObservable: Observable<MwQueryResponse>): Observable<List<String>> {
|
||||
private fun responseToCategoryName(
|
||||
responseObservable: Single<MwQueryResponse>,
|
||||
key: String? = null
|
||||
): Single<List<String>> {
|
||||
return responseObservable
|
||||
.map { it.query()?.pages() ?: emptyList() }
|
||||
.map {
|
||||
if (key != null) {
|
||||
continuationExists[key] =
|
||||
it.continuation()?.let { continuation ->
|
||||
continuationStore[key] = continuation
|
||||
true
|
||||
} ?: false
|
||||
}
|
||||
it.query()?.pages() ?: emptyList()
|
||||
}
|
||||
.map {
|
||||
it.map { page -> page.title().replace(CATEGORY_PREFIX, "") }
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasMorePagesFor(key: String) = continuationExists[key] ?: true
|
||||
|
||||
fun resetSubCategoryContinuation(category: String) {
|
||||
continuationExists.remove("$SUB_CATEGORY_CONTINUATION_PREFIX$category")
|
||||
continuationStore.remove("$SUB_CATEGORY_CONTINUATION_PREFIX$category")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import fr.free.nrw.commons.R;
|
|||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.explore.ViewPagerAdapter;
|
||||
import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment;
|
||||
import fr.free.nrw.commons.explore.categories.sub.SubCategoriesFragment;
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -69,13 +70,12 @@ public class CategoryDetailsActivity extends NavigationBaseActivity
|
|||
List<Fragment> fragmentList = new ArrayList<>();
|
||||
List<String> titleList = new ArrayList<>();
|
||||
categoriesMediaFragment = new CategoriesMediaFragment();
|
||||
SubCategoryListFragment subCategoryListFragment = new SubCategoryListFragment();
|
||||
SubCategoriesFragment subCategoryListFragment = new SubCategoriesFragment();
|
||||
SubCategoryListFragment parentCategoryListFragment = new SubCategoryListFragment();
|
||||
categoryName = getIntent().getStringExtra("categoryName");
|
||||
if (getIntent() != null && categoryName != null) {
|
||||
Bundle arguments = new Bundle();
|
||||
arguments.putString("categoryName", categoryName);
|
||||
arguments.putBoolean("isParentCategory", false);
|
||||
categoriesMediaFragment.setArguments(arguments);
|
||||
subCategoryListFragment.setArguments(arguments);
|
||||
Bundle parentCategoryArguments = new Bundle();
|
||||
|
|
|
|||
|
|
@ -195,4 +195,9 @@ public class CategoryImagesActivity
|
|||
public void requestMoreImages() {
|
||||
//unneeded
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaClicked(int position) {
|
||||
// this class is unused and will be deleted
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package fr.free.nrw.commons.category;
|
|||
public interface CategoryImagesCallback {
|
||||
void viewPagerNotifyDataSetChanged();
|
||||
void requestMoreImages();
|
||||
void onMediaClicked(int position);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
package fr.free.nrw.commons.category;
|
||||
|
||||
import io.reactivex.Single;
|
||||
import java.util.Map;
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
import retrofit2.http.QueryMap;
|
||||
|
||||
/**
|
||||
* Interface for interacting with Commons category related APIs
|
||||
|
|
@ -20,7 +21,7 @@ public interface CategoryInterface {
|
|||
*/
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2"
|
||||
+ "&generator=search&gsrnamespace=14")
|
||||
Observable<MwQueryResponse> searchCategories(@Query("gsrsearch") String filter,
|
||||
Single<MwQueryResponse> searchCategories(@Query("gsrsearch") String filter,
|
||||
@Query("gsrlimit") int itemLimit, @Query("gsroffset") int offset);
|
||||
|
||||
/**
|
||||
|
|
@ -32,16 +33,17 @@ public interface CategoryInterface {
|
|||
*/
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2"
|
||||
+ "&generator=allcategories")
|
||||
Observable<MwQueryResponse> searchCategoriesForPrefix(@Query("gacprefix") String prefix,
|
||||
Single<MwQueryResponse> searchCategoriesForPrefix(@Query("gacprefix") String prefix,
|
||||
@Query("gaclimit") int itemLimit, @Query("gacoffset") int offset);
|
||||
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2"
|
||||
+ "&generator=categorymembers&gcmtype=subcat"
|
||||
+ "&prop=info&gcmlimit=500")
|
||||
Observable<MwQueryResponse> getSubCategoryList(@Query("gcmtitle") String categoryName);
|
||||
+ "&prop=info&gcmlimit=50")
|
||||
Single<MwQueryResponse> getSubCategoryList(@Query("gcmtitle") String categoryName,
|
||||
@QueryMap(encoded = true) Map<String, String> continuation);
|
||||
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2"
|
||||
+ "&generator=categories&prop=info&gcllimit=500")
|
||||
Observable<MwQueryResponse> getParentCategoryList(@Query("titles") String categoryName);
|
||||
Single<MwQueryResponse> getParentCategoryList(@Query("titles") String categoryName);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ 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.explore.categories.media.CategoriesMediaFragment;
|
||||
import fr.free.nrw.commons.explore.categories.sub.SubCategoriesFragment;
|
||||
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;
|
||||
|
|
@ -99,4 +100,7 @@ public abstract class FragmentBuilderModule {
|
|||
|
||||
@ContributesAndroidInjector
|
||||
abstract CategoriesMediaFragment bindCategoriesMediaFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract SubCategoriesFragment bindSubCategoriesFragment();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import android.view.Menu;
|
|||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.FrameLayout;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
|
@ -31,8 +30,7 @@ import java.util.List;
|
|||
|
||||
public class ExploreActivity
|
||||
extends NavigationBaseActivity
|
||||
implements MediaDetailPagerFragment.MediaDetailProvider,
|
||||
AdapterView.OnItemClickListener, CategoryImagesCallback {
|
||||
implements MediaDetailPagerFragment.MediaDetailProvider, CategoryImagesCallback {
|
||||
|
||||
private static final String FEATURED_IMAGES_CATEGORY = "Featured_pictures_on_Wikimedia_Commons";
|
||||
private static final String MOBILE_UPLOADS_CATEGORY = "Uploaded_with_Mobile/Android";
|
||||
|
|
@ -177,7 +175,7 @@ public class ExploreActivity
|
|||
* This method is called onClick of media inside category featured images or mobile uploads.
|
||||
*/
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
public void onMediaClicked( int position) {
|
||||
tabLayout.setVisibility(View.GONE);
|
||||
viewPager.setVisibility(View.GONE);
|
||||
mediaContainer.setVisibility(View.VISIBLE);
|
||||
|
|
@ -195,7 +193,7 @@ public class ExploreActivity
|
|||
// coming back to the explore activity. See https://github.com/commons-app/apps-android-commons/issues/1631
|
||||
// https://stackoverflow.com/questions/11353075/how-can-i-maintain-fragment-state-when-added-to-the-back-stack/19022550#19022550 supportFragmentManager.executePendingTransactions();
|
||||
}
|
||||
mediaDetails.showImage(i);
|
||||
mediaDetails.showImage(position);
|
||||
forceInitBackButton();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -191,7 +191,8 @@ public class SearchActivity extends NavigationBaseActivity
|
|||
* Open media detail pager fragment on click of image in search results
|
||||
* @param index item index that should be opened
|
||||
*/
|
||||
public void onSearchImageClicked(int index) {
|
||||
@Override
|
||||
public void onMediaClicked(int index) {
|
||||
ViewUtil.hideKeyboard(this.findViewById(R.id.searchBox));
|
||||
toolbar.setVisibility(View.GONE);
|
||||
tabLayout.setVisibility(View.GONE);
|
||||
|
|
|
|||
|
|
@ -4,12 +4,18 @@ import dagger.Binds
|
|||
import dagger.Module
|
||||
import fr.free.nrw.commons.explore.categories.media.CategoryMediaPresenter
|
||||
import fr.free.nrw.commons.explore.categories.media.CategoryMediaPresenterImpl
|
||||
import fr.free.nrw.commons.explore.categories.sub.SubCategoriesPresenter
|
||||
import fr.free.nrw.commons.explore.categories.sub.SubCategoriesPresenterImpl
|
||||
|
||||
|
||||
@Module
|
||||
abstract class CategoriesModule {
|
||||
|
||||
@Binds
|
||||
abstract fun CategoryMediaPresenterImpl.bindsParentDepictionPresenter()
|
||||
abstract fun CategoryMediaPresenterImpl.bindsCategoryMediaPresenter()
|
||||
: CategoryMediaPresenter
|
||||
|
||||
@Binds
|
||||
abstract fun SubCategoriesPresenterImpl.bindsSubCategoriesPresenter()
|
||||
: SubCategoriesPresenter
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ package fr.free.nrw.commons.explore.categories.media
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import fr.free.nrw.commons.category.CATEGORY_PREFIX
|
||||
import fr.free.nrw.commons.category.CategoryDetailsActivity
|
||||
import fr.free.nrw.commons.category.CategoryImagesCallback
|
||||
import fr.free.nrw.commons.explore.media.PageableMediaFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -20,12 +18,4 @@ class CategoriesMediaFragment : PageableMediaFragment() {
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
onQueryUpdated("$CATEGORY_PREFIX${arguments!!.getString("categoryName")!!}")
|
||||
}
|
||||
|
||||
override fun onItemClicked(position: Int) {
|
||||
(activity as CategoryDetailsActivity).onMediaClicked(position)
|
||||
}
|
||||
|
||||
override fun notifyViewPager() {
|
||||
(activity as CategoryImagesCallback).viewPagerNotifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,6 @@ class PageableCategoriesDataSource @Inject constructor(
|
|||
) : PageableBaseDataSource<String>(liveDataConverter) {
|
||||
|
||||
override val loadFunction = { loadSize: Int, startPosition: Int ->
|
||||
categoryClient.searchCategories(query, loadSize, startPosition).blockingFirst()
|
||||
categoryClient.searchCategories(query, loadSize, startPosition).blockingGet()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package fr.free.nrw.commons.explore.categories.search
|
||||
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.category.CategoryDetailsActivity
|
||||
import fr.free.nrw.commons.explore.paging.BasePagingFragment
|
||||
|
||||
|
||||
abstract class PageableCategoryFragment : BasePagingFragment<String>() {
|
||||
override val errorTextId: Int = R.string.error_loading_categories
|
||||
override val pagedListAdapter by lazy {
|
||||
PagedSearchCategoriesAdapter {
|
||||
CategoryDetailsActivity.startYourself(context, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,20 +8,12 @@ import javax.inject.Inject
|
|||
/**
|
||||
* Displays the category search screen.
|
||||
*/
|
||||
class SearchCategoryFragment : BasePagingFragment<String>() {
|
||||
class SearchCategoryFragment : PageableCategoryFragment() {
|
||||
@Inject
|
||||
lateinit var presenter: SearchCategoriesFragmentPresenter
|
||||
|
||||
override val errorTextId: Int = R.string.error_loading_categories
|
||||
|
||||
override val injectedPresenter
|
||||
get() = presenter
|
||||
|
||||
override val pagedListAdapter by lazy {
|
||||
PagedSearchCategoriesAdapter {
|
||||
CategoryDetailsActivity.startYourself(context, it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getEmptyText(query: String) = getString(R.string.categories_not_found, query)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
package fr.free.nrw.commons.explore.categories.sub
|
||||
|
||||
import fr.free.nrw.commons.category.CategoryClient
|
||||
import fr.free.nrw.commons.explore.paging.LiveDataConverter
|
||||
import fr.free.nrw.commons.explore.paging.PageableBaseDataSource
|
||||
import javax.inject.Inject
|
||||
|
||||
class PageableSubCategoriesDataSource @Inject constructor(
|
||||
liveDataConverter: LiveDataConverter,
|
||||
val categoryClient: CategoryClient
|
||||
) : PageableBaseDataSource<String>(liveDataConverter) {
|
||||
|
||||
override val loadFunction = { loadSize: Int, startPosition: Int ->
|
||||
if (startPosition == 0) {
|
||||
categoryClient.resetSubCategoryContinuation(query)
|
||||
}
|
||||
categoryClient.getSubCategoryList(query).blockingGet()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package fr.free.nrw.commons.explore.categories.sub
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.category.CATEGORY_PREFIX
|
||||
import fr.free.nrw.commons.explore.categories.search.PageableCategoryFragment
|
||||
import fr.free.nrw.commons.explore.paging.PagingContract
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class SubCategoriesFragment : PageableCategoryFragment() {
|
||||
|
||||
@Inject lateinit var presenter: SubCategoriesPresenter
|
||||
|
||||
override val injectedPresenter: PagingContract.Presenter<String>
|
||||
get() = presenter
|
||||
|
||||
override fun getEmptyText(query: String) = getString(R.string.no_subcategory_found)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
onQueryUpdated("$CATEGORY_PREFIX${arguments!!.getString("categoryName")!!}")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package fr.free.nrw.commons.explore.categories.sub
|
||||
|
||||
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 SubCategoriesPresenter : PagingContract.Presenter<String>
|
||||
|
||||
class SubCategoriesPresenterImpl @Inject constructor(
|
||||
@Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler,
|
||||
dataSourceFactory: PageableSubCategoriesDataSource
|
||||
) : BasePagingPresenter<String>(mainThreadScheduler, dataSourceFactory),
|
||||
SubCategoriesPresenter
|
||||
|
|
@ -2,7 +2,6 @@ package fr.free.nrw.commons.explore.depictions.media
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity
|
||||
import fr.free.nrw.commons.explore.media.PageableMediaFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -18,11 +17,4 @@ class DepictedImagesFragment : PageableMediaFragment() {
|
|||
onQueryUpdated(arguments!!.getString("entityId")!!)
|
||||
}
|
||||
|
||||
override fun onItemClicked(position: Int) {
|
||||
(activity as WikidataItemDetailsActivity).onMediaClicked(position)
|
||||
}
|
||||
|
||||
override fun notifyViewPager() {
|
||||
(activity as WikidataItemDetailsActivity).viewPagerNotifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,38 @@
|
|||
package fr.free.nrw.commons.explore.media
|
||||
|
||||
import android.content.Context
|
||||
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.category.CategoryImagesCallback
|
||||
import fr.free.nrw.commons.explore.paging.BasePagingFragment
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider
|
||||
import kotlinx.android.synthetic.main.fragment_search_paginated.*
|
||||
|
||||
|
||||
abstract class PageableMediaFragment : BasePagingFragment<Media>() {
|
||||
override val pagedListAdapter by lazy { PagedMediaAdapter(::onItemClicked) }
|
||||
abstract class PageableMediaFragment : BasePagingFragment<Media>(), MediaDetailProvider {
|
||||
|
||||
override val pagedListAdapter by lazy {
|
||||
PagedMediaAdapter {
|
||||
categoryImagesCallback.onMediaClicked(it)
|
||||
}
|
||||
}
|
||||
|
||||
override val errorTextId: Int = R.string.error_loading_images
|
||||
|
||||
override fun getEmptyText(query: String) = getString(R.string.no_images_found)
|
||||
|
||||
protected abstract fun onItemClicked(position: Int)
|
||||
lateinit var categoryImagesCallback: CategoryImagesCallback
|
||||
|
||||
protected abstract fun notifyViewPager()
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
categoryImagesCallback = (context as CategoryImagesCallback)
|
||||
}
|
||||
|
||||
private val simpleDataObserver = SimpleDataObserver { notifyViewPager() }
|
||||
private val simpleDataObserver = SimpleDataObserver {
|
||||
categoryImagesCallback.viewPagerNotifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
|
@ -31,12 +44,12 @@ abstract class PageableMediaFragment : BasePagingFragment<Media>() {
|
|||
pagedListAdapter.unregisterAdapterDataObserver(simpleDataObserver)
|
||||
}
|
||||
|
||||
fun getMediaAtPosition(position: Int): Media? =
|
||||
override fun getMediaAtPosition(position: Int): Media? =
|
||||
pagedListAdapter.currentList?.get(position)?.takeIf { it.filename != null }
|
||||
.also {
|
||||
pagedListAdapter.currentList?.loadAround(position)
|
||||
paginatedSearchResultsList.scrollToPosition(position)
|
||||
}
|
||||
|
||||
fun getTotalMediaCount(): Int = pagedListAdapter.itemCount
|
||||
override fun getTotalMediaCount(): Int = pagedListAdapter.itemCount
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package fr.free.nrw.commons.explore.media
|
||||
|
||||
import fr.free.nrw.commons.category.CategoryImagesCallback
|
||||
import fr.free.nrw.commons.explore.SearchActivity
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
|
@ -13,14 +11,5 @@ class SearchMediaFragment : PageableMediaFragment(){
|
|||
|
||||
override val injectedPresenter
|
||||
get() = presenter
|
||||
|
||||
override fun onItemClicked(position: Int) {
|
||||
(context as SearchActivity).onSearchImageClicked(position)
|
||||
}
|
||||
|
||||
override fun notifyViewPager() {
|
||||
(activity as CategoryImagesCallback).viewPagerNotifyDataSetChanged()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -211,5 +211,6 @@ class MediaClient @Inject constructor(
|
|||
|
||||
fun resetCategoryContinuation(category: String) {
|
||||
continuationExists.remove("$CATEGORY_CONTINUATION_PREFIX$category")
|
||||
continuationStore.remove("$CATEGORY_CONTINUATION_PREFIX$category")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ import categoryItem
|
|||
import com.nhaarman.mockitokotlin2.mock
|
||||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import depictedItem
|
||||
import fr.free.nrw.commons.explore.depictions.DepictsClient
|
||||
import fr.free.nrw.commons.upload.GpsCategoryModel
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
|
@ -35,7 +34,7 @@ class CategoriesModelTest {
|
|||
|
||||
val expectedList = listOf("Test")
|
||||
whenever(categoryClient.searchCategoriesForPrefix("tes", 25))
|
||||
.thenReturn(Observable.just(expectedList))
|
||||
.thenReturn(Single.just(expectedList))
|
||||
|
||||
// Checking if both return "Test"
|
||||
val expectedItems = expectedList.map { CategoryItem(it, false) }
|
||||
|
|
@ -56,7 +55,7 @@ class CategoriesModelTest {
|
|||
whenever(gpsCategoryModel.categoriesFromLocation)
|
||||
.thenReturn(BehaviorSubject.createDefault(listOf("gpsCategory")))
|
||||
whenever(categoryClient.searchCategories("tes", 25))
|
||||
.thenReturn(Observable.just(listOf("titleSearch")))
|
||||
.thenReturn(Single.just(listOf("titleSearch")))
|
||||
whenever(categoryDao.recentCategories(25)).thenReturn(listOf("recentCategories"))
|
||||
CategoriesModel(categoryClient, categoryDao, gpsCategoryModel)
|
||||
.searchAll("", listOf("tes"), listOf(depictedItem))
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@ package fr.free.nrw.commons.category
|
|||
|
||||
import com.nhaarman.mockitokotlin2.mock
|
||||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import org.mockito.ArgumentMatchers.*
|
||||
import org.mockito.InjectMocks
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.mock
|
||||
|
|
@ -32,7 +31,7 @@ class CategoryClientTest {
|
|||
fun searchCategoriesFound() {
|
||||
val mockResponse = withMockResponse("Category:Test")
|
||||
whenever(categoryInterface.searchCategories(anyString(), anyInt(), anyInt()))
|
||||
.thenReturn(Observable.just(mockResponse))
|
||||
.thenReturn(Single.just(mockResponse))
|
||||
categoryClient.searchCategories("tes", 10)
|
||||
.test()
|
||||
.assertValues(listOf("Test"))
|
||||
|
|
@ -45,7 +44,7 @@ class CategoryClientTest {
|
|||
fun searchCategoriesNull() {
|
||||
val mockResponse = withNullPages()
|
||||
whenever(categoryInterface.searchCategories(anyString(), anyInt(), anyInt()))
|
||||
.thenReturn(Observable.just(mockResponse))
|
||||
.thenReturn(Single.just(mockResponse))
|
||||
categoryClient.searchCategories("tes", 10)
|
||||
.test()
|
||||
.assertValues(emptyList())
|
||||
|
|
@ -58,7 +57,7 @@ class CategoryClientTest {
|
|||
fun searchCategoriesForPrefixFound() {
|
||||
val mockResponse = withMockResponse("Category:Test")
|
||||
whenever(categoryInterface.searchCategoriesForPrefix(anyString(), anyInt(), anyInt()))
|
||||
.thenReturn(Observable.just(mockResponse))
|
||||
.thenReturn(Single.just(mockResponse))
|
||||
categoryClient.searchCategoriesForPrefix("tes", 10)
|
||||
.test()
|
||||
.assertValues(listOf("Test"))
|
||||
|
|
@ -71,7 +70,7 @@ class CategoryClientTest {
|
|||
fun searchCategoriesForPrefixNull() {
|
||||
val mockResponse = withNullPages()
|
||||
whenever(categoryInterface.searchCategoriesForPrefix(anyString(), anyInt(), anyInt()))
|
||||
.thenReturn(Observable.just(mockResponse))
|
||||
.thenReturn(Single.just(mockResponse))
|
||||
categoryClient.searchCategoriesForPrefix("tes", 10)
|
||||
.test()
|
||||
.assertValues(emptyList())
|
||||
|
|
@ -84,7 +83,7 @@ class CategoryClientTest {
|
|||
fun getParentCategoryListFound() {
|
||||
val mockResponse = withMockResponse("Category:Test")
|
||||
whenever(categoryInterface.getParentCategoryList(anyString()))
|
||||
.thenReturn(Observable.just(mockResponse))
|
||||
.thenReturn(Single.just(mockResponse))
|
||||
categoryClient.getParentCategoryList("tes")
|
||||
.test()
|
||||
.assertValues(listOf("Test"))
|
||||
|
|
@ -94,7 +93,7 @@ class CategoryClientTest {
|
|||
fun getParentCategoryListNull() {
|
||||
val mockResponse = withNullPages()
|
||||
whenever(categoryInterface.getParentCategoryList(anyString()))
|
||||
.thenReturn(Observable.just(mockResponse))
|
||||
.thenReturn(Single.just(mockResponse))
|
||||
categoryClient.getParentCategoryList("tes")
|
||||
.test()
|
||||
.assertValues(emptyList())
|
||||
|
|
@ -103,8 +102,8 @@ class CategoryClientTest {
|
|||
@Test
|
||||
fun getSubCategoryListFound() {
|
||||
val mockResponse = withMockResponse("Category:Test")
|
||||
whenever(categoryInterface.getSubCategoryList("tes"))
|
||||
.thenReturn(Observable.just(mockResponse))
|
||||
whenever(categoryInterface.getSubCategoryList("tes", emptyMap()))
|
||||
.thenReturn(Single.just(mockResponse))
|
||||
categoryClient.getSubCategoryList("tes")
|
||||
.test()
|
||||
.assertValues(listOf("Test"))
|
||||
|
|
@ -113,8 +112,11 @@ class CategoryClientTest {
|
|||
@Test
|
||||
fun getSubCategoryListNull() {
|
||||
val mockResponse = withNullPages()
|
||||
whenever(categoryInterface.getSubCategoryList(anyString()))
|
||||
.thenReturn(Observable.just(mockResponse))
|
||||
whenever(categoryInterface.getSubCategoryList(
|
||||
anyString(),
|
||||
anyMap()
|
||||
))
|
||||
.thenReturn(Single.just(mockResponse))
|
||||
categoryClient.getSubCategoryList("tes")
|
||||
.test()
|
||||
.assertValues(emptyList())
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
package fr.free.nrw.commons.explore.categories.sub
|
||||
|
||||
import com.nhaarman.mockitokotlin2.never
|
||||
import com.nhaarman.mockitokotlin2.verify
|
||||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import fr.free.nrw.commons.category.CategoryClient
|
||||
import fr.free.nrw.commons.explore.paging.LiveDataConverter
|
||||
import io.reactivex.Single
|
||||
import org.hamcrest.CoreMatchers.`is`
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
class PageableSubCategoriesDataSourceTest{
|
||||
@Mock
|
||||
lateinit var categoryClient: CategoryClient
|
||||
@Mock
|
||||
lateinit var liveDataConverter: LiveDataConverter
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `loadFunction calls reset at position 0`() {
|
||||
val dataSource =
|
||||
PageableSubCategoriesDataSource(liveDataConverter, categoryClient)
|
||||
dataSource.onQueryUpdated("test")
|
||||
whenever(categoryClient.getSubCategoryList("test"))
|
||||
.thenReturn(Single.just(emptyList()))
|
||||
assertThat(dataSource.loadFunction(-1, 0), `is`(emptyList()))
|
||||
verify(categoryClient).resetSubCategoryContinuation("test")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `loadFunction does not call reset at any other position`() {
|
||||
val dataSource =
|
||||
PageableSubCategoriesDataSource(liveDataConverter, categoryClient)
|
||||
dataSource.onQueryUpdated("test")
|
||||
whenever(categoryClient.getSubCategoryList("test"))
|
||||
.thenReturn(Single.just(emptyList()))
|
||||
assertThat(dataSource.loadFunction(-1, 1), `is`(emptyList()))
|
||||
verify(categoryClient, never()).resetSubCategoryContinuation("test")
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import com.nhaarman.mockitokotlin2.mock
|
|||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import fr.free.nrw.commons.category.CategoryClient
|
||||
import fr.free.nrw.commons.explore.categories.search.PageableCategoriesDataSource
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.Matchers
|
||||
import org.junit.Test
|
||||
|
|
@ -14,7 +14,7 @@ class PageableCategoriesDataSourceTest {
|
|||
fun `loadFunction loads categories`() {
|
||||
val categoryClient: CategoryClient = mock()
|
||||
whenever(categoryClient.searchCategories("test", 0, 1))
|
||||
.thenReturn(Observable.just(emptyList()))
|
||||
.thenReturn(Single.just(emptyList()))
|
||||
val pageableCategoriesDataSource = PageableCategoriesDataSource(mock(), categoryClient)
|
||||
pageableCategoriesDataSource.onQueryUpdated("test")
|
||||
assertThat(pageableCategoriesDataSource.loadFunction(0, 1), Matchers.`is`(emptyList()))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue