diff --git a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListActivity.java b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListActivity.java index 51fd9545d..9007c1606 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListActivity.java @@ -74,7 +74,7 @@ public class AppListActivity extends AppCompatActivity implements LoaderManager. private Utils.KeyboardStateMonitor keyboardStateMonitor; private interface SortClause { - String NAME = Cols.NAME; + String WORDS = Cols.NAME; String LAST_UPDATED = Cols.LAST_UPDATED; } @@ -115,14 +115,14 @@ public class AppListActivity extends AppCompatActivity implements LoaderManager. @Override public void onClick(View view) { switch (sortClauseSelected) { - case SortClause.NAME: + case SortClause.WORDS: sortClauseSelected = SortClause.LAST_UPDATED; sortImage.setImageDrawable(lastUpdated); break; case SortClause.LAST_UPDATED: - sortClauseSelected = SortClause.NAME; + sortClauseSelected = SortClause.WORDS; final Drawable alphabetical = DrawableCompat.wrap( - ContextCompat.getDrawable(AppListActivity.this, R.drawable.ic_sort_by_alpha)) + ContextCompat.getDrawable(AppListActivity.this, R.drawable.ic_sort)) .mutate(); DrawableCompat.setTint(alphabetical, FDroidApp.isAppThemeLight() ? Color.BLACK : Color.WHITE); sortImage.setImageDrawable(alphabetical); @@ -253,25 +253,36 @@ public class AppListActivity extends AppCompatActivity implements LoaderManager. } private String getSortOrder() { - final String nameCol = AppMetadataTable.NAME + "." + AppMetadataTable.Cols.NAME; - final String summaryCol = AppMetadataTable.NAME + "." + AppMetadataTable.Cols.SUMMARY; - final String nameSort = AppMetadataTable.NAME + "." + Cols.NAME + " COLLATE LOCALIZED "; - final String lastUpdatedSort = AppMetadataTable.NAME + "." + Cols.LAST_UPDATED + " DESC"; - String sortOrder; - switch (sortClauseSelected) { - case SortClause.NAME: - sortOrder = nameSort; - break; - case SortClause.LAST_UPDATED: - sortOrder = lastUpdatedSort; - break; - default: - sortOrder = nameSort; + final String table = AppMetadataTable.NAME; + final String nameCol = table + "." + AppMetadataTable.Cols.NAME; + final String summaryCol = table + "." + AppMetadataTable.Cols.SUMMARY; + final String packageCol = Cols.Package.PACKAGE_NAME; + + if (sortClauseSelected.equals(SortClause.LAST_UPDATED)) { + return table + "." + Cols.LAST_UPDATED + " DESC" + + ", " + table + "." + Cols.IS_LOCALIZED + " DESC" + + ", " + table + "." + Cols.ADDED + " ASC" + + ", " + table + "." + Cols.NAME + " IS NULL ASC" + + ", " + table + "." + Cols.ICON + " IS NULL ASC" + + ", " + table + "." + Cols.SUMMARY + " IS NULL ASC" + + ", " + table + "." + Cols.DESCRIPTION + " IS NULL ASC" + + ", " + table + "." + Cols.WHATSNEW + " IS NULL ASC" + + ", CASE WHEN " + table + "." + Cols.PHONE_SCREENSHOTS + " IS NULL" + + " AND " + table + "." + Cols.SEVEN_INCH_SCREENSHOTS + " IS NULL" + + " AND " + table + "." + Cols.TEN_INCH_SCREENSHOTS + " IS NULL" + + " AND " + table + "." + Cols.TV_SCREENSHOTS + " IS NULL" + + " AND " + table + "." + Cols.WEAR_SCREENSHOTS + " IS NULL" + + " AND " + table + "." + Cols.FEATURE_GRAPHIC + " IS NULL" + + " AND " + table + "." + Cols.PROMO_GRAPHIC + " IS NULL" + + " AND " + table + "." + Cols.TV_BANNER + " IS NULL" + + " THEN 1 ELSE 0 END"; } - final String[] terms = searchTerms.trim().split("\\s+"); + // prevent SQL injection https://en.wikipedia.org/wiki/SQL_injection#Escaping + final String[] terms = searchTerms.trim().replaceAll("[\\x1a\0\n\r\"';\\\\]+", " ").split("\\s+"); if (terms.length == 0 || terms[0].equals("")) { - return sortOrder; + return table + "." + Cols.NAME + " COLLATE LOCALIZED "; + } } StringBuilder titleCase = new StringBuilder(String.format("%s like '%%%s%%'", nameCol, terms[0])); StringBuilder summaryCase = new StringBuilder(String.format("%s like '%%%s%%'", summaryCol, terms[0])); @@ -281,6 +292,24 @@ public class AppListActivity extends AppCompatActivity implements LoaderManager. } return String.format("case when %s then 1 when %s then 2 else 3 end, %s", - titleCase.toString(), summaryCase.toString(), sortOrder); + titleCase.toString(), summaryCase.toString(), "" + + ", " + table + "." + Cols.IS_LOCALIZED + " DESC" + + ", " + table + "." + Cols.ADDED + " ASC" + + ", " + table + "." + Cols.NAME + " IS NULL ASC" + + ", " + table + "." + Cols.ICON + " IS NULL ASC" + + ", " + table + "." + Cols.SUMMARY + " IS NULL ASC" + + ", " + table + "." + Cols.DESCRIPTION + " IS NULL ASC" + + ", " + table + "." + Cols.WHATSNEW + " IS NULL ASC" + + ", CASE WHEN " + table + "." + Cols.PHONE_SCREENSHOTS + " IS NULL" + + " AND " + table + "." + Cols.SEVEN_INCH_SCREENSHOTS + " IS NULL" + + " AND " + table + "." + Cols.TEN_INCH_SCREENSHOTS + " IS NULL" + + " AND " + table + "." + Cols.TV_SCREENSHOTS + " IS NULL" + + " AND " + table + "." + Cols.WEAR_SCREENSHOTS + " IS NULL" + + " AND " + table + "." + Cols.FEATURE_GRAPHIC + " IS NULL" + + " AND " + table + "." + Cols.PROMO_GRAPHIC + " IS NULL" + + " AND " + table + "." + Cols.TV_BANNER + " IS NULL" + + " THEN 1 ELSE 0 END" + + ", " + table + "." + Cols.LAST_UPDATED + " DESC" + ); } } diff --git a/app/src/main/res/drawable/ic_sort.xml b/app/src/main/res/drawable/ic_sort.xml new file mode 100644 index 000000000..69d39e915 --- /dev/null +++ b/app/src/main/res/drawable/ic_sort.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_sort_by_alpha.xml b/app/src/main/res/drawable/ic_sort_by_alpha.xml deleted file mode 100644 index cecad4568..000000000 --- a/app/src/main/res/drawable/ic_sort_by_alpha.xml +++ /dev/null @@ -1,10 +0,0 @@ - - -