Sort categories alphabetically by name.

This pulls all the categories out of the database at once for sorting,
rather than sorting in SQLite. This is to prevent having to store the
localized category names in the database (and hence having to update
them when the locale is changed).

Fixes #967.
This commit is contained in:
Peter Serwylo 2017-04-25 12:26:45 +10:00
parent e2c82d2943
commit 4cc6e5bc89
3 changed files with 53 additions and 13 deletions

View File

@ -1,17 +1,21 @@
package org.fdroid.fdroid.views.categories;
import android.app.Activity;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.v4.app.LoaderManager;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.data.Schema;
import java.util.Collections;
import java.util.List;
public class CategoryAdapter extends RecyclerView.Adapter<CategoryController> {
private Cursor cursor;
@NonNull
private List<String> unlocalizedCategoryNames = Collections.<String>emptyList();
private final Activity activity;
private final LoaderManager loaderManager;
@ -27,17 +31,16 @@ public class CategoryAdapter extends RecyclerView.Adapter<CategoryController> {
@Override
public void onBindViewHolder(CategoryController holder, int position) {
cursor.moveToPosition(position);
holder.bindModel(cursor.getString(cursor.getColumnIndex(Schema.CategoryTable.Cols.NAME)));
holder.bindModel(unlocalizedCategoryNames.get(position));
}
@Override
public int getItemCount() {
return cursor == null ? 0 : cursor.getCount();
return unlocalizedCategoryNames.size();
}
public void setCategoriesCursor(Cursor cursor) {
this.cursor = cursor;
public void setCategories(@NonNull List<String> unlocalizedCategoryNames) {
this.unlocalizedCategoryNames = unlocalizedCategoryNames;
notifyDataSetChanged();
}

View File

@ -75,11 +75,15 @@ public class CategoryController extends RecyclerView.ViewHolder implements Loade
.build();
}
public static String translateCategory(Context context, String categoryName) {
int categoryNameId = getCategoryResource(context, categoryName, "string", false);
return categoryNameId == 0 ? categoryName : context.getString(categoryNameId);
}
void bindModel(@NonNull String categoryName) {
currentCategory = categoryName;
int categoryNameId = getCategoryResource(activity, categoryName, "string", false);
String translatedName = categoryNameId == 0 ? categoryName : activity.getString(categoryNameId);
String translatedName = translateCategory(activity, categoryName);
heading.setText(translatedName);
heading.setContentDescription(activity.getString(R.string.tts_category_name, translatedName));

View File

@ -19,6 +19,12 @@ import org.fdroid.fdroid.data.CategoryProvider;
import org.fdroid.fdroid.data.Schema;
import org.fdroid.fdroid.views.apps.AppListActivity;
import org.fdroid.fdroid.views.categories.CategoryAdapter;
import org.fdroid.fdroid.views.categories.CategoryController;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Responsible for ensuring that the categories view is inflated and then populated correctly.
@ -75,13 +81,38 @@ class CategoriesViewBinder implements LoaderManager.LoaderCallbacks<Cursor> {
);
}
/**
* Reads all categories from the cursor and stores them in memory to provide to the {@link CategoryAdapter}.
*
* It does this so it is easier to deal with localized/unlocalized categories without having
* to store the localized version in the database. It is not expected that the list of categories
* will grow so large as to make this a performance concern. If it does in the future, the
* {@link CategoryAdapter} can be reverted to wrap the cursor again, and localized category
* names can be stored in the database (allowing sorting in their localized form).
*/
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if (loader.getId() != LOADER_ID) {
if (loader.getId() != LOADER_ID || cursor == null) {
return;
}
categoryAdapter.setCategoriesCursor(cursor);
List<String> categoryNames = new ArrayList<>(cursor.getCount());
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
categoryNames.add(cursor.getString(cursor.getColumnIndex(Schema.CategoryTable.Cols.NAME)));
cursor.moveToNext();
}
Collections.sort(categoryNames, new Comparator<String>() {
@Override
public int compare(String categoryOne, String categoryTwo) {
String localizedCategoryOne = CategoryController.translateCategory(activity, categoryOne);
String localizedCategoryTwo = CategoryController.translateCategory(activity, categoryTwo);
return localizedCategoryOne.compareTo(localizedCategoryTwo);
}
});
categoryAdapter.setCategories(categoryNames);
if (categoryAdapter.getItemCount() == 0) {
emptyState.setVisibility(View.VISIBLE);
@ -90,6 +121,8 @@ class CategoriesViewBinder implements LoaderManager.LoaderCallbacks<Cursor> {
emptyState.setVisibility(View.GONE);
categoriesList.setVisibility(View.VISIBLE);
}
cursor.close();
}
@Override
@ -98,7 +131,7 @@ class CategoriesViewBinder implements LoaderManager.LoaderCallbacks<Cursor> {
return;
}
categoryAdapter.setCategoriesCursor(null);
categoryAdapter.setCategories(Collections.<String>emptyList());
}
}