mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-30 22:34:02 +01:00 
			
		
		
		
	Swapped the ListView for a RecyclerView, made the fragment cleaner and fixed a whole bunch of code inspection issues.
This commit is contained in:
		
							parent
							
								
									f73a9f15fc
								
							
						
					
					
						commit
						9c987efbd2
					
				
					 16 changed files with 435 additions and 402 deletions
				
			
		|  | @ -1,67 +0,0 @@ | |||
| package fr.free.nrw.commons.category; | ||||
| 
 | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.BaseAdapter; | ||||
| import android.widget.CheckedTextView; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| 
 | ||||
| import fr.free.nrw.commons.R; | ||||
| 
 | ||||
| public class CategoriesAdapter extends BaseAdapter { | ||||
| 
 | ||||
|     private LayoutInflater mInflater; | ||||
| 
 | ||||
|     private ArrayList<CategorizationFragment.CategoryItem> items; | ||||
| 
 | ||||
|     public CategoriesAdapter(Context context, ArrayList<CategorizationFragment.CategoryItem> items) { | ||||
|         this.items = items; | ||||
|         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int getCount() { | ||||
|         return items.size(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Object getItem(int i) { | ||||
|         return items.get(i); | ||||
|     } | ||||
| 
 | ||||
|     public ArrayList<CategorizationFragment.CategoryItem> getItems() { | ||||
|         return items; | ||||
|     } | ||||
| 
 | ||||
|     public void setItems(ArrayList<CategorizationFragment.CategoryItem> items) { | ||||
|         this.items = items; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public long getItemId(int i) { | ||||
|         return i; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public View getView(int i, View view, ViewGroup viewGroup) { | ||||
|         CheckedTextView checkedView; | ||||
| 
 | ||||
|         if(view == null) { | ||||
|             checkedView = (CheckedTextView) mInflater.inflate(R.layout.layout_categories_item, null); | ||||
| 
 | ||||
|         } else { | ||||
|             checkedView = (CheckedTextView) view; | ||||
|         } | ||||
| 
 | ||||
|         CategorizationFragment.CategoryItem item = (CategorizationFragment.CategoryItem) this.getItem(i); | ||||
|         checkedView.setChecked(item.selected); | ||||
|         checkedView.setText(item.name); | ||||
|         checkedView.setTag(i); | ||||
| 
 | ||||
|         return checkedView; | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,24 @@ | |||
| package fr.free.nrw.commons.category; | ||||
| 
 | ||||
| import com.pedrogomez.renderers.ListAdapteeCollection; | ||||
| import com.pedrogomez.renderers.RVRendererAdapter; | ||||
| import com.pedrogomez.renderers.RendererBuilder; | ||||
| 
 | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| 
 | ||||
| class CategoriesAdapterFactory { | ||||
|     private final CategoriesRenderer.CategoryClickedListener listener; | ||||
| 
 | ||||
|     CategoriesAdapterFactory(CategoriesRenderer.CategoryClickedListener listener) { | ||||
|         this.listener = listener; | ||||
|     } | ||||
| 
 | ||||
|     public RVRendererAdapter<CategoryItem> create(List<CategoryItem> placeList) { | ||||
|         RendererBuilder<CategoryItem> builder = new RendererBuilder<CategoryItem>() | ||||
|                 .bind(CategoryItem.class, new CategoriesRenderer(listener)); | ||||
|         ListAdapteeCollection<CategoryItem> collection = new ListAdapteeCollection<>( | ||||
|                 placeList != null ? placeList : Collections.<CategoryItem>emptyList()); | ||||
|         return new RVRendererAdapter<>(builder, collection); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,57 @@ | |||
| package fr.free.nrw.commons.category; | ||||
| 
 | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.CheckedTextView; | ||||
| 
 | ||||
| import com.pedrogomez.renderers.Renderer; | ||||
| 
 | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import fr.free.nrw.commons.R; | ||||
| 
 | ||||
| class CategoriesRenderer extends Renderer<CategoryItem> { | ||||
|     @BindView(R.id.tvName) CheckedTextView checkedView; | ||||
|     private final CategoryClickedListener listener; | ||||
| 
 | ||||
|     CategoriesRenderer(CategoryClickedListener listener) { | ||||
|         this.listener = listener; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) { | ||||
|         return layoutInflater.inflate(R.layout.layout_categories_item, viewGroup, false); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void setUpView(View view) { | ||||
|         ButterKnife.bind(this, view); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void hookListeners(View view) { | ||||
|         view.setOnClickListener(new View.OnClickListener() { | ||||
|             @Override | ||||
|             public void onClick(View v) { | ||||
|                 CategoryItem item = getContent(); | ||||
|                 item.selected = !item.selected; | ||||
|                 checkedView.setChecked(item.selected); | ||||
|                 if (listener != null) { | ||||
|                     listener.categoryClicked(item); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void render() { | ||||
|         CategoryItem item = getContent(); | ||||
|         checkedView.setChecked(item.selected); | ||||
|         checkedView.setText(item.name); | ||||
|     } | ||||
| 
 | ||||
|     interface CategoryClickedListener { | ||||
|         void categoryClicked(CategoryItem item); | ||||
|     } | ||||
| } | ||||
|  | @ -6,12 +6,12 @@ import android.content.SharedPreferences; | |||
| import android.database.Cursor; | ||||
| import android.os.AsyncTask; | ||||
| import android.os.Bundle; | ||||
| import android.os.Parcel; | ||||
| import android.os.Parcelable; | ||||
| import android.os.RemoteException; | ||||
| import android.preference.PreferenceManager; | ||||
| import android.support.v4.app.Fragment; | ||||
| import android.support.v7.app.AlertDialog; | ||||
| import android.support.v7.widget.LinearLayoutManager; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.text.Editable; | ||||
| import android.text.TextUtils; | ||||
| import android.text.TextWatcher; | ||||
|  | @ -22,15 +22,14 @@ import android.view.MenuInflater; | |||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.AdapterView; | ||||
| import android.widget.CheckedTextView; | ||||
| import android.widget.EditText; | ||||
| import android.widget.ListView; | ||||
| import android.widget.ProgressBar; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import com.pedrogomez.renderers.ListAdapteeCollection; | ||||
| import com.pedrogomez.renderers.RVRendererAdapter; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Date; | ||||
| import java.util.HashMap; | ||||
| import java.util.HashSet; | ||||
| import java.util.LinkedHashSet; | ||||
|  | @ -40,6 +39,8 @@ import java.util.concurrent.CountDownLatch; | |||
| import java.util.concurrent.ScheduledThreadPoolExecutor; | ||||
| import java.util.concurrent.TimeUnit; | ||||
| 
 | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import fr.free.nrw.commons.R; | ||||
| import fr.free.nrw.commons.upload.MwVolleyApi; | ||||
| import timber.log.Timber; | ||||
|  | @ -47,83 +48,180 @@ import timber.log.Timber; | |||
| /** | ||||
|  * Displays the category suggestion and selection screen. Category search is initiated here. | ||||
|  */ | ||||
| public class CategorizationFragment extends Fragment { | ||||
|     public interface OnCategoriesSaveHandler { | ||||
|         void onCategoriesSave(ArrayList<String> categories); | ||||
|     } | ||||
| public class CategorizationFragment extends Fragment implements CategoriesRenderer.CategoryClickedListener { | ||||
|     public static final int SEARCH_CATS_LIMIT = 25; | ||||
| 
 | ||||
|     ListView categoriesList; | ||||
|     protected EditText categoriesFilter; | ||||
|     ProgressBar categoriesSearchInProgress; | ||||
|     TextView categoriesNotFoundView; | ||||
|     TextView categoriesSkip; | ||||
|     private CategoryTextWatcher textWatcher = new CategoryTextWatcher(); | ||||
| 
 | ||||
|     CategoriesAdapter categoriesAdapter; | ||||
|     ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2); | ||||
|     @BindView(R.id.categoriesListBox) RecyclerView categoriesList; | ||||
|     @BindView(R.id.categoriesSearchBox) EditText categoriesFilter; | ||||
|     @BindView(R.id.categoriesSearchInProgress) ProgressBar categoriesSearchInProgress; | ||||
|     @BindView(R.id.categoriesNotFound) TextView categoriesNotFoundView; | ||||
|     @BindView(R.id.categoriesExplanation) TextView categoriesSkip; | ||||
| 
 | ||||
|     private RVRendererAdapter<CategoryItem> categoriesAdapter; | ||||
|     private OnCategoriesSaveHandler onCategoriesSaveHandler; | ||||
| 
 | ||||
|     protected HashMap<String, ArrayList<String>> categoriesCache; | ||||
| 
 | ||||
|     private HashMap<String, ArrayList<String>> categoriesCache; | ||||
|     private ArrayList<String> selectedCategories = new ArrayList<>(); | ||||
| 
 | ||||
|     private ContentProviderClient client; | ||||
|     private PrefixUpdater prefixUpdaterSub; | ||||
|     private MethodAUpdater methodAUpdaterSub; | ||||
|     private final CategoryTextWatcher textWatcher = new CategoryTextWatcher(); | ||||
|     private final CategoriesAdapterFactory adapterFactory = new CategoriesAdapterFactory(this); | ||||
|     private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2); | ||||
|     private final ArrayList<String> titleCatItems = new ArrayList<>(); | ||||
|     private final CountDownLatch mergeLatch = new CountDownLatch(1); | ||||
|     // LHS guarantees ordered insertions, allowing for prioritized method A results | ||||
|     private final Set<String> results = new LinkedHashSet<>(); | ||||
|     PrefixUpdater prefixUpdaterSub; | ||||
|     MethodAUpdater methodAUpdaterSub; | ||||
| 
 | ||||
|     private final ArrayList<String> titleCatItems = new ArrayList<>(); | ||||
|     final CountDownLatch mergeLatch = new CountDownLatch(1); | ||||
|     @SuppressWarnings("unchecked") | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||||
|         View rootView = inflater.inflate(R.layout.fragment_categorization, container, false); | ||||
|         ButterKnife.bind(this, rootView); | ||||
| 
 | ||||
|     private ContentProviderClient client; | ||||
|         categoriesList.setLayoutManager(new LinearLayoutManager(getContext())); | ||||
| 
 | ||||
|     protected final static int SEARCH_CATS_LIMIT = 25; | ||||
| 
 | ||||
|     public static class CategoryItem implements Parcelable { | ||||
|         public String name; | ||||
|         public boolean selected; | ||||
| 
 | ||||
|         public static Creator<CategoryItem> CREATOR = new Creator<CategoryItem>() { | ||||
|         categoriesSkip.setOnClickListener(new View.OnClickListener() { | ||||
|             @Override | ||||
|             public CategoryItem createFromParcel(Parcel parcel) { | ||||
|                 return new CategoryItem(parcel); | ||||
|             public void onClick(View view) { | ||||
|                 getActivity().onBackPressed(); | ||||
|                 getActivity().finish(); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|             @Override | ||||
|             public CategoryItem[] newArray(int i) { | ||||
|                 return new CategoryItem[0]; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         public CategoryItem(String name, boolean selected) { | ||||
|             this.name = name; | ||||
|             this.selected = selected; | ||||
|         ArrayList<CategoryItem> items; | ||||
|         if (savedInstanceState == null) { | ||||
|             items = new ArrayList<>(); | ||||
|             categoriesCache = new HashMap<>(); | ||||
|         } else { | ||||
|             items = savedInstanceState.getParcelableArrayList("currentCategories"); | ||||
|             categoriesCache = (HashMap<String, ArrayList<String>>) savedInstanceState.getSerializable("categoriesCache"); | ||||
|         } | ||||
| 
 | ||||
|         public CategoryItem(Parcel in) { | ||||
|             name = in.readString(); | ||||
|             selected = in.readInt() == 1; | ||||
|         } | ||||
|         categoriesAdapter = adapterFactory.create(items); | ||||
|         categoriesList.setAdapter(categoriesAdapter); | ||||
|         categoriesFilter.addTextChangedListener(textWatcher); | ||||
| 
 | ||||
|         @Override | ||||
|         public int describeContents() { | ||||
|             return 0; | ||||
|         } | ||||
|         startUpdatingCategoryList(); | ||||
| 
 | ||||
|         @Override | ||||
|         public void writeToParcel(Parcel parcel, int flags) { | ||||
|             parcel.writeString(name); | ||||
|             parcel.writeInt(selected ? 1 : 0); | ||||
|         return rootView; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { | ||||
|         menu.clear(); | ||||
|         inflater.inflate(R.menu.fragment_categorization, menu); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onResume() { | ||||
|         super.onResume(); | ||||
| 
 | ||||
|         View rootView = getView(); | ||||
|         if (rootView != null) { | ||||
|             rootView.setFocusableInTouchMode(true); | ||||
|             rootView.requestFocus(); | ||||
|             rootView.setOnKeyListener(new View.OnKeyListener() { | ||||
|                 @Override | ||||
|                 public boolean onKey(View v, int keyCode, KeyEvent event) { | ||||
|                     if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) { | ||||
|                         backButtonDialog(); | ||||
|                         return true; | ||||
|                     } | ||||
|                     return false; | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         categoriesFilter.removeTextChangedListener(textWatcher); | ||||
|         super.onDestroyView(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         client.release(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onSaveInstanceState(Bundle outState) { | ||||
|         super.onSaveInstanceState(outState); | ||||
|         int itemCount = categoriesAdapter.getItemCount(); | ||||
|         ArrayList<CategoryItem> items = new ArrayList<>(itemCount); | ||||
|         for (int i = 0; i < itemCount; i++) { | ||||
|             items.add(categoriesAdapter.getItem(i)); | ||||
|         } | ||||
|         outState.putParcelableArrayList("currentCategories", items); | ||||
|         outState.putSerializable("categoriesCache", categoriesCache); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem menuItem) { | ||||
|         switch (menuItem.getItemId()) { | ||||
|             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.selected) { | ||||
|                         selectedCategories.add(item.name); | ||||
|                         numberSelected++; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 //If no categories selected, display warning to user | ||||
|                 if (numberSelected == 0) { | ||||
|                     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", new DialogInterface.OnClickListener() { | ||||
|                                 @Override | ||||
|                                 public void onClick(DialogInterface dialog, int id) { | ||||
|                                     //Exit menuItem so user can select their categories | ||||
|                                 } | ||||
|                             }) | ||||
|                             .setNegativeButton("Yes, submit", new DialogInterface.OnClickListener() { | ||||
|                                 @Override | ||||
|                                 public void onClick(DialogInterface dialog, int id) { | ||||
|                                     //Proceed to submission | ||||
|                                     onCategoriesSaveHandler.onCategoriesSave(selectedCategories); | ||||
|                                 } | ||||
|                             }) | ||||
|                             .create() | ||||
|                             .show(); | ||||
|                 } else { | ||||
|                     //Proceed to submission | ||||
|                     onCategoriesSaveHandler.onCategoriesSave(selectedCategories); | ||||
|                     return true; | ||||
|                 } | ||||
|         } | ||||
|         return super.onOptionsItemSelected(menuItem); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onActivityCreated(Bundle savedInstanceState) { | ||||
|         super.onActivityCreated(savedInstanceState); | ||||
|         setHasOptionsMenu(true); | ||||
|         onCategoriesSaveHandler = (OnCategoriesSaveHandler) getActivity(); | ||||
|         getActivity().setTitle(R.string.categories_activity_title); | ||||
|         client = getActivity().getContentResolver().acquireContentProviderClient(CategoryContentProvider.AUTHORITY); | ||||
|     } | ||||
| 
 | ||||
|     public HashMap<String, ArrayList<String>> getCategoriesCache() { | ||||
|         return categoriesCache; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieves category suggestions from title input | ||||
|      * | ||||
|      * @return a list containing title-related categories | ||||
|      */ | ||||
|     protected ArrayList<String> titleCatQuery() { | ||||
| 
 | ||||
|     private ArrayList<String> titleCatQuery() { | ||||
|         TitleCategories titleCategoriesSub; | ||||
| 
 | ||||
|         //Retrieve the title that was saved when user tapped submit icon | ||||
|  | @ -157,37 +255,41 @@ public class CategorizationFragment extends Fragment { | |||
| 
 | ||||
|     /** | ||||
|      * Retrieves recently-used categories | ||||
|      * | ||||
|      * @return a list containing recent categories | ||||
|      */ | ||||
|     protected ArrayList<String> recentCatQuery() { | ||||
|     private ArrayList<String> recentCatQuery() { | ||||
|         ArrayList<String> items = new ArrayList<>(); | ||||
| 
 | ||||
|         Cursor cursor = null; | ||||
|         try { | ||||
|             Cursor cursor = client.query( | ||||
|             cursor = client.query( | ||||
|                     CategoryContentProvider.BASE_URI, | ||||
|                     Category.Table.ALL_FIELDS, | ||||
|                     null, | ||||
|                     new String[]{}, | ||||
|                     Category.Table.COLUMN_LAST_USED + " DESC"); | ||||
|             // fixme add a limit on the original query instead of falling out of the loop? | ||||
|             while (cursor.moveToNext() && cursor.getPosition() < SEARCH_CATS_LIMIT) { | ||||
|             while (cursor != null && cursor.moveToNext() | ||||
|                     && cursor.getPosition() < SEARCH_CATS_LIMIT) { | ||||
|                 Category cat = Category.fromCursor(cursor); | ||||
|                 items.add(cat.getName()); | ||||
|             } | ||||
|             cursor.close(); | ||||
|         } | ||||
|         catch (RemoteException e) { | ||||
|         } catch (RemoteException e) { | ||||
|             throw new RuntimeException(e); | ||||
|         } finally { | ||||
|             if (cursor != null) { | ||||
|                 cursor.close(); | ||||
|             } | ||||
|         } | ||||
|         return items; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Merges nearby categories, categories suggested based on title, and recent categories... without duplicates. | ||||
|      * | ||||
|      * @return a list containing merged categories | ||||
|      */ | ||||
|     protected ArrayList<String> mergeItems() { | ||||
| 
 | ||||
|     ArrayList<String> mergeItems() { | ||||
|         Set<String> mergedItems = new LinkedHashSet<>(); | ||||
| 
 | ||||
|         Timber.d("Calling APIs for GPS cats, title cats and recent cats..."); | ||||
|  | @ -213,7 +315,7 @@ public class CategorizationFragment extends Fragment { | |||
|         Timber.d("Adding title items: %s", titleItems); | ||||
|         mergedItems.addAll(recentItems); | ||||
|         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); | ||||
| 
 | ||||
|  | @ -223,15 +325,17 @@ public class CategorizationFragment extends Fragment { | |||
| 
 | ||||
|     /** | ||||
|      * Displays categories found to the user as they type in the search box | ||||
|      * | ||||
|      * @param categories a list of all categories found for the search string | ||||
|      * @param filter the search string | ||||
|      * @param filter     the search string | ||||
|      */ | ||||
|     protected void setCatsAfterAsync(ArrayList<String> categories, String filter) { | ||||
| 
 | ||||
|     private void setCatsAfterAsync(ArrayList<String> categories, String filter) { | ||||
|         if (getActivity() != null) { | ||||
|             ArrayList<CategoryItem> items = new ArrayList<>(); | ||||
|             HashSet<String> existingKeys = new HashSet<>(); | ||||
|             for (CategoryItem item : categoriesAdapter.getItems()) { | ||||
|             int count = categoriesAdapter.getItemCount(); | ||||
|             for (int i = 0; i < count; i++) { | ||||
|                 CategoryItem item = categoriesAdapter.getItem(i); | ||||
|                 if (item.selected) { | ||||
|                     items.add(item); | ||||
|                     existingKeys.add(item.name); | ||||
|  | @ -243,8 +347,8 @@ public class CategorizationFragment extends Fragment { | |||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             categoriesAdapter.setItems(items); | ||||
|             categoriesAdapter.notifyDataSetInvalidated(); | ||||
|             categoriesAdapter.setCollection(new ListAdapteeCollection<>(items)); | ||||
|             categoriesAdapter.notifyDataSetChanged(); | ||||
|             categoriesSearchInProgress.setVisibility(View.GONE); | ||||
| 
 | ||||
|             if (categories.isEmpty()) { | ||||
|  | @ -258,8 +362,7 @@ public class CategorizationFragment extends Fragment { | |||
|             } else { | ||||
|                 categoriesList.smoothScrollToPosition(existingKeys.size()); | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|         } else { | ||||
|             Timber.e("Error: Fragment is null"); | ||||
|         } | ||||
|     } | ||||
|  | @ -272,7 +375,6 @@ public class CategorizationFragment extends Fragment { | |||
|      * above Prefix results. | ||||
|      */ | ||||
|     private void requestSearchResults() { | ||||
| 
 | ||||
|         final CountDownLatch latch = new CountDownLatch(1); | ||||
| 
 | ||||
|         prefixUpdaterSub = new PrefixUpdater(this) { | ||||
|  | @ -282,8 +384,7 @@ public class CategorizationFragment extends Fragment { | |||
|                 try { | ||||
|                     result = super.doInBackground(); | ||||
|                     latch.await(); | ||||
|                 } | ||||
|                 catch (InterruptedException e) { | ||||
|                 } catch (InterruptedException e) { | ||||
|                     Timber.w(e); | ||||
|                     //Thread.currentThread().interrupt(); | ||||
|                 } | ||||
|  | @ -325,7 +426,6 @@ public class CategorizationFragment extends Fragment { | |||
|     } | ||||
| 
 | ||||
|     private void startUpdatingCategoryList() { | ||||
| 
 | ||||
|         if (prefixUpdaterSub != null) { | ||||
|             prefixUpdaterSub.cancel(true); | ||||
|         } | ||||
|  | @ -339,238 +439,41 @@ public class CategorizationFragment extends Fragment { | |||
| 
 | ||||
|     public int getCurrentSelectedCount() { | ||||
|         int count = 0; | ||||
|         for(CategoryItem item: categoriesAdapter.getItems()) { | ||||
|             if(item.selected) { | ||||
|         int numberOfItems = categoriesAdapter.getItemCount(); | ||||
|         for (int i = 0; i < numberOfItems; i++) { | ||||
|             CategoryItem item = categoriesAdapter.getItem(i); | ||||
|             if (item.selected) { | ||||
|                 count++; | ||||
|             } | ||||
|         } | ||||
|         return count; | ||||
|     } | ||||
| 
 | ||||
|     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.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; | ||||
|     } | ||||
| 
 | ||||
|     private class CategoryCountUpdater extends AsyncTask<Void, Void, Void> { | ||||
| 
 | ||||
|         private String name; | ||||
| 
 | ||||
|         public CategoryCountUpdater(String name) { | ||||
|             this.name = name; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         protected Void doInBackground(Void... voids) { | ||||
|             Category cat = lookupCategory(name); | ||||
|             cat.incTimesUsed(); | ||||
| 
 | ||||
|             cat.setContentProviderClient(client); | ||||
|             cat.save(); | ||||
| 
 | ||||
|             return null; // Make the compiler happy. | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void updateCategoryCount(String name) { | ||||
|         new CategoryCountUpdater(name).executeOnExecutor(executor); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||||
|         View rootView = inflater.inflate(R.layout.fragment_categorization, null); | ||||
|         categoriesList = (ListView) rootView.findViewById(R.id.categoriesListBox); | ||||
|         categoriesFilter = (EditText) rootView.findViewById(R.id.categoriesSearchBox); | ||||
|         categoriesSearchInProgress = (ProgressBar) rootView.findViewById(R.id.categoriesSearchInProgress); | ||||
|         categoriesNotFoundView = (TextView) rootView.findViewById(R.id.categoriesNotFound); | ||||
|         categoriesSkip = (TextView) rootView.findViewById(R.id.categoriesExplanation); | ||||
| 
 | ||||
|         categoriesSkip.setOnClickListener(new View.OnClickListener() { | ||||
|             @Override | ||||
|             public void onClick(View view) { | ||||
|                 getActivity().onBackPressed(); | ||||
|                 getActivity().finish(); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         ArrayList<CategoryItem> items; | ||||
|         if(savedInstanceState == null) { | ||||
|             items = new ArrayList<>(); | ||||
|             categoriesCache = new HashMap<>(); | ||||
|         } else { | ||||
|             items = savedInstanceState.getParcelableArrayList("currentCategories"); | ||||
|             categoriesCache = (HashMap<String, ArrayList<String>>) savedInstanceState.getSerializable("categoriesCache"); | ||||
|         } | ||||
| 
 | ||||
|         categoriesAdapter = new CategoriesAdapter(getActivity(), items); | ||||
|         categoriesList.setAdapter(categoriesAdapter); | ||||
| 
 | ||||
|         categoriesList.setOnItemClickListener(new AdapterView.OnItemClickListener() { | ||||
|             @Override | ||||
|             public void onItemClick(AdapterView<?> adapterView, View view, int index, long id) { | ||||
|                 CheckedTextView checkedView = (CheckedTextView) view; | ||||
|                 CategoryItem item = (CategoryItem) adapterView.getAdapter().getItem(index); | ||||
|                 item.selected = !item.selected; | ||||
|                 checkedView.setChecked(item.selected); | ||||
|                 if (item.selected) { | ||||
|                     updateCategoryCount(item.name); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         categoriesFilter.addTextChangedListener(textWatcher); | ||||
| 
 | ||||
|         startUpdatingCategoryList(); | ||||
| 
 | ||||
|         return rootView; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { | ||||
|         menu.clear(); | ||||
|         inflater.inflate(R.menu.fragment_categorization, menu); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onResume() { | ||||
|         super.onResume(); | ||||
| 
 | ||||
|         View rootView = getView(); | ||||
|         if (rootView != null) { | ||||
|             rootView.setFocusableInTouchMode(true); | ||||
|             rootView.requestFocus(); | ||||
|             rootView.setOnKeyListener(new View.OnKeyListener() { | ||||
|                 @Override | ||||
|                 public boolean onKey(View v, int keyCode, KeyEvent event) { | ||||
|                     if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) { | ||||
|                         backButtonDialog(); | ||||
|                         return true; | ||||
|                     } | ||||
|                     return false; | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         categoriesFilter.removeTextChangedListener(textWatcher); | ||||
|         super.onDestroyView(); | ||||
|     } | ||||
| 
 | ||||
|     public void backButtonDialog() { | ||||
|         AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); | ||||
| 
 | ||||
|         builder.setMessage("Are you sure you want to go back? The image will not have any categories saved.") | ||||
|                 .setTitle("Warning"); | ||||
|         builder.setPositiveButton("No", new DialogInterface.OnClickListener() { | ||||
|             @Override | ||||
|             public void onClick(DialogInterface dialog, int id) { | ||||
|                 //No need to do anything, user remains on categorization screen | ||||
|             } | ||||
|         }); | ||||
|         builder.setNegativeButton("Yes", new DialogInterface.OnClickListener() { | ||||
|             @Override | ||||
|             public void onClick(DialogInterface dialog, int id) { | ||||
|                 getActivity().finish(); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         AlertDialog dialog = builder.create(); | ||||
|         dialog.show(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         client.release(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onSaveInstanceState(Bundle outState) { | ||||
|         super.onSaveInstanceState(outState); | ||||
|         outState.putParcelableArrayList("currentCategories", categoriesAdapter.getItems()); | ||||
|         outState.putSerializable("categoriesCache", categoriesCache); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem menuItem) { | ||||
|         switch(menuItem.getItemId()) { | ||||
|             case R.id.menu_save_categories: | ||||
| 
 | ||||
|                 int numberSelected = 0; | ||||
| 
 | ||||
|                 for(CategoryItem item: categoriesAdapter.getItems()) { | ||||
|                     if(item.selected) { | ||||
|                         selectedCategories.add(item.name); | ||||
|                         numberSelected++; | ||||
|         new AlertDialog.Builder(getActivity()) | ||||
|                 .setMessage("Are you sure you want to go back? The image will not have any categories saved.") | ||||
|                 .setTitle("Warning") | ||||
|                 .setPositiveButton("No", new DialogInterface.OnClickListener() { | ||||
|                     @Override | ||||
|                     public void onClick(DialogInterface dialog, int id) { | ||||
|                         //No need to do anything, user remains on categorization screen | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 //If no categories selected, display warning to user | ||||
|                 if (numberSelected == 0) { | ||||
|                     AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); | ||||
| 
 | ||||
|                     builder.setMessage("Images without categories are rarely usable. Are you sure you want to submit without selecting categories?") | ||||
|                             .setTitle("No Categories Selected"); | ||||
|                     builder.setPositiveButton("No, go back", new DialogInterface.OnClickListener() { | ||||
|                         @Override | ||||
|                         public void onClick(DialogInterface dialog, int id) { | ||||
|                             //Exit menuItem so user can select their categories | ||||
|                             return; | ||||
|                         } | ||||
|                     }); | ||||
|                     builder.setNegativeButton("Yes, submit", new DialogInterface.OnClickListener() { | ||||
|                         @Override | ||||
|                         public void onClick(DialogInterface dialog, int id) { | ||||
|                             //Proceed to submission | ||||
|                             onCategoriesSaveHandler.onCategoriesSave(selectedCategories); | ||||
|                             return; | ||||
|                         } | ||||
|                     }); | ||||
| 
 | ||||
|                     AlertDialog dialog = builder.create(); | ||||
|                     dialog.show(); | ||||
|                 } else { | ||||
|                     //Proceed to submission | ||||
|                     onCategoriesSaveHandler.onCategoriesSave(selectedCategories); | ||||
|                     return true; | ||||
|                 } | ||||
|         } | ||||
|         return super.onOptionsItemSelected(menuItem); | ||||
|                 }) | ||||
|                 .setNegativeButton("Yes", new DialogInterface.OnClickListener() { | ||||
|                     @Override | ||||
|                     public void onClick(DialogInterface dialog, int id) { | ||||
|                         getActivity().finish(); | ||||
|                     } | ||||
|                 }) | ||||
|                 .create() | ||||
|                 .show(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onActivityCreated(Bundle savedInstanceState) { | ||||
|         super.onActivityCreated(savedInstanceState); | ||||
|         setHasOptionsMenu(true); | ||||
|         onCategoriesSaveHandler = (OnCategoriesSaveHandler) getActivity(); | ||||
|         getActivity().setTitle(R.string.categories_activity_title); | ||||
|         client = getActivity().getContentResolver().acquireContentProviderClient(CategoryContentProvider.AUTHORITY); | ||||
|     public void categoryClicked(CategoryItem item) { | ||||
|         if (item.selected) { | ||||
|             new CategoryCountUpdater(item.name, client).executeOnExecutor(executor); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private class CategoryTextWatcher implements TextWatcher { | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ public class Category { | |||
|         this.name = name; | ||||
|     } | ||||
| 
 | ||||
|     public Date getLastUsed() { | ||||
|     private Date getLastUsed() { | ||||
|         // warning: Date objects are mutable. | ||||
|         return (Date)lastUsed.clone(); | ||||
|     } | ||||
|  | @ -36,11 +36,11 @@ public class Category { | |||
|         this.lastUsed = (Date)lastUsed.clone(); | ||||
|     } | ||||
| 
 | ||||
|     public void touch() { | ||||
|     private void touch() { | ||||
|         lastUsed = new Date(); | ||||
|     } | ||||
| 
 | ||||
|     public int getTimesUsed() { | ||||
|     private int getTimesUsed() { | ||||
|         return timesUsed; | ||||
|     } | ||||
| 
 | ||||
|  | @ -70,7 +70,7 @@ public class Category { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public ContentValues toContentValues() { | ||||
|     private ContentValues toContentValues() { | ||||
|         ContentValues cv = new ContentValues(); | ||||
|         cv.put(Table.COLUMN_NAME, getName()); | ||||
|         cv.put(Table.COLUMN_LAST_USED, getLastUsed().getTime()); | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import android.database.Cursor; | |||
| import android.database.sqlite.SQLiteDatabase; | ||||
| import android.database.sqlite.SQLiteQueryBuilder; | ||||
| import android.net.Uri; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.text.TextUtils; | ||||
| 
 | ||||
| import fr.free.nrw.commons.CommonsApplication; | ||||
|  | @ -41,8 +42,9 @@ public class CategoryContentProvider extends ContentProvider { | |||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("ConstantConditions") | ||||
|     @Override | ||||
|     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { | ||||
|     public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { | ||||
|         SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); | ||||
|         queryBuilder.setTables(Category.Table.TABLE_NAME); | ||||
| 
 | ||||
|  | @ -75,15 +77,16 @@ public class CategoryContentProvider extends ContentProvider { | |||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String getType(Uri uri) { | ||||
|     public String getType(@NonNull Uri uri) { | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("ConstantConditions") | ||||
|     @Override | ||||
|     public Uri insert(Uri uri, ContentValues contentValues) { | ||||
|     public Uri insert(@NonNull Uri uri, ContentValues contentValues) { | ||||
|         int uriType = uriMatcher.match(uri); | ||||
|         SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); | ||||
|         long id = 0; | ||||
|         long id; | ||||
|         switch (uriType) { | ||||
|             case CATEGORIES: | ||||
|                 id = sqlDB.insert(Category.Table.TABLE_NAME, null, contentValues); | ||||
|  | @ -96,12 +99,13 @@ public class CategoryContentProvider extends ContentProvider { | |||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int delete(Uri uri, String s, String[] strings) { | ||||
|     public int delete(@NonNull Uri uri, String s, String[] strings) { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("ConstantConditions") | ||||
|     @Override | ||||
|     public int bulkInsert(Uri uri, ContentValues[] values) { | ||||
|     public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) { | ||||
|         Timber.d("Hello, bulk insert! (CategoryContentProvider)"); | ||||
|         int uriType = uriMatcher.match(uri); | ||||
|         SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); | ||||
|  | @ -122,8 +126,9 @@ public class CategoryContentProvider extends ContentProvider { | |||
|         return values.length; | ||||
|     } | ||||
| 
 | ||||
|     @SuppressWarnings("ConstantConditions") | ||||
|     @Override | ||||
|     public int update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) { | ||||
|     public int update(@NonNull Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) { | ||||
|         /* | ||||
|         SQL Injection warnings: First, note that we're not exposing this to the outside world (exported="false") | ||||
|         Even then, we should make sure to sanitize all user input appropriately. Input that passes through ContentValues | ||||
|  | @ -133,7 +138,7 @@ public class CategoryContentProvider extends ContentProvider { | |||
|          */ | ||||
|         int uriType = uriMatcher.match(uri); | ||||
|         SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); | ||||
|         int rowsUpdated = 0; | ||||
|         int rowsUpdated; | ||||
|         switch (uriType) { | ||||
|             case CATEGORIES_ID: | ||||
|                 int id = Integer.valueOf(uri.getLastPathSegment()); | ||||
|  |  | |||
|  | @ -0,0 +1,59 @@ | |||
| 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; | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,42 @@ | |||
| package fr.free.nrw.commons.category; | ||||
| 
 | ||||
| import android.os.Parcel; | ||||
| import android.os.Parcelable; | ||||
| 
 | ||||
| class CategoryItem implements Parcelable { | ||||
|     public final String name; | ||||
|     public 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]; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     CategoryItem(String name, boolean selected) { | ||||
|         this.name = name; | ||||
|         this.selected = selected; | ||||
|     } | ||||
| 
 | ||||
|     private CategoryItem(Parcel in) { | ||||
|         name = in.readString(); | ||||
|         selected = in.readInt() == 1; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int describeContents() { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void writeToParcel(Parcel parcel, int flags) { | ||||
|         parcel.writeString(name); | ||||
|         parcel.writeInt(selected ? 1 : 0); | ||||
|     } | ||||
| } | ||||
|  | @ -13,6 +13,8 @@ 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 | ||||
|  | @ -20,8 +22,8 @@ import timber.log.Timber; | |||
|  */ | ||||
| class MethodAUpdater extends AsyncTask<Void, Void, List<String>> { | ||||
| 
 | ||||
|     private final CategorizationFragment catFragment; | ||||
|     private String filter; | ||||
|     private CategorizationFragment catFragment; | ||||
| 
 | ||||
|     MethodAUpdater(CategorizationFragment catFragment) { | ||||
|         this.catFragment = catFragment; | ||||
|  | @ -84,7 +86,7 @@ class MethodAUpdater extends AsyncTask<Void, Void, List<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 { | ||||
|             categories = api.searchCategories(CategorizationFragment.SEARCH_CATS_LIMIT, filter); | ||||
|             categories = api.searchCategories(SEARCH_CATS_LIMIT, filter); | ||||
|             Timber.d("Method A URL filter %s", categories); | ||||
|         } catch (IOException e) { | ||||
|             Timber.e(e, "IO Exception: "); | ||||
|  |  | |||
|  | @ -0,0 +1,7 @@ | |||
| package fr.free.nrw.commons.category; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| 
 | ||||
| public interface OnCategoriesSaveHandler { | ||||
|     void onCategoriesSave(ArrayList<String> categories); | ||||
| } | ||||
|  | @ -7,6 +7,7 @@ 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; | ||||
| 
 | ||||
|  | @ -14,18 +15,20 @@ 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. | ||||
|  */ | ||||
| public class PrefixUpdater extends AsyncTask<Void, Void, List<String>> { | ||||
| class PrefixUpdater extends AsyncTask<Void, Void, List<String>> { | ||||
| 
 | ||||
|     private final CategorizationFragment catFragment; | ||||
|     private String filter; | ||||
|     private CategorizationFragment catFragment; | ||||
| 
 | ||||
|     public PrefixUpdater(CategorizationFragment catFragment) { | ||||
|     PrefixUpdater(CategorizationFragment catFragment) { | ||||
|         this.catFragment = catFragment; | ||||
|     } | ||||
| 
 | ||||
|  | @ -90,8 +93,9 @@ public class PrefixUpdater extends AsyncTask<Void, Void, List<String>> { | |||
|         } | ||||
| 
 | ||||
|         //if user types in something that is in cache, return cached category | ||||
|         if (catFragment.categoriesCache.containsKey(filter)) { | ||||
|             ArrayList<String> cachedItems = new ArrayList<>(catFragment.categoriesCache.get(filter)); | ||||
|         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)); | ||||
|         } | ||||
|  | @ -101,7 +105,7 @@ public class PrefixUpdater extends AsyncTask<Void, Void, List<String>> { | |||
|         MediaWikiApi api = CommonsApplication.getInstance().getMWApi(); | ||||
|         List<String> categories = new ArrayList<>(); | ||||
|         try { | ||||
|             categories = api.allCategories(CategorizationFragment.SEARCH_CATS_LIMIT, this.filter); | ||||
|             categories = api.allCategories(SEARCH_CATS_LIMIT, this.filter); | ||||
|             Timber.d("Prefix URL filter %s", categories); | ||||
|         } catch (IOException e) { | ||||
|             Timber.e(e, "IO Exception: "); | ||||
|  |  | |||
|  | @ -19,17 +19,12 @@ class TitleCategories extends AsyncTask<Void, Void, List<String>> { | |||
| 
 | ||||
|     private final static int SEARCH_CATS_LIMIT = 25; | ||||
| 
 | ||||
|     private String title; | ||||
|     private final String title; | ||||
| 
 | ||||
|     TitleCategories(String title) { | ||||
|         this.title = title; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onPreExecute() { | ||||
|         super.onPreExecute(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected List<String> doInBackground(Void... voids) { | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ import fr.free.nrw.commons.Media; | |||
| import fr.free.nrw.commons.R; | ||||
| import fr.free.nrw.commons.auth.AuthenticatedActivity; | ||||
| import fr.free.nrw.commons.category.CategorizationFragment; | ||||
| import fr.free.nrw.commons.category.OnCategoriesSaveHandler; | ||||
| import fr.free.nrw.commons.contributions.Contribution; | ||||
| import fr.free.nrw.commons.media.MediaDetailPagerFragment; | ||||
| import fr.free.nrw.commons.modifications.CategoryModifier; | ||||
|  | @ -43,7 +44,7 @@ public  class       MultipleShareActivity | |||
|                     AdapterView.OnItemClickListener, | ||||
|                     FragmentManager.OnBackStackChangedListener, | ||||
|                     MultipleUploadListFragment.OnMultipleUploadInitiatedHandler, | ||||
|         CategorizationFragment.OnCategoriesSaveHandler { | ||||
|         OnCategoriesSaveHandler { | ||||
|     private CommonsApplication app; | ||||
|     private ArrayList<Contribution> photosList = null; | ||||
| 
 | ||||
|  |  | |||
|  | @ -36,6 +36,7 @@ import fr.free.nrw.commons.R; | |||
| import fr.free.nrw.commons.Utils; | ||||
| import fr.free.nrw.commons.auth.AuthenticatedActivity; | ||||
| import fr.free.nrw.commons.category.CategorizationFragment; | ||||
| import fr.free.nrw.commons.category.OnCategoriesSaveHandler; | ||||
| import fr.free.nrw.commons.contributions.Contribution; | ||||
| import fr.free.nrw.commons.modifications.CategoryModifier; | ||||
| import fr.free.nrw.commons.modifications.ModificationsContentProvider; | ||||
|  | @ -51,7 +52,7 @@ import timber.log.Timber; | |||
| public  class       ShareActivity | ||||
|         extends     AuthenticatedActivity | ||||
|         implements  SingleUploadFragment.OnUploadActionInitiated, | ||||
|         CategorizationFragment.OnCategoriesSaveHandler { | ||||
|         OnCategoriesSaveHandler { | ||||
| 
 | ||||
|     private static final int REQUEST_PERM_ON_CREATE_STORAGE = 1; | ||||
|     private static final int REQUEST_PERM_ON_CREATE_LOCATION = 2; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Paul Hawke
						Paul Hawke