Skip to content

Commit 9d59915

Browse files
authored
#3822 Convert SubCategoryImagesListFragment to use Pagination (#3824)
1 parent 7817518 commit 9d59915

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+444
-477
lines changed

app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarksActivity.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -141,5 +141,7 @@ public int getTotalMediaCount() {
141141
}
142142

143143
@Override
144-
public void requestMoreImages() { }
144+
public void onMediaClicked(int position) {
145+
//TODO use with pagination
146+
}
145147
}

app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ class CategoriesModel @Inject constructor(
9393
else
9494
categoryClient.searchCategoriesForPrefix(term.toLowerCase(), SEARCH_CATS_LIMIT)
9595
.map { it.sortedWith(StringSortingUtils.sortBySimilarity(term)) }
96+
.toObservable()
9697
}
9798

9899
private fun categoriesFromDepiction(selectedDepictions: List<DepictedItem>) =
@@ -126,7 +127,7 @@ class CategoriesModel @Inject constructor(
126127
* @return
127128
*/
128129
private fun getTitleCategories(title: String): Observable<List<String>> {
129-
return categoryClient.searchCategories(title.toLowerCase(), SEARCH_CATS_LIMIT)
130+
return categoryClient.searchCategories(title.toLowerCase(), SEARCH_CATS_LIMIT).toObservable()
130131
}
131132

132133

app/src/main/java/fr/free/nrw/commons/category/CategoryClient.kt

+38-19
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
package fr.free.nrw.commons.category
22

3-
import io.reactivex.Observable
3+
import io.reactivex.Single
44
import org.wikipedia.dataclient.mwapi.MwQueryResponse
55
import javax.inject.Inject
66
import javax.inject.Singleton
77

88
const val CATEGORY_PREFIX = "Category:"
9+
const val SUB_CATEGORY_CONTINUATION_PREFIX = "sub_category_"
10+
const val PARENT_CATEGORY_CONTINUATION_PREFIX = "parent_category_"
11+
912
/**
1013
* Category Client to handle custom calls to Commons MediaWiki APIs
1114
*/
1215
@Singleton
13-
class CategoryClient @Inject constructor(private val categoryInterface: CategoryInterface) {
16+
class CategoryClient @Inject constructor(private val categoryInterface: CategoryInterface) :
17+
ContinuationClient<MwQueryResponse, String>() {
18+
1419
/**
1520
* Searches for categories containing the specified string.
1621
*
@@ -21,8 +26,8 @@ class CategoryClient @Inject constructor(private val categoryInterface: Category
2126
*/
2227
@JvmOverloads
2328
fun searchCategories(filter: String?, itemLimit: Int, offset: Int = 0):
24-
Observable<List<String>> {
25-
return responseToCategoryName(categoryInterface.searchCategories(filter, itemLimit, offset))
29+
Single<List<String>> {
30+
return responseMapper(categoryInterface.searchCategories(filter, itemLimit, offset))
2631
}
2732

2833
/**
@@ -35,8 +40,8 @@ class CategoryClient @Inject constructor(private val categoryInterface: Category
3540
*/
3641
@JvmOverloads
3742
fun searchCategoriesForPrefix(prefix: String?, itemLimit: Int, offset: Int = 0):
38-
Observable<List<String>> {
39-
return responseToCategoryName(
43+
Single<List<String>> {
44+
return responseMapper(
4045
categoryInterface.searchCategoriesForPrefix(prefix, itemLimit, offset)
4146
)
4247
}
@@ -48,8 +53,12 @@ class CategoryClient @Inject constructor(private val categoryInterface: Category
4853
* @param categoryName Category name as defined on commons
4954
* @return Observable emitting the categories returned. If our search yielded "Category:Test", "Test" is emitted.
5055
*/
51-
fun getSubCategoryList(categoryName: String?): Observable<List<String>> {
52-
return responseToCategoryName(categoryInterface.getSubCategoryList(categoryName))
56+
fun getSubCategoryList(categoryName: String): Single<List<String>> {
57+
return continuationRequest(SUB_CATEGORY_CONTINUATION_PREFIX, categoryName) {
58+
categoryInterface.getSubCategoryList(
59+
categoryName, it
60+
)
61+
}
5362
}
5463

5564
/**
@@ -59,19 +68,29 @@ class CategoryClient @Inject constructor(private val categoryInterface: Category
5968
* @param categoryName Category name as defined on commons
6069
* @return
6170
*/
62-
fun getParentCategoryList(categoryName: String?): Observable<List<String>> {
63-
return responseToCategoryName(categoryInterface.getParentCategoryList(categoryName))
71+
fun getParentCategoryList(categoryName: String): Single<List<String>> {
72+
return continuationRequest(PARENT_CATEGORY_CONTINUATION_PREFIX, categoryName) {
73+
categoryInterface.getParentCategoryList(categoryName, it)
74+
}
6475
}
6576

66-
/**
67-
* Internal function to reduce code reuse. Extracts the categories returned from MwQueryResponse.
68-
*
69-
* @param responseObservable The query response observable
70-
* @return Observable emitting the categories returned. If our search yielded "Category:Test", "Test" is emitted.
71-
*/
72-
private fun responseToCategoryName(responseObservable: Observable<MwQueryResponse>): Observable<List<String>> {
73-
return responseObservable
74-
.map { it.query()?.pages() ?: emptyList() }
77+
fun resetSubCategoryContinuation(category: String) {
78+
resetContinuation(SUB_CATEGORY_CONTINUATION_PREFIX, category)
79+
}
80+
81+
fun resetParentCategoryContinuation(category: String) {
82+
resetContinuation(PARENT_CATEGORY_CONTINUATION_PREFIX, category)
83+
}
84+
85+
override fun responseMapper(
86+
networkResult: Single<MwQueryResponse>,
87+
key: String?
88+
): Single<List<String>> {
89+
return networkResult
90+
.map {
91+
handleContinuationResponse(it.continuation(), key)
92+
it.query()?.pages() ?: emptyList()
93+
}
7594
.map {
7695
it.map { page -> page.title().replace(CATEGORY_PREFIX, "") }
7796
}

app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java

+6-17
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import fr.free.nrw.commons.Utils;
2121
import fr.free.nrw.commons.explore.ViewPagerAdapter;
2222
import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment;
23+
import fr.free.nrw.commons.explore.categories.parent.ParentCategoriesFragment;
24+
import fr.free.nrw.commons.explore.categories.sub.SubCategoriesFragment;
2325
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
2426
import fr.free.nrw.commons.theme.NavigationBaseActivity;
2527
import java.util.ArrayList;
@@ -69,25 +71,21 @@ private void setTabs() {
6971
List<Fragment> fragmentList = new ArrayList<>();
7072
List<String> titleList = new ArrayList<>();
7173
categoriesMediaFragment = new CategoriesMediaFragment();
72-
SubCategoryListFragment subCategoryListFragment = new SubCategoryListFragment();
73-
SubCategoryListFragment parentCategoryListFragment = new SubCategoryListFragment();
74+
SubCategoriesFragment subCategoryListFragment = new SubCategoriesFragment();
75+
ParentCategoriesFragment parentCategoriesFragment = new ParentCategoriesFragment();
7476
categoryName = getIntent().getStringExtra("categoryName");
7577
if (getIntent() != null && categoryName != null) {
7678
Bundle arguments = new Bundle();
7779
arguments.putString("categoryName", categoryName);
78-
arguments.putBoolean("isParentCategory", false);
7980
categoriesMediaFragment.setArguments(arguments);
8081
subCategoryListFragment.setArguments(arguments);
81-
Bundle parentCategoryArguments = new Bundle();
82-
parentCategoryArguments.putString("categoryName", categoryName);
83-
parentCategoryArguments.putBoolean("isParentCategory", true);
84-
parentCategoryListFragment.setArguments(parentCategoryArguments);
82+
parentCategoriesFragment.setArguments(arguments);
8583
}
8684
fragmentList.add(categoriesMediaFragment);
8785
titleList.add("MEDIA");
8886
fragmentList.add(subCategoryListFragment);
8987
titleList.add("SUBCATEGORIES");
90-
fragmentList.add(parentCategoryListFragment);
88+
fragmentList.add(parentCategoriesFragment);
9189
titleList.add("PARENT CATEGORIES");
9290
viewPagerAdapter.setTabData(fragmentList, titleList);
9391
viewPagerAdapter.notifyDataSetChanged();
@@ -133,7 +131,6 @@ public void onMediaClicked(int position) {
133131
*/
134132
public static void startYourself(Context context, String categoryName) {
135133
Intent intent = new Intent(context, CategoryDetailsActivity.class);
136-
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
137134
intent.putExtra("categoryName", categoryName);
138135
context.startActivity(intent);
139136
}
@@ -212,12 +209,4 @@ public void viewPagerNotifyDataSetChanged() {
212209
}
213210
}
214211

215-
/**
216-
* This method is called when viewPager has reached its end.
217-
* Fetches more images using search query and adds it to the grid view and viewpager adapter
218-
*/
219-
@Override
220-
public void requestMoreImages() {
221-
//unneeded
222-
}
223212
}

app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java

+2-6
Original file line numberDiff line numberDiff line change
@@ -187,12 +187,8 @@ public boolean onOptionsItemSelected(MenuItem item) {
187187
}
188188
}
189189

190-
/**
191-
* This method is called when viewPager has reached its end.
192-
* Fetches more images using search query and adds it to the gridView and viewpager adapter
193-
*/
194190
@Override
195-
public void requestMoreImages() {
196-
//unneeded
191+
public void onMediaClicked(int position) {
192+
// this class is unused and will be deleted
197193
}
198194
}

app/src/main/java/fr/free/nrw/commons/category/CategoryImagesCallback.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
public interface CategoryImagesCallback {
99
void viewPagerNotifyDataSetChanged();
10-
void requestMoreImages();
10+
void onMediaClicked(int position);
1111
}
1212

1313

app/src/main/java/fr/free/nrw/commons/category/CategoryInterface.java

+11-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package fr.free.nrw.commons.category;
22

3+
import io.reactivex.Single;
4+
import java.util.Map;
35
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
4-
5-
import io.reactivex.Observable;
66
import retrofit2.http.GET;
77
import retrofit2.http.Query;
8+
import retrofit2.http.QueryMap;
89

910
/**
1011
* Interface for interacting with Commons category related APIs
@@ -20,7 +21,7 @@ public interface CategoryInterface {
2021
*/
2122
@GET("w/api.php?action=query&format=json&formatversion=2"
2223
+ "&generator=search&gsrnamespace=14")
23-
Observable<MwQueryResponse> searchCategories(@Query("gsrsearch") String filter,
24+
Single<MwQueryResponse> searchCategories(@Query("gsrsearch") String filter,
2425
@Query("gsrlimit") int itemLimit, @Query("gsroffset") int offset);
2526

2627
/**
@@ -32,16 +33,18 @@ Observable<MwQueryResponse> searchCategories(@Query("gsrsearch") String filter,
3233
*/
3334
@GET("w/api.php?action=query&format=json&formatversion=2"
3435
+ "&generator=allcategories")
35-
Observable<MwQueryResponse> searchCategoriesForPrefix(@Query("gacprefix") String prefix,
36+
Single<MwQueryResponse> searchCategoriesForPrefix(@Query("gacprefix") String prefix,
3637
@Query("gaclimit") int itemLimit, @Query("gacoffset") int offset);
3738

3839
@GET("w/api.php?action=query&format=json&formatversion=2"
3940
+ "&generator=categorymembers&gcmtype=subcat"
40-
+ "&prop=info&gcmlimit=500")
41-
Observable<MwQueryResponse> getSubCategoryList(@Query("gcmtitle") String categoryName);
41+
+ "&prop=info&gcmlimit=50")
42+
Single<MwQueryResponse> getSubCategoryList(@Query("gcmtitle") String categoryName,
43+
@QueryMap(encoded = true) Map<String, String> continuation);
4244

4345
@GET("w/api.php?action=query&format=json&formatversion=2"
44-
+ "&generator=categories&prop=info&gcllimit=500")
45-
Observable<MwQueryResponse> getParentCategoryList(@Query("titles") String categoryName);
46+
+ "&generator=categories&prop=info&gcllimit=50")
47+
Single<MwQueryResponse> getParentCategoryList(@Query("titles") String categoryName,
48+
@QueryMap(encoded = true) Map<String, String> continuation);
4649

4750
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package fr.free.nrw.commons.category
2+
3+
import io.reactivex.Single
4+
5+
6+
abstract class ContinuationClient<Network, Domain> {
7+
private val continuationStore: MutableMap<String, Map<String, String>?> = mutableMapOf()
8+
private val continuationExists: MutableMap<String, Boolean> = mutableMapOf()
9+
10+
private fun hasMorePagesFor(key: String) = continuationExists[key] ?: true
11+
fun continuationRequest(
12+
prefix: String,
13+
name: String,
14+
requestFunction: (Map<String, String>) -> Single<Network>
15+
): Single<List<Domain>> {
16+
val key = "$prefix$name"
17+
return if (hasMorePagesFor(key)) {
18+
responseMapper(requestFunction(continuationStore[key] ?: emptyMap()), key)
19+
} else {
20+
Single.just(emptyList())
21+
}
22+
}
23+
24+
abstract fun responseMapper(networkResult: Single<Network>, key: String?=null): Single<List<Domain>>
25+
26+
fun handleContinuationResponse(continuation:Map<String,String>?, key:String?){
27+
if (key != null) {
28+
continuationExists[key] =
29+
continuation?.let { continuation ->
30+
continuationStore[key] = continuation
31+
true
32+
} ?: false
33+
}
34+
}
35+
36+
protected fun resetContinuation(prefix: String, category: String) {
37+
continuationExists.remove("$prefix$category")
38+
continuationStore.remove("$prefix$category")
39+
}
40+
41+
}

0 commit comments

Comments
 (0)