Merge pull request #49 from misaochan/subtask-async

Subtask async
This commit is contained in:
Nicolas Raoul 2016-01-27 13:02:08 +09:00
commit 4ee32b282b
5 changed files with 273 additions and 91 deletions

View file

@ -1,5 +1,8 @@
# Wikimedia Commons for Android
## v1.8
- New feature: Improved category search function (not limited to prefix search now)
## v1.7
- Fixed bug with uploading images in Marshmallow
- Fixed links in About page

View file

@ -1,7 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.free.nrw.commons"
android:versionCode="23"
android:versionName="1.7" >
android:versionCode="24"
android:versionName="1.8" >
<uses-sdk
android:minSdkVersion="9"

View file

@ -22,35 +22,44 @@ import fr.free.nrw.commons.R;
import fr.free.nrw.commons.upload.MwVolleyApi;
import java.io.IOException;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ScheduledThreadPoolExecutor;
public class CategorizationFragment extends SherlockFragment{
public static interface OnCategoriesSaveHandler {
public void onCategoriesSave(ArrayList<String> categories);
}
ListView categoriesList;
EditText categoriesFilter;
protected EditText categoriesFilter;
ProgressBar categoriesSearchInProgress;
TextView categoriesNotFoundView;
TextView categoriesSkip;
CategoriesAdapter categoriesAdapter;
CategoriesUpdater lastUpdater = null;
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);
private OnCategoriesSaveHandler onCategoriesSaveHandler;
private HashMap<String, ArrayList<String>> categoriesCache;
protected HashMap<String, ArrayList<String>> categoriesCache;
// LHS guarantees ordered insertions, allowing for prioritized method A results
private final Set<String> results = new LinkedHashSet<String>();
PrefixUpdater prefixUpdaterSub;
MethodAUpdater methodAUpdaterSub;
private ContentProviderClient client;
private final int SEARCH_CATS_LIMIT = 25;
protected final static int SEARCH_CATS_LIMIT = 25;
private static final String TAG = CategorizationFragment.class.getName();
public static class CategoryItem implements Parcelable {
@ -87,55 +96,7 @@ public class CategorizationFragment extends SherlockFragment{
}
}
private class CategoriesUpdater extends AsyncTask<Void, Void, ArrayList<String>> {
private String filter;
@Override
protected void onPreExecute() {
super.onPreExecute();
filter = categoriesFilter.getText().toString();
categoriesSearchInProgress.setVisibility(View.VISIBLE);
categoriesNotFoundView.setVisibility(View.GONE);
categoriesSkip.setVisibility(View.GONE);
}
@Override
protected void onPostExecute(ArrayList<String> categories) {
super.onPostExecute(categories);
ArrayList<CategoryItem> items = new ArrayList<CategoryItem>();
HashSet<String> existingKeys = new HashSet<String>();
for(CategoryItem item : categoriesAdapter.getItems()) {
if(item.selected) {
items.add(item);
existingKeys.add(item.name);
}
}
for(String category : categories) {
if(!existingKeys.contains(category)) {
items.add(new CategoryItem(category, false));
}
}
categoriesAdapter.setItems(items);
categoriesAdapter.notifyDataSetInvalidated();
categoriesSearchInProgress.setVisibility(View.GONE);
if (categories.size() == 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);
}
} else {
categoriesList.smoothScrollToPosition(existingKeys.size());
}
}
@Override
protected ArrayList<String> doInBackground(Void... voids) {
if(TextUtils.isEmpty(filter)) {
protected ArrayList<String> recentCatQuery() {
ArrayList<String> items = new ArrayList<String>();
ArrayList<String> mergedItems= new ArrayList<String>();
@ -151,11 +112,12 @@ public class CategorizationFragment extends SherlockFragment{
Category cat = Category.fromCursor(cursor);
items.add(cat.getName());
}
cursor.close();
if (MwVolleyApi.GpsCatExists.getGpsCatExists() == true){
Log.d(TAG, "GPS cats found in CategorizationFragment.java" + MwVolleyApi.getGpsCat().toString());
if (MwVolleyApi.GpsCatExists.getGpsCatExists() == true) {
//Log.d(TAG, "GPS cats found in CategorizationFragment.java" + MwVolleyApi.getGpsCat().toString());
List<String> gpsItems = new ArrayList<String>(MwVolleyApi.getGpsCat());
Log.d(TAG, "GPS items: " + gpsItems.toString());
//Log.d(TAG, "GPS items: " + gpsItems.toString());
mergedItems.addAll(gpsItems);
}
@ -163,37 +125,48 @@ public class CategorizationFragment extends SherlockFragment{
mergedItems.addAll(items);
}
catch (RemoteException e) {
// faaaail
throw new RuntimeException(e);
}
Log.d(TAG, "Merged items: " + mergedItems.toString());
//Log.d(TAG, "Merged items: " + mergedItems.toString());
return mergedItems;
}
if(categoriesCache.containsKey(filter)) {
return categoriesCache.get(filter);
protected void setCatsAfterAsync(ArrayList<String> categories, String filter) {
if (getActivity() != null) {
ArrayList<CategoryItem> items = new ArrayList<CategoryItem>();
HashSet<String> existingKeys = new HashSet<String>();
for (CategoryItem item : categoriesAdapter.getItems()) {
if (item.selected) {
items.add(item);
existingKeys.add(item.name);
}
}
for (String category : categories) {
if (!existingKeys.contains(category)) {
items.add(new CategoryItem(category, false));
}
MWApi api = CommonsApplication.createMWApi();
ApiResult result;
ArrayList<String> categories = new ArrayList<String>();
try {
result = api.action("query")
.param("list", "allcategories")
.param("acprefix", filter)
.param("aclimit", SEARCH_CATS_LIMIT)
.get();
} catch (IOException e) {
throw new RuntimeException(e);
}
ArrayList<ApiResult> categoryNodes = result.getNodes("/api/query/allcategories/c");
for(ApiResult categoryNode: categoryNodes) {
categories.add(categoryNode.getDocument().getTextContent());
categoriesAdapter.setItems(items);
categoriesAdapter.notifyDataSetInvalidated();
categoriesSearchInProgress.setVisibility(View.GONE);
if (categories.isEmpty()) {
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);
}
categoriesCache.put(filter, categories);
return categories;
} else {
categoriesList.smoothScrollToPosition(existingKeys.size());
}
}
else {
Log.e(TAG, "Error: Fragment is null");
}
}
@ -363,12 +336,70 @@ public class CategorizationFragment extends SherlockFragment{
return rootView;
}
private void startUpdatingCategoryList() {
if (lastUpdater != null) {
lastUpdater.cancel(true);
private void requestSearchResults() {
final CountDownLatch latch = new CountDownLatch(1);
prefixUpdaterSub = new PrefixUpdater(this) {
@Override
protected ArrayList<String> doInBackground(Void... voids) {
ArrayList<String> result = new ArrayList<String>();
try {
result = super.doInBackground();
latch.await();
}
lastUpdater = new CategoriesUpdater();
Utils.executeAsyncTask(lastUpdater, executor);
catch (InterruptedException e) {
Log.w(TAG, e);
//Thread.currentThread().interrupt();
}
return result;
}
@Override
protected void onPostExecute(ArrayList<String> result) {
super.onPostExecute(result);
results.addAll(result);
Log.d(TAG, "Prefix result: " + result);
String filter = categoriesFilter.getText().toString();
ArrayList<String> resultsList = new ArrayList<String>(results);
categoriesCache.put(filter, resultsList);
Log.d(TAG, "Final results List: " + resultsList);
categoriesAdapter.notifyDataSetChanged();
setCatsAfterAsync(resultsList, filter);
}
};
methodAUpdaterSub = new MethodAUpdater(this) {
@Override
protected void onPostExecute(ArrayList<String> result) {
results.clear();
super.onPostExecute(result);
results.addAll(result);
Log.d(TAG, "Method A result: " + result);
categoriesAdapter.notifyDataSetChanged();
latch.countDown();
}
};
Utils.executeAsyncTask(prefixUpdaterSub);
Utils.executeAsyncTask(methodAUpdaterSub);
}
private void startUpdatingCategoryList() {
if (prefixUpdaterSub != null) {
prefixUpdaterSub.cancel(true);
}
if (methodAUpdaterSub != null) {
methodAUpdaterSub.cancel(true);
}
requestSearchResults();
}
@Override

View file

@ -0,0 +1,78 @@
package fr.free.nrw.commons.category;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import org.mediawiki.api.ApiResult;
import org.mediawiki.api.MWApi;
import java.io.IOException;
import java.util.ArrayList;
import fr.free.nrw.commons.CommonsApplication;
public class MethodAUpdater extends AsyncTask<Void, Void, ArrayList<String>> {
private String filter;
private static final String TAG = MethodAUpdater.class.getName();
CategorizationFragment catFragment;
public 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);
}
@Override
protected ArrayList<String> doInBackground(Void... voids) {
//If user hasn't typed anything in yet, get GPS and recent items
if(TextUtils.isEmpty(filter)) {
return catFragment.recentCatQuery();
}
//if user types in something that is in cache, return cached category
if(catFragment.categoriesCache.containsKey(filter)) {
return catFragment.categoriesCache.get(filter);
}
//otherwise if user has typed something in that isn't in cache, search API for matching categories
MWApi api = CommonsApplication.createMWApi();
ApiResult result;
ArrayList<String> categories = new ArrayList<String>();
//URL https://commons.wikimedia.org/w/api.php?action=query&format=xml&list=search&srwhat=text&srenablerewrites=1&srnamespace=14&srlimit=10&srsearch=
try {
result = api.action("query")
.param("format", "xml")
.param("list", "search")
.param("srwhat", "text")
.param("srnamespace", "14")
.param("srlimit", catFragment.SEARCH_CATS_LIMIT)
.param("srsearch", filter)
.get();
Log.d(TAG, "Method A URL filter" + result.toString());
} catch (IOException e) {
throw new RuntimeException(e);
}
ArrayList<ApiResult> categoryNodes = result.getNodes("/api/query/search/p/@title");
for(ApiResult categoryNode: categoryNodes) {
String cat = categoryNode.getDocument().getTextContent();
String catString = cat.replace("Category:", "");
categories.add(catString);
}
return categories;
}
}

View file

@ -0,0 +1,70 @@
package fr.free.nrw.commons.category;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import org.mediawiki.api.ApiResult;
import org.mediawiki.api.MWApi;
import java.io.IOException;
import java.util.ArrayList;
import fr.free.nrw.commons.CommonsApplication;
public class PrefixUpdater extends AsyncTask<Void, Void, ArrayList<String>> {
private String filter;
private static final String TAG = PrefixUpdater.class.getName();
private CategorizationFragment catFragment;
public 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);
}
@Override
protected ArrayList<String> doInBackground(Void... voids) {
//If user hasn't typed anything in yet, get GPS and recent items
if(TextUtils.isEmpty(filter)) {
return catFragment.recentCatQuery();
}
//if user types in something that is in cache, return cached category
if(catFragment.categoriesCache.containsKey(filter)) {
return catFragment.categoriesCache.get(filter);
}
//otherwise if user has typed something in that isn't in cache, search API for matching categories
MWApi api = CommonsApplication.createMWApi();
ApiResult result;
ArrayList<String> categories = new ArrayList<String>();
try {
result = api.action("query")
.param("list", "allcategories")
.param("acprefix", filter)
.param("aclimit", catFragment.SEARCH_CATS_LIMIT)
.get();
Log.d(TAG, "Prefix URL filter" + result.toString());
} catch (IOException e) {
throw new RuntimeException(e);
}
ArrayList<ApiResult> categoryNodes = result.getNodes("/api/query/allcategories/c");
for(ApiResult categoryNode: categoryNodes) {
categories.add(categoryNode.getDocument().getTextContent());
}
return categories;
}
}