Migrate network tasks to RxJava

This commit is contained in:
Mikel 2017-08-06 19:58:58 +01:00
parent 84a5f0a221
commit b7215c580f
3 changed files with 133 additions and 133 deletions

View file

@ -23,7 +23,6 @@ import com.jakewharton.rxbinding2.view.RxView;
import com.jakewharton.rxbinding2.widget.RxTextView; 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.Calendar;
import java.util.Date; import java.util.Date;
@ -36,10 +35,8 @@ import butterknife.ButterKnife;
import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.data.Category; import fr.free.nrw.commons.data.Category;
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.Observable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
import timber.log.Timber; import timber.log.Timber;
@ -194,7 +191,7 @@ public class CategorizationFragment extends Fragment {
rootView.requestFocus(); rootView.requestFocus();
rootView.setOnKeyListener((v, keyCode, event) -> { rootView.setOnKeyListener((v, keyCode, event) -> {
if (event.getAction() == ACTION_UP && keyCode == KEYCODE_BACK) { if (event.getAction() == ACTION_UP && keyCode == KEYCODE_BACK) {
backButtonDialog(); showBackButtonDialog();
return true; return true;
} }
return false; return false;
@ -224,25 +221,12 @@ public class CategorizationFragment extends Fragment {
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:
//If no categories selected, display warning to user if (selectedCategories.size() > 0) {
if (selectedCategories.size() == 0) { //Some categories selected, proceed to submission
new AlertDialog.Builder(getActivity())
.setMessage("Images without categories are rarely usable. "
+ "Are you sure you want to submit without selecting "
+ "categories?")
.setTitle("No Categories Selected")
.setPositiveButton("No, go back", (dialog, id) -> {
//Exit menuItem so user can select their categories
})
.setNegativeButton("Yes, submit", (dialog, id) -> {
//Proceed to submission
onCategoriesSaveHandler.onCategoriesSave(getStringList(selectedCategories));
})
.create()
.show();
} else {
//Proceed to submission
onCategoriesSaveHandler.onCategoriesSave(getStringList(selectedCategories)); onCategoriesSaveHandler.onCategoriesSave(getStringList(selectedCategories));
} else {
//No categories selected, prompt the user to select some
showConfirmationDialog();
} }
return true; return true;
default: default:
@ -271,7 +255,7 @@ public class CategorizationFragment extends Fragment {
return Observable.fromIterable( return Observable.fromIterable(
MwVolleyApi.GpsCatExists.getGpsCatExists() ? MwVolleyApi.GpsCatExists.getGpsCatExists() ?
MwVolleyApi.getGpsCat() : new ArrayList<>()) MwVolleyApi.getGpsCat() : new ArrayList<>())
.map(s -> new CategoryItem(s, false)); .map(name -> new CategoryItem(name, false));
} }
private Observable<CategoryItem> titleCategories() { private Observable<CategoryItem> titleCategories() {
@ -279,10 +263,9 @@ public class CategorizationFragment extends Fragment {
SharedPreferences titleDesc = PreferenceManager.getDefaultSharedPreferences(getActivity()); SharedPreferences titleDesc = PreferenceManager.getDefaultSharedPreferences(getActivity());
String title = titleDesc.getString("Title", ""); String title = titleDesc.getString("Title", "");
return Observable.just(title) return CommonsApplication.getInstance().getMWApi()
.observeOn(Schedulers.io()) .searchTitles(title, SEARCH_CATS_LIMIT)
.flatMapIterable(s -> titleCatQuery(s)) .map(name -> new CategoryItem(name, false));
.map(s -> new CategoryItem(s, false));
} }
private Observable<CategoryItem> recentCategories() { private Observable<CategoryItem> recentCategories() {
@ -291,62 +274,35 @@ public class CategorizationFragment extends Fragment {
} }
private Observable<CategoryItem> search(String term) { private Observable<CategoryItem> search(String term) {
return Single.just(term) //If user hasn't typed anything in yet, get GPS and recent items
.map(s -> { if (TextUtils.isEmpty(term)) {
//If user hasn't typed anything in yet, get GPS and recent items return Observable.empty();
if (TextUtils.isEmpty(s)) { }
return new ArrayList<String>();
}
//if user types in something that is in cache, return cached category //if user types in something that is in cache, return cached category
if (categoriesCache.containsKey(s)) { if (categoriesCache.containsKey(term)) {
return categoriesCache.get(s); return Observable.fromIterable(categoriesCache.get(term))
} .map(name -> new CategoryItem(name, false));
}
//otherwise if user has typed something in that isn't in cache, search API for matching categories //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 return CommonsApplication.getInstance().getMWApi()
MediaWikiApi api = CommonsApplication.getInstance().getMWApi(); .allCategories(term, SEARCH_CATS_LIMIT)
List<String> categories = new ArrayList<>(); .map(name -> new CategoryItem(name, false));
try {
categories = api.allCategories(SEARCH_CATS_LIMIT, s);
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 categories;
})
.flatMapObservable(Observable::fromIterable)
.map(s -> new CategoryItem(s, false));
} }
private Observable<CategoryItem> search2(String term) { private Observable<CategoryItem> search2(String term) {
return Single.just(term) //If user hasn't typed anything in yet, get GPS and recent items
.map(s -> { if (TextUtils.isEmpty(term)) {
//If user hasn't typed anything in yet, get GPS and recent items return Observable.empty();
if (TextUtils.isEmpty(s)) { }
return new ArrayList<String>();
}
MediaWikiApi api = CommonsApplication.getInstance().getMWApi(); return CommonsApplication.getInstance().getMWApi()
.searchCategories(term, SEARCH_CATS_LIMIT)
//URL https://commons.wikimedia.org/w/api.php?action=query&format=xml&list=search&srwhat=text&srenablerewrites=1&srnamespace=14&srlimit=10&srsearch=
try {
return api.searchCategories(SEARCH_CATS_LIMIT, term);
} catch (IOException e) {
Timber.e(e, "IO Exception: ");
return new ArrayList<String>();
}
})
.flatMapObservable(Observable::fromIterable)
.map(s -> new CategoryItem(s, false)); .map(s -> new CategoryItem(s, false));
} }
private boolean containsYear(String items) { private boolean containsYear(String items) {
//Check for current and previous year to exclude these categories from removal //Check for current and previous year to exclude these categories from removal
Calendar now = Calendar.getInstance(); Calendar now = Calendar.getInstance();
int year = now.get(Calendar.YEAR); int year = now.get(Calendar.YEAR);
@ -356,7 +312,6 @@ public class CategorizationFragment extends Fragment {
String prevYearInString = String.valueOf(prevYear); String prevYearInString = String.valueOf(prevYear);
Timber.d("Previous year: %s", prevYearInString); Timber.d("Previous year: %s", prevYearInString);
//Check if s contains a 4-digit word anywhere within the string (.* is wildcard) //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 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) //And if it is an irrelevant category such as Media_needing_categories_as_of_16_June_2017(Issue #750)
@ -368,7 +323,7 @@ public class CategorizationFragment extends Fragment {
return selectedCategories.size(); return selectedCategories.size();
} }
public void backButtonDialog() { public void showBackButtonDialog() {
new AlertDialog.Builder(getActivity()) new AlertDialog.Builder(getActivity())
.setMessage("Are you sure you want to go back? The image will not " .setMessage("Are you sure you want to go back? The image will not "
+ "have any categories saved.") + "have any categories saved.")
@ -380,4 +335,21 @@ public class CategorizationFragment extends Fragment {
.create() .create()
.show(); .show();
} }
public void showConfirmationDialog() {
new AlertDialog.Builder(getActivity())
.setMessage("Images without categories are rarely usable. "
+ "Are you sure you want to submit without selecting "
+ "categories?")
.setTitle("No Categories Selected")
.setPositiveButton("No, go back", (dialog, id) -> {
//Exit menuItem so user can select their categories
})
.setNegativeButton("Yes, submit", (dialog, id) -> {
//Proceed to submission
onCategoriesSaveHandler.onCategoriesSave(getStringList(selectedCategories));
})
.create()
.show();
}
} }

View file

@ -35,6 +35,7 @@ import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.PageTitle; import fr.free.nrw.commons.PageTitle;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import in.yuvi.http.fluent.Http; import in.yuvi.http.fluent.Http;
import io.reactivex.Observable;
import io.reactivex.Single; import io.reactivex.Single;
import timber.log.Timber; import timber.log.Timber;
@ -205,78 +206,104 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
@Override @Override
@NonNull @NonNull
public List<String> searchCategories(int searchCatsLimit, String filterValue) throws IOException { public Observable<String> searchCategories(String filterValue, int searchCatsLimit) {
List<ApiResult> categoryNodes = api.action("query") return Single.fromCallable(() -> {
.param("format", "xml") List<ApiResult> categoryNodes = null;
.param("list", "search") try {
.param("srwhat", "text") categoryNodes = api.action("query")
.param("srnamespace", "14") .param("format", "xml")
.param("srlimit", searchCatsLimit) .param("list", "search")
.param("srsearch", filterValue) .param("srwhat", "text")
.get() .param("srnamespace", "14")
.getNodes("/api/query/search/p/@title"); .param("srlimit", searchCatsLimit)
.param("srsearch", filterValue)
.get()
.getNodes("/api/query/search/p/@title");
} catch (IOException e) {
Timber.e("Failed to obtain searchCategories", e);
}
if (categoryNodes == null) { if (categoryNodes == null) {
return Collections.emptyList(); return new ArrayList<String>();
} }
List<String> categories = new ArrayList<>(); List<String> categories = new ArrayList<>();
for (ApiResult categoryNode : categoryNodes) { for (ApiResult categoryNode : categoryNodes) {
String cat = categoryNode.getDocument().getTextContent(); String cat = categoryNode.getDocument().getTextContent();
String catString = cat.replace("Category:", ""); String catString = cat.replace("Category:", "");
categories.add(catString); categories.add(catString);
} }
return categories; return categories;
})
.flatMapObservable(list -> Observable.fromIterable(list));
} }
@Override @Override
@NonNull @NonNull
public List<String> allCategories(int searchCatsLimit, String filterValue) throws IOException { public Observable<String> allCategories(String filterValue, int searchCatsLimit) {
ArrayList<ApiResult> categoryNodes = api.action("query") return Single.fromCallable(() -> {
.param("list", "allcategories") ArrayList<ApiResult> categoryNodes = null;
.param("acprefix", filterValue) try {
.param("aclimit", searchCatsLimit) categoryNodes = api.action("query")
.get() .param("list", "allcategories")
.getNodes("/api/query/allcategories/c"); .param("acprefix", filterValue)
.param("aclimit", searchCatsLimit)
.get()
.getNodes("/api/query/allcategories/c");
} catch (IOException e) {
Timber.e("Failed to obtain allCategories", e);
}
if (categoryNodes == null) { if (categoryNodes == null) {
return Collections.emptyList(); return new ArrayList<String>();
} }
List<String> categories = new ArrayList<>(); List<String> categories = new ArrayList<>();
for (ApiResult categoryNode : categoryNodes) { for (ApiResult categoryNode : categoryNodes) {
categories.add(categoryNode.getDocument().getTextContent()); categories.add(categoryNode.getDocument().getTextContent());
} }
return categories; return categories;
})
.flatMapObservable(list -> Observable.fromIterable(list));
} }
@Override @Override
@NonNull @NonNull
public List<String> searchTitles(int searchCatsLimit, String title) throws IOException { public Observable<String> searchTitles(String title, int searchCatsLimit) {
ArrayList<ApiResult> categoryNodes = api.action("query") return Single.fromCallable(() -> {
.param("format", "xml") ArrayList<ApiResult> categoryNodes = null;
.param("list", "search")
.param("srwhat", "text")
.param("srnamespace", "14")
.param("srlimit", searchCatsLimit)
.param("srsearch", title)
.get()
.getNodes("/api/query/search/p/@title");
if (categoryNodes == null) { try {
return Collections.emptyList(); categoryNodes = api.action("query")
} .param("format", "xml")
.param("list", "search")
.param("srwhat", "text")
.param("srnamespace", "14")
.param("srlimit", searchCatsLimit)
.param("srsearch", title)
.get()
.getNodes("/api/query/search/p/@title");
} catch (IOException e) {
Timber.e("Failed to obtain searchTitles", e);
return new ArrayList();
}
List<String> titleCategories = new ArrayList<>(); if (categoryNodes == null) {
for (ApiResult categoryNode : categoryNodes) { return Collections.emptyList();
String cat = categoryNode.getDocument().getTextContent(); }
String catString = cat.replace("Category:", "");
titleCategories.add(catString);
}
return titleCategories; List<String> titleCategories = new ArrayList<>();
for (ApiResult categoryNode : categoryNodes) {
String cat = categoryNode.getDocument().getTextContent();
String catString = cat.replace("Category:", "");
titleCategories.add(catString);
}
return titleCategories;
})
.flatMapObservable(list -> Observable.fromIterable(list));
} }
@Override @Override

View file

@ -7,6 +7,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
import io.reactivex.Observable;
import io.reactivex.Single; import io.reactivex.Single;
public interface MediaWikiApi { public interface MediaWikiApi {
@ -38,13 +39,13 @@ public interface MediaWikiApi {
MediaResult fetchMediaByFilename(String filename) throws IOException; MediaResult fetchMediaByFilename(String filename) throws IOException;
@NonNull @NonNull
List<String> searchCategories(int searchCatsLimit, String filterValue) throws IOException; Observable<String> searchCategories(String filterValue, int searchCatsLimit);
@NonNull @NonNull
List<String> allCategories(int searchCatsLimit, String filter) throws IOException; Observable<String> allCategories(String filter, int searchCatsLimit);
@NonNull @NonNull
List<String> searchTitles(int searchCatsLimit, String title) throws IOException; Observable<String> searchTitles(String title, int searchCatsLimit);
@Nullable @Nullable
String revisionsByFilename(String filename) throws IOException; String revisionsByFilename(String filename) throws IOException;