diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarksActivity.java b/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarksActivity.java index ad6e10e22..e16ec6d31 100644 --- a/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarksActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarksActivity.java @@ -140,9 +140,6 @@ public class BookmarksActivity extends NavigationBaseActivity return adapter.getMediaAdapter().getCount(); } - @Override - public void requestMoreImages() { } - @Override public void onMediaClicked(int position) { //TODO use with pagination diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryClient.kt b/app/src/main/java/fr/free/nrw/commons/category/CategoryClient.kt index 15f6f2854..aeb62b127 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryClient.kt @@ -7,15 +7,14 @@ import javax.inject.Singleton const val CATEGORY_PREFIX = "Category:" const val SUB_CATEGORY_CONTINUATION_PREFIX = "sub_category_" +const val PARENT_CATEGORY_CONTINUATION_PREFIX = "parent_category_" /** * Category Client to handle custom calls to Commons MediaWiki APIs */ @Singleton -class CategoryClient @Inject constructor(private val categoryInterface: CategoryInterface) { - - private val continuationStore: MutableMap?> = mutableMapOf() - private val continuationExists: MutableMap = mutableMapOf() +class CategoryClient @Inject constructor(private val categoryInterface: CategoryInterface) : + ContinuationClient() { /** * Searches for categories containing the specified string. @@ -28,9 +27,7 @@ class CategoryClient @Inject constructor(private val categoryInterface: Category @JvmOverloads fun searchCategories(filter: String?, itemLimit: Int, offset: Int = 0): Single> { - return responseToCategoryName( - categoryInterface.searchCategories(filter, itemLimit, offset) - ) + return responseMapper(categoryInterface.searchCategories(filter, itemLimit, offset)) } /** @@ -44,7 +41,7 @@ class CategoryClient @Inject constructor(private val categoryInterface: Category @JvmOverloads fun searchCategoriesForPrefix(prefix: String?, itemLimit: Int, offset: Int = 0): Single> { - return responseToCategoryName( + return responseMapper( categoryInterface.searchCategoriesForPrefix(prefix, itemLimit, offset) ) } @@ -56,18 +53,11 @@ 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?): Single> { - val key = "$SUB_CATEGORY_CONTINUATION_PREFIX$categoryName" - return if (hasMorePagesFor(key)) { - responseToCategoryName( - categoryInterface.getSubCategoryList( - categoryName, - continuationStore[key] ?: emptyMap() - ), - key + fun getSubCategoryList(categoryName: String): Single> { + return continuationRequest(SUB_CATEGORY_CONTINUATION_PREFIX, categoryName) { + categoryInterface.getSubCategoryList( + categoryName, it ) - } else { - Single.just(emptyList()) } } @@ -78,29 +68,27 @@ class CategoryClient @Inject constructor(private val categoryInterface: Category * @param categoryName Category name as defined on commons * @return */ - fun getParentCategoryList(categoryName: String?): Single> { - return responseToCategoryName(categoryInterface.getParentCategoryList(categoryName)) + fun getParentCategoryList(categoryName: String): Single> { + return continuationRequest(PARENT_CATEGORY_CONTINUATION_PREFIX, categoryName) { + categoryInterface.getParentCategoryList(categoryName, it) + } } - /** - * 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 fun responseToCategoryName( - responseObservable: Single, - key: String? = null + fun resetSubCategoryContinuation(category: String) { + resetContinuation(SUB_CATEGORY_CONTINUATION_PREFIX, category) + } + + fun resetParentCategoryContinuation(category: String) { + resetContinuation(PARENT_CATEGORY_CONTINUATION_PREFIX, category) + } + + override fun responseMapper( + networkResult: Single, + key: String? ): Single> { - return responseObservable + return networkResult .map { - if (key != null) { - continuationExists[key] = - it.continuation()?.let { continuation -> - continuationStore[key] = continuation - true - } ?: false - } + handleContinuationResponse(it.continuation(), key) it.query()?.pages() ?: emptyList() } .map { @@ -108,10 +96,4 @@ class CategoryClient @Inject constructor(private val categoryInterface: Category } } - 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") - } } diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java index f2d53ba9b..ed085fc37 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java @@ -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.parent.ParentCategoriesFragment; import fr.free.nrw.commons.explore.categories.sub.SubCategoriesFragment; import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.theme.NavigationBaseActivity; @@ -71,23 +72,20 @@ public class CategoryDetailsActivity extends NavigationBaseActivity List titleList = new ArrayList<>(); categoriesMediaFragment = new CategoriesMediaFragment(); SubCategoriesFragment subCategoryListFragment = new SubCategoriesFragment(); - SubCategoryListFragment parentCategoryListFragment = new SubCategoryListFragment(); + ParentCategoriesFragment parentCategoriesFragment = new ParentCategoriesFragment(); categoryName = getIntent().getStringExtra("categoryName"); if (getIntent() != null && categoryName != null) { Bundle arguments = new Bundle(); arguments.putString("categoryName", categoryName); categoriesMediaFragment.setArguments(arguments); subCategoryListFragment.setArguments(arguments); - Bundle parentCategoryArguments = new Bundle(); - parentCategoryArguments.putString("categoryName", categoryName); - parentCategoryArguments.putBoolean("isParentCategory", true); - parentCategoryListFragment.setArguments(parentCategoryArguments); + parentCategoriesFragment.setArguments(arguments); } fragmentList.add(categoriesMediaFragment); titleList.add("MEDIA"); fragmentList.add(subCategoryListFragment); titleList.add("SUBCATEGORIES"); - fragmentList.add(parentCategoryListFragment); + fragmentList.add(parentCategoriesFragment); titleList.add("PARENT CATEGORIES"); viewPagerAdapter.setTabData(fragmentList, titleList); viewPagerAdapter.notifyDataSetChanged(); @@ -133,7 +131,6 @@ public class CategoryDetailsActivity extends NavigationBaseActivity */ public static void startYourself(Context context, String categoryName) { Intent intent = new Intent(context, CategoryDetailsActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.putExtra("categoryName", categoryName); context.startActivity(intent); } @@ -212,12 +209,4 @@ public class CategoryDetailsActivity extends NavigationBaseActivity } } - /** - * This method is called when viewPager has reached its end. - * Fetches more images using search query and adds it to the grid view and viewpager adapter - */ - @Override - public void requestMoreImages() { - //unneeded - } } diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java index 8e7cf9778..058aa397f 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java @@ -187,15 +187,6 @@ public class CategoryImagesActivity } } - /** - * This method is called when viewPager has reached its end. - * Fetches more images using search query and adds it to the gridView and viewpager adapter - */ - @Override - public void requestMoreImages() { - //unneeded - } - @Override public void onMediaClicked(int position) { // this class is unused and will be deleted diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesCallback.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesCallback.java index 06b09b10b..5b85a2f81 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesCallback.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesCallback.java @@ -7,7 +7,6 @@ package fr.free.nrw.commons.category; public interface CategoryImagesCallback { void viewPagerNotifyDataSetChanged(); - void requestMoreImages(); void onMediaClicked(int position); } diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryInterface.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryInterface.java index e6c00a68c..a9f030dec 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryInterface.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryInterface.java @@ -43,7 +43,8 @@ public interface CategoryInterface { @QueryMap(encoded = true) Map continuation); @GET("w/api.php?action=query&format=json&formatversion=2" - + "&generator=categories&prop=info&gcllimit=500") - Single getParentCategoryList(@Query("titles") String categoryName); + + "&generator=categories&prop=info&gcllimit=50") + Single getParentCategoryList(@Query("titles") String categoryName, + @QueryMap(encoded = true) Map continuation); } diff --git a/app/src/main/java/fr/free/nrw/commons/category/ContinuationClient.kt b/app/src/main/java/fr/free/nrw/commons/category/ContinuationClient.kt new file mode 100644 index 000000000..cbf9017b2 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/category/ContinuationClient.kt @@ -0,0 +1,41 @@ +package fr.free.nrw.commons.category + +import io.reactivex.Single + + +abstract class ContinuationClient { + private val continuationStore: MutableMap?> = mutableMapOf() + private val continuationExists: MutableMap = mutableMapOf() + + private fun hasMorePagesFor(key: String) = continuationExists[key] ?: true + fun continuationRequest( + prefix: String, + name: String, + requestFunction: (Map) -> Single + ): Single> { + val key = "$prefix$name" + return if (hasMorePagesFor(key)) { + responseMapper(requestFunction(continuationStore[key] ?: emptyMap()), key) + } else { + Single.just(emptyList()) + } + } + + abstract fun responseMapper(networkResult: Single, key: String?=null): Single> + + fun handleContinuationResponse(continuation:Map?, key:String?){ + if (key != null) { + continuationExists[key] = + continuation?.let { continuation -> + continuationStore[key] = continuation + true + } ?: false + } + } + + protected fun resetContinuation(prefix: String, category: String) { + continuationExists.remove("$prefix$category") + continuationStore.remove("$prefix$category") + } + +} diff --git a/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java deleted file mode 100644 index d5f33a20f..000000000 --- a/app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java +++ /dev/null @@ -1,154 +0,0 @@ -package fr.free.nrw.commons.category; - - -import static android.view.View.GONE; -import static android.view.View.VISIBLE; -import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_PREFIX; - -import android.content.Intent; -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.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import butterknife.BindView; -import butterknife.ButterKnife; -import fr.free.nrw.commons.R; -import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; -import fr.free.nrw.commons.explore.categories.search.SearchCategoriesAdapter; -import fr.free.nrw.commons.utils.NetworkUtils; -import fr.free.nrw.commons.utils.ViewUtil; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; -import java.util.List; -import javax.inject.Inject; -import kotlin.Unit; -import timber.log.Timber; - -/** - * Displays the category search screen. - */ - -public class SubCategoryListFragment extends CommonsDaggerSupportFragment { - - @BindView(R.id.imagesListBox) - RecyclerView categoriesRecyclerView; - @BindView(R.id.imageSearchInProgress) - ProgressBar progressBar; - @BindView(R.id.imagesNotFound) - TextView categoriesNotFoundView; - - private String categoryName = null; - @Inject CategoryClient categoryClient; - - private SearchCategoriesAdapter categoriesAdapter; - private boolean isParentCategory = true; - - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { - View rootView = inflater.inflate(R.layout.fragment_browse_image, container, false); - ButterKnife.bind(this, rootView); - categoryName = getArguments().getString("categoryName"); - isParentCategory = getArguments().getBoolean("isParentCategory"); - initSubCategoryList(); - if (getActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){ - categoriesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - } - else{ - categoriesRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2)); - } - categoriesAdapter = new SearchCategoriesAdapter(item->{ - Intent intent = new Intent(getContext(), CategoryDetailsActivity.class); - intent.putExtra("categoryName", item); - getContext().startActivity(intent); - return Unit.INSTANCE; - }); - categoriesRecyclerView.setAdapter(categoriesAdapter); - return rootView; - } - - /** - * Checks for internet connection and then initializes the recycler view with all(max 500) categories of the searched query - * Clearing categoryAdapter every time new keyword is searched so that user can see only new results - */ - public void initSubCategoryList() { - categoriesNotFoundView.setVisibility(GONE); - if (!NetworkUtils.isInternetConnectionEstablished(getContext())) { - handleNoInternet(); - return; - } - progressBar.setVisibility(View.VISIBLE); - if (isParentCategory) { - compositeDisposable.add(categoryClient.getParentCategoryList( - CATEGORY_PREFIX +categoryName) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(this::handleSuccess, this::handleError)); - } else { - compositeDisposable.add(categoryClient.getSubCategoryList( - CATEGORY_PREFIX +categoryName) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(this::handleSuccess, this::handleError)); - } - } - - - /** - * Handles the success scenario - * it initializes the recycler view by adding items to the adapter - * @param subCategoryList - */ - private void handleSuccess(List subCategoryList) { - if (subCategoryList == null || subCategoryList.isEmpty()) { - initEmptyView(); - } - else { - progressBar.setVisibility(View.GONE); - categoriesAdapter.addAll(subCategoryList); - categoriesAdapter.notifyDataSetChanged(); - } - } - - /** - * Logs and handles API error scenario - * @param throwable - */ - private void handleError(Throwable throwable) { - if (!isParentCategory){ - Timber.e(throwable, "Error occurred while loading queried subcategories"); - ViewUtil.showShortSnackbar(categoriesRecyclerView,R.string.error_loading_categories); - }else { - Timber.e(throwable, "Error occurred while loading queried parentcategories"); - ViewUtil.showShortSnackbar(categoriesRecyclerView,R.string.error_loading_categories); - } - } - - /** - * Handles the UI updates for a empty results scenario - */ - private void initEmptyView() { - progressBar.setVisibility(GONE); - categoriesNotFoundView.setVisibility(VISIBLE); - if (!isParentCategory){ - categoriesNotFoundView.setText(getString(R.string.no_subcategory_found)); - }else { - categoriesNotFoundView.setText(getString(R.string.no_parentcategory_found)); - } - - } - - /** - * Handles the UI updates for no internet scenario - */ - private void handleNoInternet() { - progressBar.setVisibility(GONE); - ViewUtil.showShortSnackbar(categoriesRecyclerView, R.string.no_internet); - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt index a001da3ca..44b1a35fb 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt @@ -51,9 +51,6 @@ class ContributionBoundaryCallback @Inject constructor( * Fetches contributions using the MediaWiki API */ fun fetchContributions() { - if (mediaClient.doesMediaListForUserHaveMorePages(sessionManager.userName!!).not()) { - return - } compositeDisposable.add( mediaClient.getMediaListForUser(sessionManager.userName!!) .map { mediaList: List -> diff --git a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java index 19d5a0eda..75359588b 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java @@ -4,15 +4,15 @@ import dagger.Module; import dagger.android.ContributesAndroidInjector; import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsFragment; import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment; -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.parent.ParentCategoriesFragment; +import fr.free.nrw.commons.explore.categories.search.SearchCategoryFragment; 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; -import fr.free.nrw.commons.explore.categories.search.SearchCategoryFragment; 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; @@ -29,7 +29,7 @@ import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment; /** * This Class Represents the Module for dependency injection (using dagger) * so, if a developer needs to add a new Fragment to the commons app - * then that must be mentioned here to inject the dependencies + * then that must be mentioned here to inject the dependencies */ @Module @SuppressWarnings({"WeakerAccess", "unused"}) @@ -50,9 +50,6 @@ public abstract class FragmentBuilderModule { @ContributesAndroidInjector abstract DepictedImagesFragment bindDepictedImagesFragment(); - @ContributesAndroidInjector - abstract SubCategoryListFragment bindSubCategoryListFragment(); - @ContributesAndroidInjector abstract SearchMediaFragment bindBrowseImagesListFragment(); @@ -103,4 +100,7 @@ public abstract class FragmentBuilderModule { @ContributesAndroidInjector abstract SubCategoriesFragment bindSubCategoriesFragment(); + + @ContributesAndroidInjector + abstract ParentCategoriesFragment bindParentCategoriesFragment(); } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/ExploreActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/ExploreActivity.java index 570ab68d4..485c716fc 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/ExploreActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/ExploreActivity.java @@ -161,16 +161,6 @@ public class ExploreActivity super.onBackPressed(); } - - /** - * This method is called when viewPager has reached its end. - * Fetches more images and adds them to the recycler view and viewpager adapter - */ - @Override - public void requestMoreImages() { - //unneeded - } - /** * This method is called onClick of media inside category featured images or mobile uploads. */ diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index b6e0e1226..14a4fe889 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -264,14 +264,6 @@ public class SearchActivity extends NavigationBaseActivity viewPager.requestFocus(); } - /** - * This method is called when viewPager has reached its end. - * Fetches more images using search query and adds it to the recycler view and viewpager adapter - */ - @Override - public void requestMoreImages() { - //unneeded - } @Override protected void onDestroy() { super.onDestroy(); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/CategoriesModule.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/CategoriesModule.kt index c7484f5bb..394b88ca6 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/CategoriesModule.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/CategoriesModule.kt @@ -4,6 +4,8 @@ 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.parent.ParentCategoriesPresenter +import fr.free.nrw.commons.explore.categories.parent.ParentCategoriesPresenterImpl import fr.free.nrw.commons.explore.categories.sub.SubCategoriesPresenter import fr.free.nrw.commons.explore.categories.sub.SubCategoriesPresenterImpl @@ -18,4 +20,8 @@ abstract class CategoriesModule { @Binds abstract fun SubCategoriesPresenterImpl.bindsSubCategoriesPresenter() : SubCategoriesPresenter + + @Binds + abstract fun ParentCategoriesPresenterImpl.bindsParentCategoriesPresenter() + : ParentCategoriesPresenter } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/PageableCategoryFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/PageableCategoryFragment.kt similarity index 89% rename from app/src/main/java/fr/free/nrw/commons/explore/categories/search/PageableCategoryFragment.kt rename to app/src/main/java/fr/free/nrw/commons/explore/categories/PageableCategoryFragment.kt index d4c9597f2..54a97cb09 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/PageableCategoryFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/PageableCategoryFragment.kt @@ -1,4 +1,4 @@ -package fr.free.nrw.commons.explore.categories.search +package fr.free.nrw.commons.explore.categories import fr.free.nrw.commons.R import fr.free.nrw.commons.category.CategoryDetailsActivity diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/PagedSearchCategoriesAdapter.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/PagedCategoriesAdapter.kt similarity index 96% rename from app/src/main/java/fr/free/nrw/commons/explore/categories/search/PagedSearchCategoriesAdapter.kt rename to app/src/main/java/fr/free/nrw/commons/explore/categories/PagedCategoriesAdapter.kt index 220339180..fd41a6b73 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/PagedSearchCategoriesAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/PagedCategoriesAdapter.kt @@ -1,4 +1,4 @@ -package fr.free.nrw.commons.explore.categories.search +package fr.free.nrw.commons.explore.categories import android.view.View import android.view.ViewGroup diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/PageableParentCategoriesDataSource.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/PageableParentCategoriesDataSource.kt new file mode 100644 index 000000000..24d2356e6 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/PageableParentCategoriesDataSource.kt @@ -0,0 +1,19 @@ +package fr.free.nrw.commons.explore.categories.parent + +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 PageableParentCategoriesDataSource @Inject constructor( + liveDataConverter: LiveDataConverter, + val categoryClient: CategoryClient +) : PageableBaseDataSource(liveDataConverter) { + + override val loadFunction = { loadSize: Int, startPosition: Int -> + if (startPosition == 0) { + categoryClient.resetParentCategoryContinuation(query) + } + categoryClient.getParentCategoryList(query).blockingGet() + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/ParentCategoriesFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/ParentCategoriesFragment.kt new file mode 100644 index 000000000..a43fdad1d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/ParentCategoriesFragment.kt @@ -0,0 +1,26 @@ +package fr.free.nrw.commons.explore.categories.parent + +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.PageableCategoryFragment +import javax.inject.Inject + + +class ParentCategoriesFragment : PageableCategoryFragment() { + + @Inject + lateinit var presenter: ParentCategoriesPresenter + + override val injectedPresenter + get() = presenter + + override fun getEmptyText(query: String) = getString(R.string.no_parentcategory_found) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + onQueryUpdated("$CATEGORY_PREFIX${arguments!!.getString("categoryName")!!}") + } +} + diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/ParentCategoriesPresenterImpl.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/ParentCategoriesPresenterImpl.kt new file mode 100644 index 000000000..90c11d10d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/ParentCategoriesPresenterImpl.kt @@ -0,0 +1,17 @@ +package fr.free.nrw.commons.explore.categories.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 io.reactivex.Scheduler +import javax.inject.Inject +import javax.inject.Named + + +interface ParentCategoriesPresenter : PagingContract.Presenter + +class ParentCategoriesPresenterImpl @Inject constructor( + @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, + dataSourceFactory: PageableParentCategoriesDataSource +) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), + ParentCategoriesPresenter diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/PageableCategoriesDataSource.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/search/PageableSearchCategoriesDataSource.kt similarity index 90% rename from app/src/main/java/fr/free/nrw/commons/explore/categories/search/PageableCategoriesDataSource.kt rename to app/src/main/java/fr/free/nrw/commons/explore/categories/search/PageableSearchCategoriesDataSource.kt index 320980143..bab526cbe 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/PageableCategoriesDataSource.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/search/PageableSearchCategoriesDataSource.kt @@ -5,7 +5,7 @@ import fr.free.nrw.commons.explore.paging.LiveDataConverter import fr.free.nrw.commons.explore.paging.PageableBaseDataSource import javax.inject.Inject -class PageableCategoriesDataSource @Inject constructor( +class PageableSearchCategoriesDataSource @Inject constructor( liveDataConverter: LiveDataConverter, val categoryClient: CategoryClient ) : PageableBaseDataSource(liveDataConverter) { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoriesAdapter.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoriesAdapter.kt deleted file mode 100644 index fc5dff374..000000000 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoriesAdapter.kt +++ /dev/null @@ -1,11 +0,0 @@ -package fr.free.nrw.commons.explore.categories.search - -import fr.free.nrw.commons.upload.categories.BaseDelegateAdapter - - -class SearchCategoriesAdapter(onCateoryClicked: (String) -> Unit) : BaseDelegateAdapter( - searchCategoryDelegate( - onCateoryClicked - ), - areItemsTheSame = { oldItem, newItem -> oldItem == newItem } -) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoriesAdapterDelegates.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoriesAdapterDelegates.kt deleted file mode 100644 index 9c7811f27..000000000 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoriesAdapterDelegates.kt +++ /dev/null @@ -1,15 +0,0 @@ -package fr.free.nrw.commons.explore.categories.search - -import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer -import fr.free.nrw.commons.R -import fr.free.nrw.commons.category.CATEGORY_PREFIX -import kotlinx.android.synthetic.main.item_recent_searches.* - - -fun searchCategoryDelegate(onCategoryClicked: (String) -> Unit) = - adapterDelegateLayoutContainer(R.layout.item_recent_searches) { - containerView.setOnClickListener { onCategoryClicked(item) } - bind { - textView1.text = item.substringAfter(CATEGORY_PREFIX) - } - } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoriesFragmentPresenterImpl.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoriesFragmentPresenterImpl.kt index 691fa961a..998dee1e8 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoriesFragmentPresenterImpl.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoriesFragmentPresenterImpl.kt @@ -11,6 +11,6 @@ interface SearchCategoriesFragmentPresenter : PagingContract.Presenter class SearchCategoriesFragmentPresenterImpl @Inject constructor( @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, - dataSourceFactory: PageableCategoriesDataSource + dataSourceFactory: PageableSearchCategoriesDataSource ) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), SearchCategoriesFragmentPresenter diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoryFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoryFragment.kt index 6c3c0a741..042c9d7c4 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoryFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoryFragment.kt @@ -1,8 +1,7 @@ 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 +import fr.free.nrw.commons.explore.categories.PageableCategoryFragment import javax.inject.Inject /** diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/SubCategoriesFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/SubCategoriesFragment.kt index 726293f17..36ffca70e 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/SubCategoriesFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/SubCategoriesFragment.kt @@ -4,8 +4,7 @@ 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 fr.free.nrw.commons.explore.categories.PageableCategoryFragment import javax.inject.Inject @@ -13,7 +12,7 @@ class SubCategoriesFragment : PageableCategoryFragment() { @Inject lateinit var presenter: SubCategoriesPresenter - override val injectedPresenter: PagingContract.Presenter + override val injectedPresenter get() = presenter override fun getEmptyText(query: String) = getString(R.string.no_subcategory_found) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.java index 688d5073c..e061b2dff 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.java @@ -13,6 +13,7 @@ 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.category.CategoryImagesCallback; 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; @@ -26,7 +27,8 @@ import java.util.List; /** * Activity to show depiction media, parent classes and child classes of depicted items in Explore */ -public class WikidataItemDetailsActivity extends NavigationBaseActivity implements MediaDetailPagerFragment.MediaDetailProvider { +public class WikidataItemDetailsActivity extends NavigationBaseActivity implements MediaDetailPagerFragment.MediaDetailProvider, + CategoryImagesCallback { private FragmentManager supportFragmentManager; private DepictedImagesFragment depictionImagesListFragment; private MediaDetailPagerFragment mediaDetailPagerFragment; @@ -73,13 +75,13 @@ public class WikidataItemDetailsActivity extends NavigationBaseActivity implemen * This method is called on success of API call for featured Images. * The viewpager will notified that number of items have changed. */ + @Override public void viewPagerNotifyDataSetChanged() { if (mediaDetailPagerFragment !=null){ mediaDetailPagerFragment.notifyDataSetChanged(); } } - /** * This activity contains 3 tabs and a viewpager. This method is used to set the titles of tab, * Set the fragments according to the tab selected in the viewPager. diff --git a/app/src/main/java/fr/free/nrw/commons/explore/media/PageableMediaFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/media/PageableMediaFragment.kt index db425ae4a..1c43db691 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/media/PageableMediaFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/media/PageableMediaFragment.kt @@ -14,9 +14,7 @@ import kotlinx.android.synthetic.main.fragment_search_paginated.* abstract class PageableMediaFragment : BasePagingFragment(), MediaDetailProvider { override val pagedListAdapter by lazy { - PagedMediaAdapter { - categoryImagesCallback.onMediaClicked(it) - } + PagedMediaAdapter(categoryImagesCallback::onMediaClicked) } override val errorTextId: Int = R.string.error_loading_images @@ -30,9 +28,8 @@ abstract class PageableMediaFragment : BasePagingFragment(), MediaDetailP categoryImagesCallback = (context as CategoryImagesCallback) } - private val simpleDataObserver = SimpleDataObserver { - categoryImagesCallback.viewPagerNotifyDataSetChanged() - } + private val simpleDataObserver = + SimpleDataObserver { categoryImagesCallback.viewPagerNotifyDataSetChanged() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt b/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt index 2b93beebf..02b28ff36 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt @@ -2,6 +2,7 @@ package fr.free.nrw.commons.media import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.Media +import fr.free.nrw.commons.category.ContinuationClient import fr.free.nrw.commons.explore.media.MediaConverter import fr.free.nrw.commons.utils.CommonsDateUtil import io.reactivex.Single @@ -24,14 +25,10 @@ class MediaClient @Inject constructor( private val pageMediaInterface: PageMediaInterface, private val mediaDetailInterface: MediaDetailInterface, private val mediaConverter: MediaConverter -) { +) : ContinuationClient() { fun getMediaById(id: String) = - responseToMediaList(mediaInterface.getMediaById(id)).map { it.first() } - - //OkHttpJsonApiClient used JsonKvStore for this. I don't know why. - private val continuationStore: MutableMap?> = mutableMapOf() - private val continuationExists: MutableMap = mutableMapOf() + responseMapper(mediaInterface.getMediaById(id)).map { it.first() } /** * Checks if a page exists on Commons @@ -62,18 +59,8 @@ class MediaClient @Inject constructor( * @return */ fun getMediaListFromCategory(category: String): Single> { - val key = "$CATEGORY_CONTINUATION_PREFIX$category" - return if (hasMorePagesFor(key)) { - responseToMediaList( - mediaInterface.getMediaListFromCategory( - category, - 10, - continuationStore[key] ?: emptyMap() - ), - key - ) - } else { - Single.just(emptyList()) + return continuationRequest(CATEGORY_CONTINUATION_PREFIX, category) { + mediaInterface.getMediaListFromCategory(category, 10, it) } } @@ -86,17 +73,11 @@ class MediaClient @Inject constructor( * @return */ fun getMediaListForUser(userName: String): Single> { - return responseToMediaList( - mediaInterface.getMediaListForUser( - userName, - 10, - continuationStore["user_$userName"] ?: Collections.emptyMap() - ), - "user_$userName" - ) + return continuationRequest("user_", userName) { + mediaInterface.getMediaListForUser(userName, 10, it) + } } - /** * This method takes a keyword as input and returns a list of Media objects filtered using image generator query * It uses the generator query API to get the images searched using a query, 10 at a time. @@ -107,7 +88,7 @@ class MediaClient @Inject constructor( * @return */ fun getMediaListFromSearch(keyword: String?, limit: Int, offset: Int) = - responseToMediaList(mediaInterface.getMediaListFromSearch(keyword, limit, offset)) + responseMapper(mediaInterface.getMediaListFromSearch(keyword, limit, offset)) /** * @return list of images for a particular depict entity @@ -117,7 +98,7 @@ class MediaClient @Inject constructor( srlimit: Int, sroffset: Int ): Single> { - return responseToMediaList( + return responseMapper( mediaInterface.fetchImagesForDepictedItem( "haswbstatement:" + BuildConfig.DEPICTS_PROPERTY + "=" + query, srlimit.toString(), @@ -126,23 +107,6 @@ class MediaClient @Inject constructor( ) } - private fun responseToMediaList( - response: Single, - key: String? = null - ): Single> { - return response.map { - if (key != null) { - continuationExists[key] = - it.continuation()?.let { continuation -> - continuationStore[key] = continuation - true - } ?: false - } - it.query()?.pages() ?: emptyList() - }.flatMap(::mediaFromPageAndEntity) - - } - private fun mediaFromPageAndEntity(pages: List): Single> { return if (pages.isEmpty()) Single.just(emptyList()) @@ -165,7 +129,7 @@ class MediaClient @Inject constructor( * @return */ fun getMedia(titles: String?): Single { - return responseToMediaList(mediaInterface.getMedia(titles)) + return responseMapper(mediaInterface.getMedia(titles)) .map { it.first() } } @@ -176,7 +140,7 @@ class MediaClient @Inject constructor( */ fun getPictureOfTheDay(): Single { val date = CommonsDateUtil.getIso8601DateFormatShort().format(Date()) - return responseToMediaList(mediaInterface.getMediaWithGenerator("Template:Potd/$date")).map { it.first() } + return responseMapper(mediaInterface.getMediaWithGenerator("Template:Potd/$date")).map { it.first() } } @@ -193,24 +157,22 @@ class MediaClient @Inject constructor( } - /** - * Check if media for user has reached the end of the list. - * @param userName - * @return - */ - fun doesMediaListForUserHaveMorePages(userName: String): Boolean { - return hasMorePagesFor("user_$userName") - } - - private fun hasMorePagesFor(key: String) = continuationExists[key] ?: true - fun doesPageContainMedia(title: String?): Single { return pageMediaInterface.getMediaList(title) .map { it.items.isNotEmpty() } } fun resetCategoryContinuation(category: String) { - continuationExists.remove("$CATEGORY_CONTINUATION_PREFIX$category") - continuationStore.remove("$CATEGORY_CONTINUATION_PREFIX$category") + resetContinuation(CATEGORY_CONTINUATION_PREFIX, category) + } + + override fun responseMapper( + networkResult: Single, + key: String? + ): Single> { + return networkResult.map { + handleContinuationResponse(it.continuation(), key) + it.query()?.pages() ?: emptyList() + }.flatMap(::mediaFromPageAndEntity) } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java index 3e5f3f0fd..957f45265 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java @@ -13,7 +13,6 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.Toast; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentStatePagerAdapter; @@ -26,7 +25,6 @@ import fr.free.nrw.commons.R; import fr.free.nrw.commons.bookmarks.Bookmark; import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider; import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao; -import fr.free.nrw.commons.category.CategoryImagesCallback; import fr.free.nrw.commons.contributions.Contribution; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.utils.DownloadUtils; @@ -269,8 +267,6 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple Timber.d("Returning as activity is destroyed!"); return; } - if (i+1 >= adapter.getCount() && getContext() instanceof CategoryImagesCallback) - ((CategoryImagesCallback) getContext()).requestMoreImages(); getActivity().invalidateOptionsMenu(); } diff --git a/app/src/main/res/layout/fragment_browse_image.xml b/app/src/main/res/layout/fragment_browse_image.xml deleted file mode 100644 index 8a199034e..000000000 --- a/app/src/main/res/layout/fragment_browse_image.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/categroies/PageableCategoriesDataSourceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/categroies/PageableSearchCategoriesDataSourceTest.kt similarity index 74% rename from app/src/test/kotlin/fr/free/nrw/commons/explore/categroies/PageableCategoriesDataSourceTest.kt rename to app/src/test/kotlin/fr/free/nrw/commons/explore/categroies/PageableSearchCategoriesDataSourceTest.kt index f1f6ded90..160b6f851 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/categroies/PageableCategoriesDataSourceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/categroies/PageableSearchCategoriesDataSourceTest.kt @@ -3,19 +3,19 @@ package fr.free.nrw.commons.explore.categroies 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 fr.free.nrw.commons.explore.categories.search.PageableSearchCategoriesDataSource import io.reactivex.Single import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers import org.junit.Test -class PageableCategoriesDataSourceTest { +class PageableSearchCategoriesDataSourceTest { @Test fun `loadFunction loads categories`() { val categoryClient: CategoryClient = mock() whenever(categoryClient.searchCategories("test", 0, 1)) .thenReturn(Single.just(emptyList())) - val pageableCategoriesDataSource = PageableCategoriesDataSource(mock(), categoryClient) + val pageableCategoriesDataSource = PageableSearchCategoriesDataSource(mock(), categoryClient) pageableCategoriesDataSource.onQueryUpdated("test") assertThat(pageableCategoriesDataSource.loadFunction(0, 1), Matchers.`is`(emptyList())) }