mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 04:43:54 +01:00
Refactor CategorizationFragment to use RxJava2
This commit is contained in:
parent
494739da5e
commit
0c0c814604
8 changed files with 231 additions and 597 deletions
|
|
@ -3,7 +3,6 @@ package fr.free.nrw.commons.category;
|
||||||
import android.content.ContentProviderClient;
|
import android.content.ContentProviderClient;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
|
@ -11,9 +10,7 @@ import android.support.v4.app.Fragment;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
|
@ -24,24 +21,28 @@ import android.widget.EditText;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.pedrogomez.renderers.ListAdapteeCollection;
|
import com.jakewharton.rxbinding2.view.RxView;
|
||||||
|
import com.jakewharton.rxbinding2.widget.RxTextView;
|
||||||
import com.pedrogomez.renderers.RVRendererAdapter;
|
import com.pedrogomez.renderers.RVRendererAdapter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.category.CategoriesRenderer.CategoryClickedListener;
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
import fr.free.nrw.commons.upload.MwVolleyApi;
|
import fr.free.nrw.commons.upload.MwVolleyApi;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.Single;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static android.view.KeyEvent.ACTION_UP;
|
import static android.view.KeyEvent.ACTION_UP;
|
||||||
|
|
@ -51,7 +52,8 @@ import static fr.free.nrw.commons.category.CategoryContentProvider.AUTHORITY;
|
||||||
/**
|
/**
|
||||||
* Displays the category suggestion and selection screen. Category search is initiated here.
|
* Displays the category suggestion and selection screen. Category search is initiated here.
|
||||||
*/
|
*/
|
||||||
public class CategorizationFragment extends Fragment implements CategoryClickedListener {
|
public class CategorizationFragment extends Fragment {
|
||||||
|
|
||||||
public static final int SEARCH_CATS_LIMIT = 25;
|
public static final int SEARCH_CATS_LIMIT = 25;
|
||||||
|
|
||||||
@BindView(R.id.categoriesListBox)
|
@BindView(R.id.categoriesListBox)
|
||||||
|
|
@ -68,19 +70,54 @@ public class CategorizationFragment extends Fragment implements CategoryClickedL
|
||||||
private RVRendererAdapter<CategoryItem> categoriesAdapter;
|
private RVRendererAdapter<CategoryItem> categoriesAdapter;
|
||||||
private OnCategoriesSaveHandler onCategoriesSaveHandler;
|
private OnCategoriesSaveHandler onCategoriesSaveHandler;
|
||||||
private HashMap<String, ArrayList<String>> categoriesCache;
|
private HashMap<String, ArrayList<String>> categoriesCache;
|
||||||
private ArrayList<String> selectedCategories = new ArrayList<>();
|
private List<CategoryItem> selectedCategories = new ArrayList<>();
|
||||||
private ContentProviderClient client;
|
private ContentProviderClient client;
|
||||||
private PrefixUpdater prefixUpdaterSub;
|
private final CategoriesAdapterFactory adapterFactory = new CategoriesAdapterFactory(item -> {
|
||||||
private MethodAUpdater methodAUpdaterSub;
|
if (item.isSelected()) {
|
||||||
private final CategoryTextWatcher textWatcher = new CategoryTextWatcher();
|
selectedCategories.add(item);
|
||||||
private final CategoriesAdapterFactory adapterFactory = new CategoriesAdapterFactory(this);
|
updateCategoryCount(item, client);
|
||||||
private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);
|
} else {
|
||||||
private final ArrayList<String> titleCatItems = new ArrayList<>();
|
selectedCategories.remove(item);
|
||||||
private final CountDownLatch mergeLatch = new CountDownLatch(1);
|
}
|
||||||
// LHS guarantees ordered insertions, allowing for prioritized method A results
|
});
|
||||||
private final Set<String> results = new LinkedHashSet<>();
|
|
||||||
|
private void updateCategoryCount(CategoryItem item, ContentProviderClient client) {
|
||||||
|
Category cat = lookupCategory(item.getName());
|
||||||
|
cat.incTimesUsed();
|
||||||
|
|
||||||
|
cat.setContentProviderClient(client);
|
||||||
|
cat.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Category lookupCategory(String name) {
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
cursor = client.query(
|
||||||
|
CategoryContentProvider.BASE_URI,
|
||||||
|
Category.Table.ALL_FIELDS,
|
||||||
|
Category.Table.COLUMN_NAME + "=?",
|
||||||
|
new String[]{name},
|
||||||
|
null);
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
return Category.fromCursor(cursor);
|
||||||
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// This feels lazy, but to hell with checked exceptions. :)
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Newly used category...
|
||||||
|
Category cat = new Category();
|
||||||
|
cat.setName(name);
|
||||||
|
cat.setLastUsed(new Date());
|
||||||
|
cat.setTimesUsed(0);
|
||||||
|
return cat;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
|
|
@ -89,30 +126,76 @@ public class CategorizationFragment extends Fragment implements CategoryClickedL
|
||||||
|
|
||||||
categoriesList.setLayoutManager(new LinearLayoutManager(getContext()));
|
categoriesList.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
|
||||||
categoriesSkip.setOnClickListener(view -> {
|
RxView.clicks(categoriesSkip)
|
||||||
|
.takeUntil(RxView.detaches(categoriesSkip))
|
||||||
|
.subscribe(o -> {
|
||||||
getActivity().onBackPressed();
|
getActivity().onBackPressed();
|
||||||
getActivity().finish();
|
getActivity().finish();
|
||||||
});
|
});
|
||||||
|
|
||||||
ArrayList<CategoryItem> items;
|
ArrayList<CategoryItem> items = new ArrayList<>();
|
||||||
if (savedInstanceState == null) {
|
|
||||||
items = new ArrayList<>();
|
|
||||||
categoriesCache = new HashMap<>();
|
categoriesCache = new HashMap<>();
|
||||||
} else {
|
if (savedInstanceState != null) {
|
||||||
items = savedInstanceState.getParcelableArrayList("currentCategories");
|
items.addAll(savedInstanceState.getParcelableArrayList("currentCategories"));
|
||||||
categoriesCache = (HashMap<String, ArrayList<String>>) savedInstanceState
|
categoriesCache.putAll((HashMap<String, ArrayList<String>>) savedInstanceState
|
||||||
.getSerializable("categoriesCache");
|
.getSerializable("categoriesCache"));
|
||||||
}
|
}
|
||||||
|
|
||||||
categoriesAdapter = adapterFactory.create(items);
|
categoriesAdapter = adapterFactory.create(items);
|
||||||
categoriesList.setAdapter(categoriesAdapter);
|
categoriesList.setAdapter(categoriesAdapter);
|
||||||
categoriesFilter.addTextChangedListener(textWatcher);
|
|
||||||
|
|
||||||
startUpdatingCategoryList();
|
|
||||||
|
|
||||||
|
RxTextView.textChanges(categoriesFilter)
|
||||||
|
.takeUntil(RxView.detaches(categoriesFilter))
|
||||||
|
.debounce(300, TimeUnit.MILLISECONDS)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(filter -> updateCategoryList(filter.toString()));
|
||||||
return rootView;
|
return rootView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateCategoryList(String filter) {
|
||||||
|
Observable.fromIterable(selectedCategories)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.doOnSubscribe(disposable -> {
|
||||||
|
categoriesSearchInProgress.setVisibility(View.VISIBLE);
|
||||||
|
categoriesNotFoundView.setVisibility(View.GONE);
|
||||||
|
categoriesSkip.setVisibility(View.GONE);
|
||||||
|
categoriesAdapter.clear();
|
||||||
|
})
|
||||||
|
.observeOn(Schedulers.io())
|
||||||
|
.concatWith(
|
||||||
|
search(filter)
|
||||||
|
.mergeWith(search2(filter))
|
||||||
|
.filter(categoryItem -> !selectedCategories.contains(categoryItem))
|
||||||
|
.switchIfEmpty(
|
||||||
|
gpsCategories()
|
||||||
|
.concatWith(titleCategories())
|
||||||
|
.concatWith(recentCategories())
|
||||||
|
.filter(categoryItem -> !selectedCategories.contains(categoryItem))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.filter(categoryItem -> !containsYear(categoryItem.getName()))
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(
|
||||||
|
s -> categoriesAdapter.add(s),
|
||||||
|
throwable -> Timber.e(throwable),
|
||||||
|
() -> {
|
||||||
|
categoriesAdapter.notifyDataSetChanged();
|
||||||
|
categoriesSearchInProgress.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
if (categoriesAdapter.getItemCount() == 0) {
|
||||||
|
if (TextUtils.isEmpty(filter)) {
|
||||||
|
// If we found no recent cats, show the skip message!
|
||||||
|
categoriesSkip.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
categoriesNotFoundView.setText(getString(R.string.categories_not_found, filter));
|
||||||
|
categoriesNotFoundView.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
menu.clear();
|
menu.clear();
|
||||||
|
|
@ -137,12 +220,6 @@ public class CategorizationFragment extends Fragment implements CategoryClickedL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
categoriesFilter.removeTextChangedListener(textWatcher);
|
|
||||||
super.onDestroyView();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
@ -165,21 +242,8 @@ public class CategorizationFragment extends Fragment implements CategoryClickedL
|
||||||
public boolean onOptionsItemSelected(MenuItem menuItem) {
|
public boolean onOptionsItemSelected(MenuItem menuItem) {
|
||||||
switch (menuItem.getItemId()) {
|
switch (menuItem.getItemId()) {
|
||||||
case R.id.menu_save_categories:
|
case R.id.menu_save_categories:
|
||||||
|
|
||||||
int numberSelected = 0;
|
|
||||||
|
|
||||||
selectedCategories = new ArrayList<>();
|
|
||||||
int count = categoriesAdapter.getItemCount();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
CategoryItem item = categoriesAdapter.getItem(i);
|
|
||||||
if (item.isSelected()) {
|
|
||||||
selectedCategories.add(item.getName());
|
|
||||||
numberSelected++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//If no categories selected, display warning to user
|
//If no categories selected, display warning to user
|
||||||
if (numberSelected == 0) {
|
if (selectedCategories.size() == 0) {
|
||||||
new AlertDialog.Builder(getActivity())
|
new AlertDialog.Builder(getActivity())
|
||||||
.setMessage("Images without categories are rarely usable. "
|
.setMessage("Images without categories are rarely usable. "
|
||||||
+ "Are you sure you want to submit without selecting "
|
+ "Are you sure you want to submit without selecting "
|
||||||
|
|
@ -190,18 +254,19 @@ public class CategorizationFragment extends Fragment implements CategoryClickedL
|
||||||
})
|
})
|
||||||
.setNegativeButton("Yes, submit", (dialog, id) -> {
|
.setNegativeButton("Yes, submit", (dialog, id) -> {
|
||||||
//Proceed to submission
|
//Proceed to submission
|
||||||
onCategoriesSaveHandler.onCategoriesSave(selectedCategories);
|
onCategoriesSaveHandler.onCategoriesSave(getStringList(selectedCategories));
|
||||||
})
|
})
|
||||||
.create()
|
.create()
|
||||||
.show();
|
.show();
|
||||||
} else {
|
} else {
|
||||||
//Proceed to submission
|
//Proceed to submission
|
||||||
onCategoriesSaveHandler.onCategoriesSave(selectedCategories);
|
onCategoriesSaveHandler.onCategoriesSave(getStringList(selectedCategories));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
default:
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(menuItem);
|
return super.onOptionsItemSelected(menuItem);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
|
|
@ -212,8 +277,12 @@ public class CategorizationFragment extends Fragment implements CategoryClickedL
|
||||||
client = getActivity().getContentResolver().acquireContentProviderClient(AUTHORITY);
|
client = getActivity().getContentResolver().acquireContentProviderClient(AUTHORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashMap<String, ArrayList<String>> getCategoriesCache() {
|
private List<String> getStringList(List<CategoryItem> input) {
|
||||||
return categoriesCache;
|
List<String> output = new ArrayList<>();
|
||||||
|
for (CategoryItem item : input) {
|
||||||
|
output.add(item.getName());
|
||||||
|
}
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -221,36 +290,15 @@ public class CategorizationFragment extends Fragment implements CategoryClickedL
|
||||||
*
|
*
|
||||||
* @return a list containing title-related categories
|
* @return a list containing title-related categories
|
||||||
*/
|
*/
|
||||||
private ArrayList<String> titleCatQuery() {
|
private List<String> titleCatQuery(String title) {
|
||||||
TitleCategories titleCategoriesSub;
|
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
|
||||||
|
//URL https://commons.wikimedia.org/w/api.php?action=query&format=xml&list=search&srwhat=text&srenablerewrites=1&srnamespace=14&srlimit=10&srsearch=
|
||||||
//Retrieve the title that was saved when user tapped submit icon
|
|
||||||
SharedPreferences titleDesc = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
|
||||||
String title = titleDesc.getString("Title", "");
|
|
||||||
Timber.d("Title: %s", title);
|
|
||||||
|
|
||||||
//Override onPostExecute to access the results of async API call
|
|
||||||
titleCategoriesSub = new TitleCategories(title) {
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(List<String> result) {
|
|
||||||
super.onPostExecute(result);
|
|
||||||
Timber.d("Results in onPostExecute: %s", result);
|
|
||||||
titleCatItems.addAll(result);
|
|
||||||
Timber.d("TitleCatItems in onPostExecute: %s", titleCatItems);
|
|
||||||
mergeLatch.countDown();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
titleCategoriesSub.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
Timber.d("TitleCatItems in titleCatQuery: %s", titleCatItems);
|
|
||||||
|
|
||||||
//Only return titleCatItems after API call has finished
|
|
||||||
try {
|
try {
|
||||||
mergeLatch.await(5L, TimeUnit.SECONDS);
|
return api.searchTitles(SEARCH_CATS_LIMIT, title);
|
||||||
} catch (InterruptedException e) {
|
} catch (IOException e) {
|
||||||
Timber.e(e, "Interrupted exception: ");
|
Timber.e(e, "IO Exception: ");
|
||||||
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
return titleCatItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -284,171 +332,105 @@ public class CategorizationFragment extends Fragment implements CategoryClickedL
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private Observable<CategoryItem> gpsCategories() {
|
||||||
* Merges nearby categories, categories suggested based on title, and recent categories...
|
return Observable.fromIterable(
|
||||||
* without duplicates.
|
MwVolleyApi.GpsCatExists.getGpsCatExists() ?
|
||||||
*
|
MwVolleyApi.getGpsCat() : new ArrayList<>())
|
||||||
* @return a list containing merged categories
|
.map(s -> new CategoryItem(s, false));
|
||||||
*/
|
|
||||||
ArrayList<String> mergeItems() {
|
|
||||||
Set<String> mergedItems = new LinkedHashSet<>();
|
|
||||||
|
|
||||||
Timber.d("Calling APIs for GPS cats, title cats and recent cats...");
|
|
||||||
|
|
||||||
List<String> gpsItems = new ArrayList<>();
|
|
||||||
if (MwVolleyApi.GpsCatExists.getGpsCatExists()) {
|
|
||||||
gpsItems.addAll(MwVolleyApi.getGpsCat());
|
|
||||||
}
|
}
|
||||||
List<String> titleItems = new ArrayList<>(titleCatQuery());
|
|
||||||
List<String> recentItems = new ArrayList<>(recentCatQuery());
|
|
||||||
|
|
||||||
//Await results of titleItems, which is likely to come in last
|
private Observable<CategoryItem> titleCategories() {
|
||||||
|
//Retrieve the title that was saved when user tapped submit icon
|
||||||
|
SharedPreferences titleDesc = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||||
|
String title = titleDesc.getString("Title", "");
|
||||||
|
|
||||||
|
return Observable.just(title)
|
||||||
|
.observeOn(Schedulers.io())
|
||||||
|
.flatMapIterable(s -> titleCatQuery(s))
|
||||||
|
.map(s -> new CategoryItem(s, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Observable<CategoryItem> recentCategories() {
|
||||||
|
return Observable.fromIterable(recentCatQuery())
|
||||||
|
.map(s -> new CategoryItem(s, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Observable<CategoryItem> search(String term) {
|
||||||
|
return Single.just(term)
|
||||||
|
.map(s -> {
|
||||||
|
//If user hasn't typed anything in yet, get GPS and recent items
|
||||||
|
if (TextUtils.isEmpty(s)) {
|
||||||
|
return new ArrayList<String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
//if user types in something that is in cache, return cached category
|
||||||
|
if (categoriesCache.containsKey(s)) {
|
||||||
|
return categoriesCache.get(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
//otherwise if user has typed something in that isn't in cache, search API for matching categories
|
||||||
|
//URL: https://commons.wikimedia.org/w/api.php?action=query&list=allcategories&acprefix=filter&aclimit=25
|
||||||
|
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
|
||||||
|
List<String> categories = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
mergeLatch.await(5L, TimeUnit.SECONDS);
|
categories = api.allCategories(SEARCH_CATS_LIMIT, s);
|
||||||
Timber.d("Waited for merge");
|
Timber.d("Prefix URL filter %s", categories);
|
||||||
} catch (InterruptedException e) {
|
} catch (IOException e) {
|
||||||
Timber.e(e, "Interrupted Exception: ");
|
Timber.e(e, "IO Exception: ");
|
||||||
|
//Return empty arraylist
|
||||||
|
return categories;
|
||||||
}
|
}
|
||||||
|
|
||||||
mergedItems.addAll(gpsItems);
|
Timber.d("Found categories from Prefix search, waiting for filter");
|
||||||
Timber.d("Adding GPS items: %s", gpsItems);
|
return categories;
|
||||||
mergedItems.addAll(titleItems);
|
})
|
||||||
Timber.d("Adding title items: %s", titleItems);
|
.flatMapObservable(Observable::fromIterable)
|
||||||
mergedItems.addAll(recentItems);
|
.map(s -> new CategoryItem(s, false));
|
||||||
Timber.d("Adding recent items: %s", recentItems);
|
|
||||||
|
|
||||||
// Needs to be an ArrayList and not a List unless we want to modify a big portion
|
|
||||||
// of preexisting code
|
|
||||||
ArrayList<String> mergedItemsList = new ArrayList<>(mergedItems);
|
|
||||||
|
|
||||||
Timber.d("Merged item list: %s", mergedItemsList);
|
|
||||||
return mergedItemsList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private Observable<CategoryItem> search2(String term) {
|
||||||
* Displays categories found to the user as they type in the search box
|
return Single.just(term)
|
||||||
*
|
.map(s -> {
|
||||||
* @param categories a list of all categories found for the search string
|
//If user hasn't typed anything in yet, get GPS and recent items
|
||||||
* @param filter the search string
|
if (TextUtils.isEmpty(s)) {
|
||||||
*/
|
return new ArrayList<String>();
|
||||||
private void setCatsAfterAsync(ArrayList<String> categories, String filter) {
|
|
||||||
if (getActivity() != null) {
|
|
||||||
ArrayList<CategoryItem> items = new ArrayList<>();
|
|
||||||
HashSet<String> existingKeys = new HashSet<>();
|
|
||||||
int count = categoriesAdapter.getItemCount();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
CategoryItem item = categoriesAdapter.getItem(i);
|
|
||||||
if (item.isSelected()) {
|
|
||||||
items.add(item);
|
|
||||||
existingKeys.add(item.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (String category : categories) {
|
|
||||||
if (!existingKeys.contains(category)) {
|
|
||||||
items.add(new CategoryItem(category, false));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
categoriesAdapter.setCollection(new ListAdapteeCollection<>(items));
|
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
|
||||||
categoriesAdapter.notifyDataSetChanged();
|
|
||||||
categoriesSearchInProgress.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
if (categories.isEmpty()) {
|
//URL https://commons.wikimedia.org/w/api.php?action=query&format=xml&list=search&srwhat=text&srenablerewrites=1&srnamespace=14&srlimit=10&srsearch=
|
||||||
if (TextUtils.isEmpty(filter)) {
|
|
||||||
// If we found no recent cats, show the skip message!
|
|
||||||
categoriesSkip.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
categoriesNotFoundView.setText(getString(R.string.categories_not_found, filter));
|
|
||||||
categoriesNotFoundView.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
categoriesList.smoothScrollToPosition(existingKeys.size());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Timber.e("Error: Fragment is null");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes asynchronous calls to the Commons MediaWiki API via anonymous subclasses of
|
|
||||||
* 'MethodAUpdater' and 'PrefixUpdater'. Some of their methods are overridden in order to
|
|
||||||
* aggregate the results. A CountDownLatch is used to ensure that MethodA results are shown
|
|
||||||
* above Prefix results.
|
|
||||||
*/
|
|
||||||
private void requestSearchResults() {
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
|
|
||||||
prefixUpdaterSub = new PrefixUpdater(this) {
|
|
||||||
@Override
|
|
||||||
protected List<String> doInBackground(Void... voids) {
|
|
||||||
List<String> result = new ArrayList<>();
|
|
||||||
try {
|
try {
|
||||||
result = super.doInBackground();
|
return api.searchCategories(SEARCH_CATS_LIMIT, term);
|
||||||
latch.await();
|
} catch (IOException e) {
|
||||||
} catch (InterruptedException e) {
|
Timber.e(e, "IO Exception: ");
|
||||||
Timber.w(e);
|
return new ArrayList<String>();
|
||||||
//Thread.currentThread().interrupt();
|
|
||||||
}
|
}
|
||||||
return result;
|
})
|
||||||
|
.flatMapObservable(Observable::fromIterable)
|
||||||
|
.map(s -> new CategoryItem(s, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private boolean containsYear(String items) {
|
||||||
protected void onPostExecute(List<String> result) {
|
|
||||||
super.onPostExecute(result);
|
|
||||||
|
|
||||||
results.addAll(result);
|
//Check for current and previous year to exclude these categories from removal
|
||||||
Timber.d("Prefix result: %s", result);
|
Calendar now = Calendar.getInstance();
|
||||||
|
int year = now.get(Calendar.YEAR);
|
||||||
|
String yearInString = String.valueOf(year);
|
||||||
|
|
||||||
String filter = categoriesFilter.getText().toString();
|
int prevYear = year - 1;
|
||||||
ArrayList<String> resultsList = new ArrayList<>(results);
|
String prevYearInString = String.valueOf(prevYear);
|
||||||
categoriesCache.put(filter, resultsList);
|
Timber.d("Previous year: %s", prevYearInString);
|
||||||
Timber.d("Final results List: %s", resultsList);
|
|
||||||
|
|
||||||
categoriesAdapter.notifyDataSetChanged();
|
|
||||||
setCatsAfterAsync(resultsList, filter);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
methodAUpdaterSub = new MethodAUpdater(this) {
|
//Check if s contains a 4-digit word anywhere within the string (.* is wildcard)
|
||||||
@Override
|
//And that s does not equal the current year or previous year
|
||||||
protected void onPostExecute(List<String> result) {
|
//And if it is an irrelevant category such as Media_needing_categories_as_of_16_June_2017(Issue #750)
|
||||||
results.clear();
|
return ((items.matches(".*(19|20)\\d{2}.*") && !items.contains(yearInString) && !items.contains(prevYearInString))
|
||||||
super.onPostExecute(result);
|
|| items.matches("(.*)needing(.*)") || items.matches("(.*)taken on(.*)"));
|
||||||
|
|
||||||
results.addAll(result);
|
|
||||||
Timber.d("Method A result: %s", result);
|
|
||||||
categoriesAdapter.notifyDataSetChanged();
|
|
||||||
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
prefixUpdaterSub.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
methodAUpdaterSub.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startUpdatingCategoryList() {
|
|
||||||
if (prefixUpdaterSub != null) {
|
|
||||||
prefixUpdaterSub.cancel(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methodAUpdaterSub != null) {
|
|
||||||
methodAUpdaterSub.cancel(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
requestSearchResults();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCurrentSelectedCount() {
|
public int getCurrentSelectedCount() {
|
||||||
int count = 0;
|
return selectedCategories.size();
|
||||||
int numberOfItems = categoriesAdapter.getItemCount();
|
|
||||||
for (int i = 0; i < numberOfItems; i++) {
|
|
||||||
CategoryItem item = categoriesAdapter.getItem(i);
|
|
||||||
if (item.isSelected()) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void backButtonDialog() {
|
public void backButtonDialog() {
|
||||||
|
|
@ -463,27 +445,4 @@ public class CategorizationFragment extends Fragment implements CategoryClickedL
|
||||||
.create()
|
.create()
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void categoryClicked(CategoryItem item) {
|
|
||||||
if (item.isSelected()) {
|
|
||||||
new CategoryCountUpdater(item.getName(), client).executeOnExecutor(executor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CategoryTextWatcher implements TextWatcher {
|
|
||||||
@Override
|
|
||||||
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
|
|
||||||
startUpdatingCategoryList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable editable) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
package fr.free.nrw.commons.category;
|
|
||||||
|
|
||||||
import android.content.ContentProviderClient;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
class CategoryCountUpdater extends AsyncTask<Void, Void, Void> {
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
private final ContentProviderClient client;
|
|
||||||
|
|
||||||
CategoryCountUpdater(String name, ContentProviderClient client) {
|
|
||||||
this.name = name;
|
|
||||||
this.client = client;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... voids) {
|
|
||||||
Category cat = lookupCategory(name);
|
|
||||||
cat.incTimesUsed();
|
|
||||||
|
|
||||||
cat.setContentProviderClient(client);
|
|
||||||
cat.save();
|
|
||||||
|
|
||||||
return null; // Make the compiler happy.
|
|
||||||
}
|
|
||||||
|
|
||||||
private Category lookupCategory(String name) {
|
|
||||||
Cursor cursor = null;
|
|
||||||
try {
|
|
||||||
cursor = client.query(
|
|
||||||
CategoryContentProvider.BASE_URI,
|
|
||||||
Category.Table.ALL_FIELDS,
|
|
||||||
Category.Table.COLUMN_NAME + "=?",
|
|
||||||
new String[]{name},
|
|
||||||
null);
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
|
||||||
return Category.fromCursor(cursor);
|
|
||||||
}
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
// This feels lazy, but to hell with checked exceptions. :)
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
if (cursor != null) {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Newly used category...
|
|
||||||
Category cat = new Category();
|
|
||||||
cat.setName(name);
|
|
||||||
cat.setLastUsed(new Date());
|
|
||||||
cat.setTimesUsed(0);
|
|
||||||
return cat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
package fr.free.nrw.commons.category;
|
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import static fr.free.nrw.commons.category.CategorizationFragment.SEARCH_CATS_LIMIT;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends asynchronous queries to the Commons MediaWiki API to retrieve categories that are close to
|
|
||||||
* the keyword typed in by the user. The 'srsearch' action-specific parameter is used for this
|
|
||||||
* purpose. This class should be subclassed in CategorizationFragment.java to aggregate the results.
|
|
||||||
*/
|
|
||||||
class MethodAUpdater extends AsyncTask<Void, Void, List<String>> {
|
|
||||||
|
|
||||||
private final CategorizationFragment catFragment;
|
|
||||||
private String filter;
|
|
||||||
|
|
||||||
MethodAUpdater(CategorizationFragment catFragment) {
|
|
||||||
this.catFragment = catFragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
super.onPreExecute();
|
|
||||||
filter = catFragment.categoriesFilter.getText().toString();
|
|
||||||
catFragment.categoriesSearchInProgress.setVisibility(View.VISIBLE);
|
|
||||||
catFragment.categoriesNotFoundView.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
catFragment.categoriesSkip.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove categories that contain a year in them (starting with 19__ or 20__), except for this year
|
|
||||||
* and previous year
|
|
||||||
* Rationale: https://github.com/commons-app/apps-android-commons/issues/47
|
|
||||||
*
|
|
||||||
* @param items Unfiltered list of categories
|
|
||||||
* @return Filtered category list
|
|
||||||
*/
|
|
||||||
private List<String> filterYears(List<String> items) {
|
|
||||||
|
|
||||||
Iterator<String> iterator;
|
|
||||||
|
|
||||||
//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);
|
|
||||||
Timber.d("Year: %s", yearInString);
|
|
||||||
|
|
||||||
int prevYear = year - 1;
|
|
||||||
String prevYearInString = String.valueOf(prevYear);
|
|
||||||
Timber.d("Previous year: %s", prevYearInString);
|
|
||||||
|
|
||||||
//Copy to Iterator to prevent ConcurrentModificationException when removing item
|
|
||||||
for (iterator = items.iterator(); iterator.hasNext(); ) {
|
|
||||||
String s = iterator.next();
|
|
||||||
|
|
||||||
//Check if s contains a 4-digit word anywhere within the string (.* is wildcard)
|
|
||||||
//And that s does not equal the current year or previous year
|
|
||||||
if (s.matches(".*(19|20)\\d{2}.*") && !s.contains(yearInString) && !s.contains(prevYearInString)) {
|
|
||||||
Timber.d("Filtering out year %s", s);
|
|
||||||
iterator.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.d("Items: %s", items);
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<String> doInBackground(Void... voids) {
|
|
||||||
|
|
||||||
//otherwise if user has typed something in that isn't in cache, search API for matching categories
|
|
||||||
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
|
|
||||||
List<String> categories = new ArrayList<>();
|
|
||||||
|
|
||||||
//URL https://commons.wikimedia.org/w/api.php?action=query&format=xml&list=search&srwhat=text&srenablerewrites=1&srnamespace=14&srlimit=10&srsearch=
|
|
||||||
try {
|
|
||||||
categories = api.searchCategories(SEARCH_CATS_LIMIT, filter);
|
|
||||||
Timber.d("Method A URL filter %s", categories);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Timber.e(e, "IO Exception: ");
|
|
||||||
//Return empty arraylist
|
|
||||||
return categories;
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.d("Found categories from Method A search, waiting for filter");
|
|
||||||
return new ArrayList<>(filterYears(categories));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package fr.free.nrw.commons.category;
|
package fr.free.nrw.commons.category;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.List;
|
||||||
|
|
||||||
public interface OnCategoriesSaveHandler {
|
public interface OnCategoriesSaveHandler {
|
||||||
void onCategoriesSave(ArrayList<String> categories);
|
void onCategoriesSave(List<String> categories);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
package fr.free.nrw.commons.category;
|
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import static fr.free.nrw.commons.category.CategorizationFragment.SEARCH_CATS_LIMIT;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends asynchronous queries to the Commons MediaWiki API to retrieve categories that share the
|
|
||||||
* same prefix as the keyword typed in by the user. The 'acprefix' action-specific parameter is used
|
|
||||||
* for this purpose. This class should be subclassed in CategorizationFragment.java to aggregate
|
|
||||||
* the results.
|
|
||||||
*/
|
|
||||||
class PrefixUpdater extends AsyncTask<Void, Void, List<String>> {
|
|
||||||
|
|
||||||
private final CategorizationFragment catFragment;
|
|
||||||
private String filter;
|
|
||||||
|
|
||||||
PrefixUpdater(CategorizationFragment catFragment) {
|
|
||||||
this.catFragment = catFragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
super.onPreExecute();
|
|
||||||
filter = catFragment.categoriesFilter.getText().toString();
|
|
||||||
catFragment.categoriesSearchInProgress.setVisibility(View.VISIBLE);
|
|
||||||
catFragment.categoriesNotFoundView.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
catFragment.categoriesSkip.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove categories that contain a year in them (starting with 19__ or 20__), except for this year
|
|
||||||
* and previous year
|
|
||||||
* Rationale: https://github.com/commons-app/apps-android-commons/issues/47
|
|
||||||
*
|
|
||||||
* @param items Unfiltered list of categories
|
|
||||||
* @return Filtered category list
|
|
||||||
*/
|
|
||||||
private List<String> filterIrrelevantResults(List<String> items) {
|
|
||||||
|
|
||||||
Iterator<String> iterator;
|
|
||||||
|
|
||||||
//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);
|
|
||||||
Timber.d("Year: %s", yearInString);
|
|
||||||
|
|
||||||
int prevYear = year - 1;
|
|
||||||
String prevYearInString = String.valueOf(prevYear);
|
|
||||||
Timber.d("Previous year: %s", prevYearInString);
|
|
||||||
|
|
||||||
//Copy to Iterator to prevent ConcurrentModificationException when removing item
|
|
||||||
for (iterator = items.iterator(); iterator.hasNext();) {
|
|
||||||
String s = iterator.next();
|
|
||||||
|
|
||||||
//Check if s contains a 4-digit word anywhere within the string (.* is wildcard)
|
|
||||||
//And that s 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)
|
|
||||||
if ((s.matches(".*(19|20)\\d{2}.*") && !s.contains(yearInString) && !s.contains(prevYearInString))
|
|
||||||
|| s.matches("(.*)needing(.*)")||s.matches("(.*)taken on(.*)")) {
|
|
||||||
Timber.d("Filtering out irrelevant result: %s", s);
|
|
||||||
iterator.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.d("Items: %s", items);
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<String> doInBackground(Void... voids) {
|
|
||||||
//If user hasn't typed anything in yet, get GPS and recent items
|
|
||||||
if (TextUtils.isEmpty(filter)) {
|
|
||||||
ArrayList<String> mergedItems = new ArrayList<>(catFragment.mergeItems());
|
|
||||||
Timber.d("Merged items, waiting for filter");
|
|
||||||
return new ArrayList<>(filterIrrelevantResults(mergedItems));
|
|
||||||
}
|
|
||||||
|
|
||||||
//if user types in something that is in cache, return cached category
|
|
||||||
HashMap<String, ArrayList<String>> categoriesCache = catFragment.getCategoriesCache();
|
|
||||||
if (categoriesCache.containsKey(filter)) {
|
|
||||||
ArrayList<String> cachedItems = new ArrayList<>(categoriesCache.get(filter));
|
|
||||||
Timber.d("Found cache items, waiting for filter");
|
|
||||||
return new ArrayList<>(filterIrrelevantResults(cachedItems));
|
|
||||||
}
|
|
||||||
|
|
||||||
//otherwise if user has typed something in that isn't in cache, search API for matching categories
|
|
||||||
//URL: https://commons.wikimedia.org/w/api.php?action=query&list=allcategories&acprefix=filter&aclimit=25
|
|
||||||
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
|
|
||||||
List<String> categories = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
categories = api.allCategories(SEARCH_CATS_LIMIT, this.filter);
|
|
||||||
Timber.d("Prefix URL filter %s", categories);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Timber.e(e, "IO Exception: ");
|
|
||||||
//Return empty arraylist
|
|
||||||
return categories;
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.d("Found categories from Prefix search, waiting for filter");
|
|
||||||
return new ArrayList<>(filterIrrelevantResults(categories));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
package fr.free.nrw.commons.category;
|
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends asynchronous queries to the Commons MediaWiki API to retrieve categories that are related to
|
|
||||||
* the title entered in previous screen. The 'srsearch' action-specific parameter is used for this
|
|
||||||
* purpose. This class should be subclassed in CategorizationFragment.java to add the results to recent and GPS cats.
|
|
||||||
*/
|
|
||||||
class TitleCategories extends AsyncTask<Void, Void, List<String>> {
|
|
||||||
|
|
||||||
private final static int SEARCH_CATS_LIMIT = 25;
|
|
||||||
|
|
||||||
private final String title;
|
|
||||||
|
|
||||||
TitleCategories(String title) {
|
|
||||||
this.title = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<String> doInBackground(Void... voids) {
|
|
||||||
|
|
||||||
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
|
|
||||||
List<String> titleCategories = new ArrayList<>();
|
|
||||||
|
|
||||||
//URL https://commons.wikimedia.org/w/api.php?action=query&format=xml&list=search&srwhat=text&srenablerewrites=1&srnamespace=14&srlimit=10&srsearch=
|
|
||||||
try {
|
|
||||||
titleCategories = api.searchTitles(SEARCH_CATS_LIMIT, this.title);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Timber.e(e, "IO Exception: ");
|
|
||||||
//Return empty arraylist
|
|
||||||
return titleCategories;
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.d("Title cat query results: %s", titleCategories);
|
|
||||||
|
|
||||||
return titleCategories;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -22,6 +22,7 @@ import android.widget.AdapterView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
|
|
@ -160,7 +161,7 @@ public class MultipleShareActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCategoriesSave(ArrayList<String> categories) {
|
public void onCategoriesSave(List<String> categories) {
|
||||||
if(categories.size() > 0) {
|
if(categories.size() > 0) {
|
||||||
ContentProviderClient client = getContentResolver().acquireContentProviderClient(ModificationsContentProvider.AUTHORITY);
|
ContentProviderClient client = getContentResolver().acquireContentProviderClient(ModificationsContentProvider.AUTHORITY);
|
||||||
for(Contribution contribution: photosList) {
|
for(Contribution contribution: photosList) {
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ public class ShareActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCategoriesSave(ArrayList<String> categories) {
|
public void onCategoriesSave(List<String> categories) {
|
||||||
if(categories.size() > 0) {
|
if(categories.size() > 0) {
|
||||||
ModifierSequence categoriesSequence = new ModifierSequence(contribution.getContentUri());
|
ModifierSequence categoriesSequence = new ModifierSequence(contribution.getContentUri());
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue