mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 12:53:55 +01:00
parent
e597a7c96f
commit
4690925cf5
22 changed files with 593 additions and 888 deletions
|
|
@ -1,242 +0,0 @@
|
|||
package fr.free.nrw.commons.category;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.upload.GpsCategoryModel;
|
||||
import fr.free.nrw.commons.utils.StringSortingUtils;
|
||||
import io.reactivex.Observable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* The model class for categories in upload
|
||||
*/
|
||||
public class CategoriesModel{
|
||||
private static final int SEARCH_CATS_LIMIT = 25;
|
||||
|
||||
private final CategoryClient categoryClient;
|
||||
private final CategoryDao categoryDao;
|
||||
private final JsonKvStore directKvStore;
|
||||
private final GpsCategoryModel gpsCategoryModel;
|
||||
|
||||
private List<CategoryItem> selectedCategories;
|
||||
|
||||
@Inject
|
||||
public CategoriesModel(CategoryClient categoryClient,
|
||||
CategoryDao categoryDao,
|
||||
@Named("default_preferences") JsonKvStore directKvStore,
|
||||
final GpsCategoryModel gpsCategoryModel) {
|
||||
this.categoryClient = categoryClient;
|
||||
this.categoryDao = categoryDao;
|
||||
this.directKvStore = directKvStore;
|
||||
this.gpsCategoryModel = gpsCategoryModel;
|
||||
this.selectedCategories = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts CategoryItem by similarity
|
||||
* @param filter
|
||||
* @return
|
||||
*/
|
||||
public Comparator<CategoryItem> sortBySimilarity(final String filter) {
|
||||
Comparator<String> stringSimilarityComparator = StringSortingUtils.sortBySimilarity(filter);
|
||||
return (firstItem, secondItem) -> stringSimilarityComparator
|
||||
.compare(firstItem.getName(), secondItem.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the item contains an year
|
||||
* @param item
|
||||
* @return
|
||||
*/
|
||||
public boolean containsYear(String item) {
|
||||
//Check for current and previous year to exclude these categories from removal
|
||||
Calendar now = Calendar.getInstance();
|
||||
int year = now.get(Calendar.YEAR);
|
||||
String yearInString = String.valueOf(year);
|
||||
|
||||
int prevYear = year - 1;
|
||||
String prevYearInString = String.valueOf(prevYear);
|
||||
Timber.d("Previous year: %s", prevYearInString);
|
||||
|
||||
//Check if item contains a 4-digit word anywhere within the string (.* is wildcard)
|
||||
//And that item does not equal the current year or previous year
|
||||
//And if it is an irrelevant category such as Media_needing_categories_as_of_16_June_2017(Issue #750)
|
||||
//Check if the year in the form of XX(X)0s is relevant, i.e. in the 2000s or 2010s as stated in Issue #1029
|
||||
return ((item.matches(".*(19|20)\\d{2}.*") && !item.contains(yearInString) && !item.contains(prevYearInString))
|
||||
|| item.matches("(.*)needing(.*)") || item.matches("(.*)taken on(.*)")
|
||||
|| (item.matches(".*0s.*") && !item.matches(".*(200|201)0s.*")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates category count in category dao
|
||||
* @param item
|
||||
*/
|
||||
public void updateCategoryCount(CategoryItem item) {
|
||||
Category category = categoryDao.find(item.getName());
|
||||
|
||||
// Newly used category...
|
||||
if (category == null) {
|
||||
category = new Category(null, item.getName(), new Date(), 0);
|
||||
}
|
||||
|
||||
category.incTimesUsed();
|
||||
categoryDao.save(category);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Regional category search
|
||||
* @param term
|
||||
* @param imageTitleList
|
||||
* @return
|
||||
*/
|
||||
public Observable<CategoryItem> searchAll(String term, List<String> imageTitleList) {
|
||||
//If query text is empty, show him category based on gps and title and recent searches
|
||||
if (TextUtils.isEmpty(term)) {
|
||||
Observable<CategoryItem> categoryItemObservable =
|
||||
Observable.concat(gpsCategories(), titleCategories(imageTitleList));
|
||||
if (hasDirectCategories()) {
|
||||
return Observable.concat(
|
||||
categoryItemObservable,
|
||||
directCategories(),
|
||||
recentCategories()
|
||||
);
|
||||
}
|
||||
return categoryItemObservable;
|
||||
}
|
||||
|
||||
//otherwise, search API for matching categories
|
||||
//term passed as lower case to make search case-insensitive(taking only lower case for everything)
|
||||
return categoryClient
|
||||
.searchCategoriesForPrefix(term.toLowerCase(), SEARCH_CATS_LIMIT)
|
||||
.map(name -> new CategoryItem(name, false));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns if we have a category in DirectKV Store
|
||||
* @return
|
||||
*/
|
||||
private boolean hasDirectCategories() {
|
||||
return !directKvStore.getString("Category", "").equals("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns categories in DirectKVStore
|
||||
* @return
|
||||
*/
|
||||
private Observable<CategoryItem> directCategories() {
|
||||
String directCategory = directKvStore.getString("Category", "");
|
||||
List<String> categoryList = new ArrayList<>();
|
||||
Timber.d("Direct category found: " + directCategory);
|
||||
|
||||
if (!directCategory.equals("")) {
|
||||
categoryList.add(directCategory);
|
||||
Timber.d("DirectCat does not equal emptyString. Direct Cat list has " + categoryList);
|
||||
}
|
||||
return Observable.fromIterable(categoryList).map(name -> new CategoryItem(name, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns GPS categories
|
||||
* @return
|
||||
*/
|
||||
Observable<CategoryItem> gpsCategories() {
|
||||
return Observable.fromIterable(gpsCategoryModel.getCategoryList())
|
||||
.map(name -> new CategoryItem(name, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns title based categories
|
||||
* @param titleList
|
||||
* @return
|
||||
*/
|
||||
private Observable<CategoryItem> titleCategories(List<String> titleList) {
|
||||
return Observable.fromIterable(titleList)
|
||||
.concatMap(this::getTitleCategories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return category for single title
|
||||
* title is converted to lower case to make search case-insensitive
|
||||
* @param title
|
||||
* @return
|
||||
*/
|
||||
private Observable<CategoryItem> getTitleCategories(String title) {
|
||||
return categoryClient.searchCategories(title.toLowerCase(), SEARCH_CATS_LIMIT)
|
||||
.map(name -> new CategoryItem(name, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns recent categories
|
||||
* @return
|
||||
*/
|
||||
private Observable<CategoryItem> recentCategories() {
|
||||
return Observable.fromIterable(categoryDao.recentCategories(SEARCH_CATS_LIMIT))
|
||||
.map(s -> new CategoryItem(s, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles category item selection
|
||||
* @param item
|
||||
*/
|
||||
public void onCategoryItemClicked(CategoryItem item) {
|
||||
if (item.isSelected()) {
|
||||
selectCategory(item);
|
||||
updateCategoryCount(item);
|
||||
} else {
|
||||
unselectCategory(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select's category
|
||||
* @param item
|
||||
*/
|
||||
public void selectCategory(CategoryItem item) {
|
||||
selectedCategories.add(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unselect Category
|
||||
* @param item
|
||||
*/
|
||||
public void unselectCategory(CategoryItem item) {
|
||||
selectedCategories.remove(item);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get Selected Categories
|
||||
* @return
|
||||
*/
|
||||
public List<CategoryItem> getSelectedCategories() {
|
||||
return selectedCategories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Categories String List
|
||||
* @return
|
||||
*/
|
||||
public List<String> getCategoryStringList() {
|
||||
List<String> output = new ArrayList<>();
|
||||
for (CategoryItem item : selectedCategories) {
|
||||
output.add(item.getName());
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup the existing in memory cache's
|
||||
*/
|
||||
public void cleanUp() {
|
||||
this.selectedCategories.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
package fr.free.nrw.commons.category
|
||||
|
||||
import android.text.TextUtils
|
||||
import fr.free.nrw.commons.upload.GpsCategoryModel
|
||||
import fr.free.nrw.commons.utils.StringSortingUtils
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.functions.Function3
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* The model class for categories in upload
|
||||
*/
|
||||
class CategoriesModel @Inject constructor(
|
||||
private val categoryClient: CategoryClient,
|
||||
private val categoryDao: CategoryDao,
|
||||
private val gpsCategoryModel: GpsCategoryModel
|
||||
) {
|
||||
private val selectedCategories: MutableList<CategoryItem> = mutableListOf()
|
||||
|
||||
/**
|
||||
* Returns if the item contains an year
|
||||
* @param item
|
||||
* @return
|
||||
*/
|
||||
fun containsYear(item: String): Boolean {
|
||||
//Check for current and previous year to exclude these categories from removal
|
||||
val now = Calendar.getInstance()
|
||||
val year = now[Calendar.YEAR]
|
||||
val yearInString = year.toString()
|
||||
val prevYear = year - 1
|
||||
val prevYearInString = prevYear.toString()
|
||||
Timber.d("Previous year: %s", prevYearInString)
|
||||
|
||||
//Check if item contains a 4-digit word anywhere within the string (.* is wildcard)
|
||||
//And that item does not equal the current year or previous year
|
||||
//And if it is an irrelevant category such as Media_needing_categories_as_of_16_June_2017(Issue #750)
|
||||
//Check if the year in the form of XX(X)0s is relevant, i.e. in the 2000s or 2010s as stated in Issue #1029
|
||||
return item.matches(".*(19|20)\\d{2}.*".toRegex())
|
||||
&& !item.contains(yearInString)
|
||||
&& !item.contains(prevYearInString)
|
||||
|| item.matches("(.*)needing(.*)".toRegex())
|
||||
|| item.matches("(.*)taken on(.*)".toRegex())
|
||||
|| item.matches(".*0s.*".toRegex())
|
||||
&& !item.matches(".*(200|201)0s.*".toRegex())
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates category count in category dao
|
||||
* @param item
|
||||
*/
|
||||
fun updateCategoryCount(item: CategoryItem) {
|
||||
var category = categoryDao.find(item.name)
|
||||
|
||||
// Newly used category...
|
||||
if (category == null) {
|
||||
category = Category(null, item.name, Date(), 0)
|
||||
}
|
||||
category.incTimesUsed()
|
||||
categoryDao.save(category)
|
||||
}
|
||||
|
||||
/**
|
||||
* Regional category search
|
||||
* @param term
|
||||
* @param imageTitleList
|
||||
* @return
|
||||
*/
|
||||
fun searchAll(term: String, imageTitleList: List<String>): Observable<List<CategoryItem>> {
|
||||
return suggestionsOrSearch(term, imageTitleList)
|
||||
.map { it.map { CategoryItem(it, false) } }
|
||||
}
|
||||
|
||||
private fun suggestionsOrSearch(term: String, imageTitleList: List<String>):
|
||||
Observable<List<String>> {
|
||||
return if (TextUtils.isEmpty(term))
|
||||
Observable.combineLatest(
|
||||
gpsCategoryModel.categoriesFromLocation,
|
||||
titleCategories(imageTitleList),
|
||||
Observable.just(categoryDao.recentCategories(SEARCH_CATS_LIMIT)),
|
||||
Function3(::combine)
|
||||
)
|
||||
else
|
||||
categoryClient.searchCategoriesForPrefix(term.toLowerCase(), SEARCH_CATS_LIMIT)
|
||||
.map { it.sortedWith(StringSortingUtils.sortBySimilarity(term)) }
|
||||
}
|
||||
|
||||
private fun combine(
|
||||
locationCategories: List<String>,
|
||||
titles: List<String>,
|
||||
recents: List<String>
|
||||
) = locationCategories + titles + recents
|
||||
|
||||
|
||||
/**
|
||||
* Returns title based categories
|
||||
* @param titleList
|
||||
* @return
|
||||
*/
|
||||
private fun titleCategories(titleList: List<String>): Observable<List<String>> {
|
||||
return if (titleList.isNotEmpty())
|
||||
Observable.combineLatest(titleList.map { getTitleCategories(it) }) { searchResults ->
|
||||
searchResults.map { it as List<String> }.flatten()
|
||||
}
|
||||
else
|
||||
Observable.just(emptyList())
|
||||
}
|
||||
|
||||
/**
|
||||
* Return category for single title
|
||||
* title is converted to lower case to make search case-insensitive
|
||||
* @param title
|
||||
* @return
|
||||
*/
|
||||
private fun getTitleCategories(title: String): Observable<List<String>> {
|
||||
return categoryClient.searchCategories(title.toLowerCase(), SEARCH_CATS_LIMIT)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles category item selection
|
||||
* @param item
|
||||
*/
|
||||
fun onCategoryItemClicked(item: CategoryItem) {
|
||||
if (item.isSelected) {
|
||||
selectedCategories.add(item)
|
||||
updateCategoryCount(item)
|
||||
} else {
|
||||
selectedCategories.remove(item)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Selected Categories
|
||||
* @return
|
||||
*/
|
||||
fun getSelectedCategories(): List<CategoryItem> {
|
||||
return selectedCategories
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup the existing in memory cache's
|
||||
*/
|
||||
fun cleanUp() {
|
||||
selectedCategories.clear()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SEARCH_CATS_LIMIT = 25
|
||||
}
|
||||
}
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
package fr.free.nrw.commons.category;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryPage;
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryResult;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The method takes categoryName as input and returns a List of Subcategories
|
||||
* It uses the generator query API to get the subcategories in a category, 500 at a time.
|
||||
*
|
||||
* @param categoryName Category name as defined on commons
|
||||
* @return Observable emitting the categories returned. If our search yielded "Category:Test", "Test" is emitted.
|
||||
*/
|
||||
public Observable<String> getSubCategoryList(String categoryName) {
|
||||
return responseToCategoryName(CategoryInterface.getSubCategoryList(categoryName));
|
||||
}
|
||||
|
||||
/**
|
||||
* The method takes categoryName as input and returns a List of parent categories
|
||||
* It uses the generator query API to get the parent categories of a category, 500 at a time.
|
||||
*
|
||||
* @param categoryName Category name as defined on commons
|
||||
* @return
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<String> getParentCategoryList(String categoryName) {
|
||||
return responseToCategoryName(CategoryInterface.getParentCategoryList(categoryName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 -> {
|
||||
MwQueryResult query;
|
||||
List<MwQueryPage> pages;
|
||||
if ((query = mwQueryResponse.query()) == null ||
|
||||
(pages = query.pages()) == null) {
|
||||
Timber.d("No categories returned.");
|
||||
return Observable.empty();
|
||||
} else
|
||||
return Observable.fromIterable(pages);
|
||||
})
|
||||
.map(MwQueryPage::title)
|
||||
.doOnEach(s -> Timber.d("Category returned: %s", s))
|
||||
.map(cat -> cat.replace("Category:", ""));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package fr.free.nrw.commons.category
|
||||
|
||||
import io.reactivex.Observable
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryResponse
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Category Client to handle custom calls to Commons MediaWiki APIs
|
||||
*/
|
||||
@Singleton
|
||||
class CategoryClient @Inject constructor(private val 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
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun searchCategories(filter: String?, itemLimit: Int, offset: Int = 0):
|
||||
Observable<List<String>> {
|
||||
return responseToCategoryName(categoryInterface.searchCategories(filter, itemLimit, offset))
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun searchCategoriesForPrefix(prefix: String?, itemLimit: Int, offset: Int = 0):
|
||||
Observable<List<String>> {
|
||||
return responseToCategoryName(
|
||||
categoryInterface.searchCategoriesForPrefix(prefix, itemLimit, offset)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The method takes categoryName as input and returns a List of Subcategories
|
||||
* It uses the generator query API to get the subcategories in a category, 500 at a time.
|
||||
*
|
||||
* @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))
|
||||
}
|
||||
|
||||
/**
|
||||
* The method takes categoryName as input and returns a List of parent categories
|
||||
* It uses the generator query API to get the parent categories of a category, 500 at a time.
|
||||
*
|
||||
* @param categoryName Category name as defined on commons
|
||||
* @return
|
||||
*/
|
||||
fun getParentCategoryList(categoryName: String?): Observable<List<String>> {
|
||||
return responseToCategoryName(categoryInterface.getParentCategoryList(categoryName))
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: Observable<MwQueryResponse>): Observable<List<String>> {
|
||||
return responseObservable
|
||||
.map { it.query()?.pages() ?: emptyList() }
|
||||
.map {
|
||||
it.map { page -> page.title().replace("Category:", "") }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
package fr.free.nrw.commons.category;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class CategoryItem implements Parcelable {
|
||||
private final String name;
|
||||
private boolean selected;
|
||||
|
||||
public static Creator<CategoryItem> CREATOR = new Creator<CategoryItem>() {
|
||||
@Override
|
||||
public CategoryItem createFromParcel(Parcel parcel) {
|
||||
return new CategoryItem(parcel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CategoryItem[] newArray(int i) {
|
||||
return new CategoryItem[0];
|
||||
}
|
||||
};
|
||||
|
||||
public CategoryItem(String name, boolean selected) {
|
||||
this.name = name;
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
private CategoryItem(Parcel in) {
|
||||
name = in.readString();
|
||||
selected = in.readInt() == 1;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public void setSelected(boolean selected) {
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
parcel.writeString(name);
|
||||
parcel.writeInt(selected ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CategoryItem that = (CategoryItem) o;
|
||||
|
||||
return name.equals(that.name);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CategoryItem: '" + name + '\'';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package fr.free.nrw.commons.category
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class CategoryItem(val name: String, var isSelected: Boolean) : Parcelable {
|
||||
|
||||
override fun toString(): String {
|
||||
return "CategoryItem: '$name'"
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as CategoryItem
|
||||
|
||||
if (name != other.name) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return name.hashCode()
|
||||
}
|
||||
}
|
||||
|
|
@ -91,13 +91,11 @@ public class SubCategoryListFragment extends CommonsDaggerSupportFragment {
|
|||
compositeDisposable.add(categoryClient.getParentCategoryList("Category:"+categoryName)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.collect(ArrayList<String>::new, ArrayList::add)
|
||||
.subscribe(this::handleSuccess, this::handleError));
|
||||
} else {
|
||||
compositeDisposable.add(categoryClient.getSubCategoryList("Category:"+categoryName)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.collect(ArrayList<String>::new, ArrayList::add)
|
||||
.subscribe(this::handleSuccess, this::handleError));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,7 +133,6 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment {
|
|||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSubscribe(disposable -> saveQuery(query))
|
||||
.collect(ArrayList<String>::new, ArrayList::add)
|
||||
.subscribe(this::handleSuccess, this::handleError));
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +149,6 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment {
|
|||
compositeDisposable.add(categoryClient.searchCategories(query,25, queryList.size())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.collect(ArrayList<String>::new, ArrayList::add)
|
||||
.subscribe(this::handlePaginationSuccess, this::handleError));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import io.reactivex.Flowable;
|
|||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import java.io.IOException;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.inject.Inject;
|
||||
|
|
@ -111,20 +110,10 @@ public class UploadRepository {
|
|||
* @param imageTitleList
|
||||
* @return
|
||||
*/
|
||||
public Observable<CategoryItem> searchAll(String query, List<String> imageTitleList) {
|
||||
public Observable<List<CategoryItem>> searchAll(String query, List<String> imageTitleList) {
|
||||
return categoriesModel.searchAll(query, imageTitleList);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the string list of categories
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
public List<String> getCategoryStringList() {
|
||||
return categoriesModel.getCategoryStringList();
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the list of selected categories for the current upload
|
||||
*
|
||||
|
|
@ -143,16 +132,6 @@ public class UploadRepository {
|
|||
categoriesModel.onCategoryItemClicked(categoryItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns category sorted based on similarity with query
|
||||
*
|
||||
* @param query
|
||||
* @return
|
||||
*/
|
||||
public Comparator<? super CategoryItem> sortBySimilarity(String query) {
|
||||
return categoriesModel.sortBySimilarity(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* prunes the category list for irrelevant categories see #750
|
||||
*
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ class FileProcessor @Inject constructor(
|
|||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe(
|
||||
{ gpsCategoryModel.categoryList = it },
|
||||
gpsCategoryModel::setCategoriesFromLocation,
|
||||
{
|
||||
Timber.e(it)
|
||||
gpsCategoryModel.clear()
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class GpsCategoryModel {
|
||||
private Set<String> categorySet;
|
||||
|
||||
@Inject
|
||||
public GpsCategoryModel() {
|
||||
clear();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
categorySet = new HashSet<>();
|
||||
}
|
||||
|
||||
public boolean getGpsCatExists() {
|
||||
return !categorySet.isEmpty();
|
||||
}
|
||||
|
||||
public List<String> getCategoryList() {
|
||||
return new ArrayList<>(categorySet);
|
||||
}
|
||||
|
||||
public void setCategoryList(List<String> categoryList) {
|
||||
clear();
|
||||
categorySet.addAll(categoryList != null ? categoryList : new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package fr.free.nrw.commons.upload
|
||||
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class GpsCategoryModel @Inject constructor() {
|
||||
val categoriesFromLocation = BehaviorSubject.createDefault(emptyList<String>())
|
||||
|
||||
fun clear() {
|
||||
categoriesFromLocation.onNext(emptyList())
|
||||
}
|
||||
|
||||
fun setCategoriesFromLocation(categoryList: List<String>) {
|
||||
categoriesFromLocation.onNext(categoryList)
|
||||
}
|
||||
}
|
||||
|
|
@ -30,7 +30,6 @@ import fr.free.nrw.commons.CommonsApplication;
|
|||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.auth.LoginActivity;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.category.CategoriesModel;
|
||||
import fr.free.nrw.commons.contributions.ContributionController;
|
||||
import fr.free.nrw.commons.filepicker.UploadableFile;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
|
|
@ -63,8 +62,6 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
@Inject
|
||||
UploadContract.UserActionListener presenter;
|
||||
@Inject
|
||||
CategoriesModel categoriesModel;
|
||||
@Inject
|
||||
SessionManager sessionManager;
|
||||
@Inject
|
||||
UserClient userClient;
|
||||
|
|
@ -96,7 +93,7 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
private CompositeDisposable compositeDisposable;
|
||||
private ProgressDialog progressDialog;
|
||||
private UploadImageAdapter uploadImagesAdapter;
|
||||
private List<Fragment> fragments;
|
||||
private List<UploadBaseFragment> fragments;
|
||||
private UploadCategoriesFragment uploadCategoriesFragment;
|
||||
private DepictsFragment depictsFragment;
|
||||
private MediaLicenseFragment mediaLicenseFragment;
|
||||
|
|
@ -416,6 +413,7 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
public void onNextButtonClicked(int index) {
|
||||
if (index < fragments.size() - 1) {
|
||||
vpUpload.setCurrentItem(index + 1, false);
|
||||
fragments.get(index + 1).onBecameVisible();
|
||||
} else {
|
||||
presenter.handleSubmit();
|
||||
}
|
||||
|
|
@ -425,6 +423,7 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
public void onPreviousButtonClicked(int index) {
|
||||
if (index != 0) {
|
||||
vpUpload.setCurrentItem(index - 1, true);
|
||||
fragments.get(index - 1).onBecameVisible();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -433,14 +432,14 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
*/
|
||||
|
||||
private class UploadImageAdapter extends FragmentStatePagerAdapter {
|
||||
List<Fragment> fragments;
|
||||
List<UploadBaseFragment> fragments;
|
||||
|
||||
public UploadImageAdapter(FragmentManager fragmentManager) {
|
||||
super(fragmentManager);
|
||||
this.fragments = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void setFragments(List<Fragment> fragments) {
|
||||
public void setFragments(List<UploadBaseFragment> fragments) {
|
||||
this.fragments = fragments;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ public class UploadBaseFragment extends CommonsDaggerSupportFragment {
|
|||
this.callback = callback;
|
||||
}
|
||||
|
||||
protected void onBecameVisible() {
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
|
||||
void onNextButtonClicked(int index);
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ public abstract class UploadModule {
|
|||
presenter);
|
||||
|
||||
@Binds
|
||||
public abstract CategoriesContract.UserActionListener bindsCategoriesPresenter(CategoriesPresenter
|
||||
presenter);
|
||||
public abstract CategoriesContract.UserActionListener bindsCategoriesPresenter(
|
||||
CategoriesPresenter presenter);
|
||||
|
||||
@Binds
|
||||
public abstract MediaLicenseContract.UserActionListener bindsMediaLicensePresenter(
|
||||
|
|
|
|||
|
|
@ -1,146 +0,0 @@
|
|||
package fr.free.nrw.commons.upload.categories;
|
||||
|
||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD;
|
||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.MAIN_THREAD;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.category.CategoryItem;
|
||||
import fr.free.nrw.commons.repository.UploadRepository;
|
||||
import fr.free.nrw.commons.upload.UploadItem;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* The presenter class for UploadCategoriesFragment
|
||||
*/
|
||||
@Singleton
|
||||
public class CategoriesPresenter implements CategoriesContract.UserActionListener {
|
||||
|
||||
private static final CategoriesContract.View DUMMY = (CategoriesContract.View) Proxy
|
||||
.newProxyInstance(
|
||||
CategoriesContract.View.class.getClassLoader(),
|
||||
new Class[]{CategoriesContract.View.class},
|
||||
(proxy, method, methodArgs) -> null);
|
||||
private final Scheduler ioScheduler;
|
||||
private final Scheduler mainThreadScheduler;
|
||||
|
||||
CategoriesContract.View view = DUMMY;
|
||||
private UploadRepository repository;
|
||||
|
||||
private CompositeDisposable compositeDisposable;
|
||||
|
||||
@Inject
|
||||
public CategoriesPresenter(UploadRepository repository, @Named(IO_THREAD) Scheduler ioScheduler,
|
||||
@Named(MAIN_THREAD) Scheduler mainThreadScheduler) {
|
||||
this.repository = repository;
|
||||
this.ioScheduler = ioScheduler;
|
||||
this.mainThreadScheduler = mainThreadScheduler;
|
||||
compositeDisposable = new CompositeDisposable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachView(CategoriesContract.View view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachView() {
|
||||
this.view = DUMMY;
|
||||
compositeDisposable.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* asks the repository to fetch categories for the query
|
||||
* @param query
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void searchForCategories(String query) {
|
||||
List<CategoryItem> categoryItems = new ArrayList<>();
|
||||
List<String> imageTitleList = getImageTitleList();
|
||||
Observable<CategoryItem> distinctCategoriesObservable = Observable
|
||||
.fromIterable(repository.getSelectedCategories())
|
||||
.subscribeOn(ioScheduler)
|
||||
.observeOn(mainThreadScheduler)
|
||||
.doOnSubscribe(disposable -> {
|
||||
view.showProgress(true);
|
||||
view.showError(null);
|
||||
view.setCategories(null);
|
||||
})
|
||||
.observeOn(ioScheduler)
|
||||
.concatWith(
|
||||
repository.searchAll(query, imageTitleList)
|
||||
)
|
||||
.filter(categoryItem -> !repository.containsYear(categoryItem.getName()))
|
||||
.distinct();
|
||||
|
||||
if(!TextUtils.isEmpty(query)) {
|
||||
distinctCategoriesObservable=distinctCategoriesObservable.sorted(repository.sortBySimilarity(query));
|
||||
}
|
||||
Disposable searchCategoriesDisposable = distinctCategoriesObservable
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribe(
|
||||
s -> categoryItems.add(s),
|
||||
Timber::e,
|
||||
() -> {
|
||||
view.setCategories(categoryItems);
|
||||
view.showProgress(false);
|
||||
|
||||
if (categoryItems.isEmpty()) {
|
||||
view.showError(R.string.no_categories_found);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
compositeDisposable.add(searchCategoriesDisposable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns image title list from UploadItem
|
||||
* @return
|
||||
*/
|
||||
private List<String> getImageTitleList() {
|
||||
List<String> titleList = new ArrayList<>();
|
||||
for (UploadItem item : repository.getUploads()) {
|
||||
final String captionText = item.getUploadMediaDetails().get(0).getCaptionText();
|
||||
if (!TextUtils.isEmpty(captionText)) {
|
||||
titleList.add(captionText);
|
||||
}
|
||||
}
|
||||
return titleList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the number of categories selected, prompts the user if none selected
|
||||
*/
|
||||
@Override
|
||||
public void verifyCategories() {
|
||||
List<CategoryItem> selectedCategories = repository.getSelectedCategories();
|
||||
if (selectedCategories != null && !selectedCategories.isEmpty()) {
|
||||
repository.setSelectedCategories(repository.getCategoryStringList());
|
||||
view.goToNextScreen();
|
||||
} else {
|
||||
view.showNoCategorySelected();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ask repository to handle category clicked
|
||||
*
|
||||
* @param categoryItem
|
||||
*/
|
||||
@Override
|
||||
public void onCategoryItemClicked(CategoryItem categoryItem) {
|
||||
repository.onCategoryClicked(categoryItem);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package fr.free.nrw.commons.upload.categories
|
||||
|
||||
import android.text.TextUtils
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.category.CategoryItem
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.repository.UploadRepository
|
||||
import fr.free.nrw.commons.upload.depicts.proxy
|
||||
import io.reactivex.Scheduler
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* The presenter class for UploadCategoriesFragment
|
||||
*/
|
||||
@Singleton
|
||||
class CategoriesPresenter @Inject constructor(
|
||||
private val repository: UploadRepository,
|
||||
@param:Named(CommonsApplicationModule.IO_THREAD) private val ioScheduler: Scheduler,
|
||||
@param:Named(CommonsApplicationModule.MAIN_THREAD) private val mainThreadScheduler: Scheduler
|
||||
) : CategoriesContract.UserActionListener {
|
||||
|
||||
companion object {
|
||||
private val DUMMY: CategoriesContract.View = proxy()
|
||||
}
|
||||
|
||||
var view = DUMMY
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
private val searchTerms = PublishSubject.create<String>()
|
||||
|
||||
override fun onAttachView(view: CategoriesContract.View) {
|
||||
this.view = view
|
||||
compositeDisposable.add(
|
||||
searchTerms
|
||||
.observeOn(mainThreadScheduler)
|
||||
.doOnNext {
|
||||
view.showProgress(true)
|
||||
view.showError(null)
|
||||
view.setCategories(null)
|
||||
}
|
||||
.switchMap(::searchResults)
|
||||
.map { repository.selectedCategories + it }
|
||||
.map { it.distinctBy { categoryItem -> categoryItem.name } }
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribe(
|
||||
{
|
||||
view.setCategories(it)
|
||||
view.showProgress(false)
|
||||
if (it.isEmpty()) {
|
||||
view.showError(R.string.no_categories_found)
|
||||
}
|
||||
},
|
||||
Timber::e
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun searchResults(term: String) =
|
||||
repository.searchAll(term, getImageTitleList())
|
||||
.subscribeOn(ioScheduler)
|
||||
.map { it.filterNot { categoryItem -> repository.containsYear(categoryItem.name) } }
|
||||
|
||||
override fun onDetachView() {
|
||||
view = DUMMY
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* asks the repository to fetch categories for the query
|
||||
* @param query
|
||||
*/
|
||||
override fun searchForCategories(query: String) {
|
||||
searchTerms.onNext(query)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns image title list from UploadItem
|
||||
* @return
|
||||
*/
|
||||
private fun getImageTitleList(): List<String> {
|
||||
return repository.uploads
|
||||
.map { it.uploadMediaDetails[0].captionText }
|
||||
.filterNot { TextUtils.isEmpty(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the number of categories selected, prompts the user if none selected
|
||||
*/
|
||||
override fun verifyCategories() {
|
||||
val selectedCategories = repository.selectedCategories
|
||||
if (selectedCategories.isNotEmpty()) {
|
||||
repository.setSelectedCategories(selectedCategories.map { it.name })
|
||||
view.goToNextScreen()
|
||||
} else {
|
||||
view.showNoCategorySelected()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ask repository to handle category clicked
|
||||
*
|
||||
* @param categoryItem
|
||||
*/
|
||||
override fun onCategoryItemClicked(categoryItem: CategoryItem) {
|
||||
repository.onCategoryClicked(categoryItem)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package fr.free.nrw.commons.upload.categories;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
|
@ -50,8 +51,6 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
|
|||
CategoriesContract.UserActionListener presenter;
|
||||
private RVRendererAdapter<CategoryItem> adapter;
|
||||
private Disposable subscribe;
|
||||
private List<CategoryItem> categories;
|
||||
private boolean isVisible;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
|
|
@ -78,15 +77,6 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
|
|||
presenter.onAttachView(this);
|
||||
initRecyclerView();
|
||||
addTextChangeListenerToEtSearch();
|
||||
//get default categories for empty query
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (presenter != null && isVisible && (categories == null || categories.isEmpty())) {
|
||||
presenter.searchForCategories(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void addTextChangeListenerToEtSearch() {
|
||||
|
|
@ -135,7 +125,6 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
|
|||
public void setCategories(List<CategoryItem> categories) {
|
||||
adapter.clear();
|
||||
if (categories != null) {
|
||||
this.categories = categories;
|
||||
adapter.addAll(categories);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
|
@ -174,12 +163,11 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
isVisible = isVisibleToUser;
|
||||
|
||||
if (presenter != null && isResumed() && (categories == null || categories.isEmpty())) {
|
||||
presenter.searchForCategories(null);
|
||||
protected void onBecameVisible() {
|
||||
super.onBecameVisible();
|
||||
final Editable text = etSearch.getText();
|
||||
if (text != null) {
|
||||
presenter.searchForCategories(text.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue