Merge branch 'fix/count-apps-in-repo'
This commit is contained in:
commit
141f133c7a
@ -3,9 +3,3 @@ These issues are a must-fix before the next stable release:
|
||||
* Right after updating a repo, `Recently Updated` shows the apps correctly but
|
||||
the new apks don't show up on App Details until the whole app is restarted
|
||||
(or until the repos are wiped and re-downloaded)
|
||||
|
||||
* `App.curVersion` is now used in some places where before we used
|
||||
`App.curApk.version`, which means that e.g. app lists now show the current
|
||||
version at upstream and not the latest stable version in the repository
|
||||
(highly misleading to users, who might end up looking for versions not in
|
||||
the repo yet)
|
||||
|
@ -128,7 +128,7 @@ public class AppDetails extends ListActivity {
|
||||
|
||||
holder.version.setText(getString(R.string.version)
|
||||
+ " " + apk.version
|
||||
+ (apk.vercode == app.curVercode ? " ☆" : ""));
|
||||
+ (apk.vercode == app.suggestedVercode ? " ☆" : ""));
|
||||
|
||||
if (apk.vercode == app.getInstalledVerCode(getContext())
|
||||
&& mInstalledSigID != null && apk.sig != null
|
||||
@ -534,7 +534,7 @@ public class AppDetails extends ListActivity {
|
||||
Apk curApk = null;
|
||||
for (int i = 0; i < adapter.getCount(); i ++) {
|
||||
Apk apk = adapter.getItem(i);
|
||||
if (apk.vercode == app.curVercode) {
|
||||
if (apk.vercode == app.suggestedVercode) {
|
||||
curApk = apk;
|
||||
break;
|
||||
}
|
||||
@ -678,7 +678,7 @@ public class AppDetails extends ListActivity {
|
||||
}
|
||||
|
||||
// Check count > 0 due to incompatible apps resulting in an empty list.
|
||||
if (app.getInstalledVersion(this) == null && app.curVercode > 0 &&
|
||||
if (app.getInstalledVersion(this) == null && app.suggestedVercode > 0 &&
|
||||
adapter.getCount() > 0) {
|
||||
MenuItemCompat.setShowAsAction(menu.add(
|
||||
Menu.NONE, INSTALL, 1, R.string.menu_install)
|
||||
@ -716,7 +716,7 @@ public class AppDetails extends ListActivity {
|
||||
menu.add(Menu.NONE, IGNORETHIS, 2, R.string.menu_ignore_this)
|
||||
.setIcon(android.R.drawable.ic_menu_close_clear_cancel)
|
||||
.setCheckable(true)
|
||||
.setChecked(app.ignoreThisUpdate >= app.curVercode);
|
||||
.setChecked(app.ignoreThisUpdate >= app.suggestedVercode);
|
||||
}
|
||||
if (app.webURL.length() > 0) {
|
||||
menu.add(Menu.NONE, WEBSITE, 3, R.string.menu_website).setIcon(
|
||||
@ -783,8 +783,8 @@ public class AppDetails extends ListActivity {
|
||||
|
||||
case INSTALL:
|
||||
// Note that this handles updating as well as installing.
|
||||
if (app.curVercode > 0) {
|
||||
final Apk apkToInstall = ApkProvider.Helper.find(this, app.id, app.curVercode);
|
||||
if (app.suggestedVercode > 0) {
|
||||
final Apk apkToInstall = ApkProvider.Helper.find(this, app.id, app.suggestedVercode);
|
||||
install(apkToInstall);
|
||||
}
|
||||
return true;
|
||||
@ -799,10 +799,10 @@ public class AppDetails extends ListActivity {
|
||||
return true;
|
||||
|
||||
case IGNORETHIS:
|
||||
if (app.ignoreThisUpdate >= app.curVercode)
|
||||
if (app.ignoreThisUpdate >= app.suggestedVercode)
|
||||
app.ignoreThisUpdate = 0;
|
||||
else
|
||||
app.ignoreThisUpdate = app.curVercode;
|
||||
app.ignoreThisUpdate = app.suggestedVercode;
|
||||
item.setChecked(app.ignoreThisUpdate > 0);
|
||||
return true;
|
||||
|
||||
|
@ -226,12 +226,12 @@ public class RepoXMLHandler extends DefaultHandler {
|
||||
curapp.lastUpdated = null;
|
||||
}
|
||||
} else if (curel.equals("marketversion")) {
|
||||
curapp.curVersion = str;
|
||||
curapp.upstreamVersion = str;
|
||||
} else if (curel.equals("marketvercode")) {
|
||||
try {
|
||||
curapp.curVercode = Integer.parseInt(str);
|
||||
curapp.upstreamVercode = Integer.parseInt(str);
|
||||
} catch (NumberFormatException ex) {
|
||||
curapp.curVercode = -1;
|
||||
curapp.upstreamVercode = -1;
|
||||
}
|
||||
} else if (curel.equals("categories")) {
|
||||
curapp.categories = Utils.CommaSeparatedList.make(str);
|
||||
|
@ -400,19 +400,19 @@ public class UpdateService extends IntentService implements ProgressListener {
|
||||
private static void calcCurrentApkForApp(App app, List<Apk> apksForApp) {
|
||||
Apk latestApk = null;
|
||||
// Try and return the real current version first. It will find the
|
||||
// closest version smaller than the curVercode, being the same
|
||||
// closest version smaller than the upstreamVercode, being the same
|
||||
// vercode if it exists.
|
||||
if (app.curVercode > 0) {
|
||||
if (app.upstreamVercode > 0) {
|
||||
int latestcode = -1;
|
||||
for (Apk apk : apksForApp) {
|
||||
if ((!app.compatible || apk.compatible)
|
||||
&& apk.vercode <= app.curVercode
|
||||
&& apk.vercode <= app.upstreamVercode
|
||||
&& apk.vercode > latestcode) {
|
||||
latestApk = apk;
|
||||
latestcode = apk.vercode;
|
||||
}
|
||||
}
|
||||
} else if (app.curVercode == -1) {
|
||||
} else if (app.upstreamVercode == -1) {
|
||||
// If the current version was not set we return the most recent apk.
|
||||
int latestCode = -1;
|
||||
for (Apk apk : apksForApp) {
|
||||
@ -425,8 +425,7 @@ public class UpdateService extends IntentService implements ProgressListener {
|
||||
}
|
||||
|
||||
if (latestApk != null) {
|
||||
app.curVercode = latestApk.vercode;
|
||||
app.curVersion = latestApk.version;
|
||||
app.suggestedVercode = latestApk.vercode;
|
||||
}
|
||||
}
|
||||
|
||||
@ -629,6 +628,7 @@ public class UpdateService extends IntentService implements ProgressListener {
|
||||
operations.add(updateExistingApk(apk));
|
||||
} else {
|
||||
operations.add(insertNewApk(apk));
|
||||
knownApks.add(apk); // In case another repo has the same version/id combo for this apk.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,6 +100,8 @@ public class ApkProvider extends FDroidProvider {
|
||||
|
||||
public interface DataColumns extends BaseColumns {
|
||||
|
||||
public static String _COUNT_DISTINCT_ID = "countDistinct";
|
||||
|
||||
public static String APK_ID = "id";
|
||||
public static String VERSION = "version";
|
||||
public static String REPO_ID = "repo";
|
||||
@ -221,15 +223,16 @@ public class ApkProvider extends FDroidProvider {
|
||||
return matcher;
|
||||
}
|
||||
|
||||
private static class QueryBuilder {
|
||||
|
||||
private StringBuilder fields = new StringBuilder();
|
||||
private StringBuilder tables = new StringBuilder(DBHelper.TABLE_APK + " AS apk");
|
||||
private String selection = null;
|
||||
private String orderBy = null;
|
||||
private static class Query extends QueryBuilder {
|
||||
|
||||
private boolean repoTableRequired = false;
|
||||
|
||||
@Override
|
||||
protected String getRequiredTables() {
|
||||
return DBHelper.TABLE_APK + " AS apk";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addField(String field) {
|
||||
if (REPO_FIELDS.containsKey(field)) {
|
||||
addRepoField(REPO_FIELDS.get(field), field);
|
||||
@ -237,68 +240,21 @@ public class ApkProvider extends FDroidProvider {
|
||||
appendField("rowid", "apk", "_id");
|
||||
} else if (field.equals(DataColumns._COUNT)) {
|
||||
appendField("COUNT(*) AS " + DataColumns._COUNT);
|
||||
} else if (field.equals(DataColumns._COUNT_DISTINCT_ID)) {
|
||||
appendField("COUNT(DISTINCT apk.id) AS " + DataColumns._COUNT_DISTINCT_ID);
|
||||
} else {
|
||||
appendField(field, "apk");
|
||||
}
|
||||
}
|
||||
|
||||
public void addRepoField(String field, String alias) {
|
||||
private void addRepoField(String field, String alias) {
|
||||
if (!repoTableRequired) {
|
||||
repoTableRequired = true;
|
||||
tables.append(" LEFT JOIN ");
|
||||
tables.append(DBHelper.TABLE_REPO);
|
||||
tables.append(" AS repo ON (apk.repo = repo._id) ");
|
||||
leftJoin(DBHelper.TABLE_REPO, "repo", "apk.repo = repo._id");
|
||||
}
|
||||
appendField(field, "repo", alias);
|
||||
}
|
||||
|
||||
private void appendField(String field) {
|
||||
appendField(field, null, null);
|
||||
}
|
||||
|
||||
private void appendField(String field, String tableAlias) {
|
||||
appendField(field, tableAlias, null);
|
||||
}
|
||||
|
||||
private void appendField(String field, String tableAlias,
|
||||
String fieldAlias) {
|
||||
if (fields.length() != 0) {
|
||||
fields.append(',');
|
||||
}
|
||||
|
||||
if (tableAlias != null) {
|
||||
fields.append(tableAlias).append('.');
|
||||
}
|
||||
|
||||
fields.append(field);
|
||||
|
||||
if (fieldAlias != null) {
|
||||
fields.append(" AS ").append(fieldAlias);
|
||||
}
|
||||
}
|
||||
|
||||
public void addSelection(String selection) {
|
||||
this.selection = selection;
|
||||
}
|
||||
|
||||
public void addOrderBy(String orderBy) {
|
||||
this.orderBy = orderBy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
StringBuilder suffix = new StringBuilder();
|
||||
if (selection != null) {
|
||||
suffix.append(" WHERE ").append(selection);
|
||||
}
|
||||
|
||||
if (orderBy != null) {
|
||||
suffix.append(" ORDER BY ").append(orderBy);
|
||||
}
|
||||
|
||||
return "SELECT " + fields + " FROM " + tables + suffix;
|
||||
}
|
||||
}
|
||||
|
||||
private QuerySelection queryApp(String appId) {
|
||||
@ -377,7 +333,7 @@ public class ApkProvider extends FDroidProvider {
|
||||
throw new UnsupportedOperationException("Invalid URI for apk content provider: " + uri);
|
||||
}
|
||||
|
||||
QueryBuilder queryBuilder = new QueryBuilder();
|
||||
Query queryBuilder = new Query();
|
||||
for (String field : projection) {
|
||||
queryBuilder.addField(field);
|
||||
}
|
||||
|
@ -41,8 +41,18 @@ public class App extends ValueObject implements Comparable<App> {
|
||||
|
||||
public String flattrID;
|
||||
|
||||
public String curVersion;
|
||||
public int curVercode;
|
||||
public String upstreamVersion;
|
||||
public int upstreamVercode;
|
||||
|
||||
/**
|
||||
* Unlike other public fields, this is only accessible via a getter, to
|
||||
* emphasise that setting it wont do anything. In order to change this,
|
||||
* you need to change suggestedVercode to an apk which is in the apk table.
|
||||
*/
|
||||
private String suggestedVersion;
|
||||
|
||||
public int suggestedVercode;
|
||||
|
||||
public Date added;
|
||||
public Date lastUpdated;
|
||||
|
||||
@ -114,10 +124,14 @@ public class App extends ValueObject implements Comparable<App> {
|
||||
dogecoinAddr = cursor.getString(i);
|
||||
} else if (column.equals(AppProvider.DataColumns.FLATTR_ID)) {
|
||||
flattrID = cursor.getString(i);
|
||||
} else if (column.equals(AppProvider.DataColumns.CURRENT_VERSION)) {
|
||||
curVersion = cursor.getString(i);
|
||||
} else if (column.equals(AppProvider.DataColumns.CURRENT_VERSION_CODE)) {
|
||||
curVercode = cursor.getInt(i);
|
||||
} else if (column.equals(AppProvider.DataColumns.SuggestedApk.VERSION)) {
|
||||
suggestedVersion = cursor.getString(i);
|
||||
} else if (column.equals(AppProvider.DataColumns.SUGGESTED_VERSION_CODE)) {
|
||||
suggestedVercode = cursor.getInt(i);
|
||||
} else if (column.equals(AppProvider.DataColumns.UPSTREAM_VERSION_CODE)) {
|
||||
upstreamVercode = cursor.getInt(i);
|
||||
} else if (column.equals(AppProvider.DataColumns.UPSTREAM_VERSION)) {
|
||||
upstreamVersion = cursor.getString(i);
|
||||
} else if (column.equals(AppProvider.DataColumns.ADDED)) {
|
||||
added = ValueObject.toDate(cursor.getString(i));
|
||||
} else if (column.equals(AppProvider.DataColumns.LAST_UPDATED)) {
|
||||
@ -158,8 +172,9 @@ public class App extends ValueObject implements Comparable<App> {
|
||||
values.put(AppProvider.DataColumns.FLATTR_ID, flattrID);
|
||||
values.put(AppProvider.DataColumns.ADDED, added == null ? "" : Utils.DATE_FORMAT.format(added));
|
||||
values.put(AppProvider.DataColumns.LAST_UPDATED, added == null ? "" : Utils.DATE_FORMAT.format(lastUpdated));
|
||||
values.put(AppProvider.DataColumns.CURRENT_VERSION, curVersion);
|
||||
values.put(AppProvider.DataColumns.CURRENT_VERSION_CODE, curVercode);
|
||||
values.put(AppProvider.DataColumns.SUGGESTED_VERSION_CODE, suggestedVercode);
|
||||
values.put(AppProvider.DataColumns.UPSTREAM_VERSION, upstreamVersion);
|
||||
values.put(AppProvider.DataColumns.UPSTREAM_VERSION_CODE, upstreamVercode);
|
||||
values.put(AppProvider.DataColumns.CATEGORIES, Utils.CommaSeparatedList.str(categories));
|
||||
values.put(AppProvider.DataColumns.ANTI_FEATURES, Utils.CommaSeparatedList.str(antiFeatures));
|
||||
values.put(AppProvider.DataColumns.REQUIREMENTS, Utils.CommaSeparatedList.str(requirements));
|
||||
@ -207,9 +222,9 @@ public class App extends ValueObject implements Comparable<App> {
|
||||
*/
|
||||
public boolean hasUpdates(Context context) {
|
||||
boolean updates = false;
|
||||
if (curVercode > 0) {
|
||||
if (suggestedVercode > 0) {
|
||||
int installedVerCode = getInstalledVerCode(context);
|
||||
updates = (installedVerCode > 0 && installedVerCode < curVercode);
|
||||
updates = (installedVerCode > 0 && installedVerCode < suggestedVercode);
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
@ -218,7 +233,7 @@ public class App extends ValueObject implements Comparable<App> {
|
||||
// to be notified about them
|
||||
public boolean canAndWantToUpdate(Context context) {
|
||||
boolean canUpdate = hasUpdates(context);
|
||||
boolean wantsUpdate = !ignoreAllUpdates && ignoreThisUpdate < curVercode;
|
||||
boolean wantsUpdate = !ignoreAllUpdates && ignoreThisUpdate < suggestedVercode;
|
||||
return canUpdate && wantsUpdate && !isFiltered();
|
||||
}
|
||||
|
||||
@ -227,4 +242,8 @@ public class App extends ValueObject implements Comparable<App> {
|
||||
public boolean isFiltered() {
|
||||
return new AppFilter().filter(this);
|
||||
}
|
||||
|
||||
public String getSuggestedVersion() {
|
||||
return suggestedVersion;
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public class AppProvider extends FDroidProvider {
|
||||
public static List<String> categories(Context context) {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
Uri uri = getContentUri();
|
||||
String[] projection = { "DISTINCT " + DataColumns.CATEGORIES };
|
||||
String[] projection = { DataColumns.CATEGORIES };
|
||||
Cursor cursor = resolver.query(uri, projection, null, null, null );
|
||||
Set<String> categorySet = new HashSet<String>();
|
||||
if (cursor != null) {
|
||||
@ -110,7 +110,7 @@ public class AppProvider extends FDroidProvider {
|
||||
|
||||
public interface DataColumns {
|
||||
|
||||
public static final String _ID = "rowid as _id";
|
||||
public static final String _ID = "rowid as _id"; // Required for CursorLoaders
|
||||
public static final String _COUNT = "_count";
|
||||
public static final String IS_COMPATIBLE = "compatible";
|
||||
public static final String APP_ID = "id";
|
||||
@ -127,8 +127,9 @@ public class AppProvider extends FDroidProvider {
|
||||
public static final String LITECOIN_ADDR = "litecoinAddr";
|
||||
public static final String DOGECOIN_ADDR = "dogecoinAddr";
|
||||
public static final String FLATTR_ID = "flattrID";
|
||||
public static final String CURRENT_VERSION = "curVersion";
|
||||
public static final String CURRENT_VERSION_CODE = "curVercode";
|
||||
public static final String SUGGESTED_VERSION_CODE = "suggestedVercode";
|
||||
public static final String UPSTREAM_VERSION = "upstreamVersion";
|
||||
public static final String UPSTREAM_VERSION_CODE = "upstreamVercode";
|
||||
public static final String CURRENT_APK = null;
|
||||
public static final String ADDED = "added";
|
||||
public static final String LAST_UPDATED = "lastUpdated";
|
||||
@ -147,16 +148,73 @@ public class AppProvider extends FDroidProvider {
|
||||
public static final String UPDATED = null;
|
||||
public static final String APKS = null;
|
||||
|
||||
public interface SuggestedApk {
|
||||
public static final String VERSION = "suggestedApkVersion";
|
||||
}
|
||||
|
||||
public static String[] ALL = {
|
||||
IS_COMPATIBLE, APP_ID, NAME, SUMMARY, ICON, DESCRIPTION,
|
||||
LICENSE, WEB_URL, TRACKER_URL, SOURCE_URL, DONATE_URL,
|
||||
BITCOIN_ADDR, LITECOIN_ADDR, DOGECOIN_ADDR, FLATTR_ID,
|
||||
CURRENT_VERSION, CURRENT_VERSION_CODE, ADDED, LAST_UPDATED,
|
||||
UPSTREAM_VERSION, UPSTREAM_VERSION_CODE, ADDED, LAST_UPDATED,
|
||||
CATEGORIES, ANTI_FEATURES, REQUIREMENTS, IGNORE_ALLUPDATES,
|
||||
IGNORE_THISUPDATE, ICON_URL
|
||||
IGNORE_THISUPDATE, ICON_URL, SUGGESTED_VERSION_CODE,
|
||||
SuggestedApk.VERSION
|
||||
};
|
||||
}
|
||||
|
||||
private static class Query extends QueryBuilder {
|
||||
|
||||
private boolean isSuggestedApkTableAdded = false;
|
||||
|
||||
private boolean categoryFieldAdded = false;
|
||||
|
||||
@Override
|
||||
protected String getRequiredTables() {
|
||||
return DBHelper.TABLE_APP;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDistinct() {
|
||||
return fieldCount() == 1 && categoryFieldAdded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addField(String field) {
|
||||
if (field.equals(DataColumns.SuggestedApk.VERSION)) {
|
||||
addSuggestedApkVersionField();
|
||||
} else if (field.equals(DataColumns._COUNT)) {
|
||||
appendCountField();
|
||||
} else {
|
||||
if (field.equals(DataColumns.CATEGORIES)) {
|
||||
categoryFieldAdded = true;
|
||||
}
|
||||
appendField(field, "fdroid_app");
|
||||
}
|
||||
}
|
||||
|
||||
private void appendCountField() {
|
||||
appendField("COUNT(*) AS " + DataColumns._COUNT);
|
||||
}
|
||||
|
||||
private void addSuggestedApkVersionField() {
|
||||
addSuggestedApkField(
|
||||
ApkProvider.DataColumns.VERSION,
|
||||
DataColumns.SuggestedApk.VERSION);
|
||||
}
|
||||
|
||||
private void addSuggestedApkField(String fieldName, String alias) {
|
||||
if (!isSuggestedApkTableAdded) {
|
||||
isSuggestedApkTableAdded = true;
|
||||
leftJoin(
|
||||
DBHelper.TABLE_APK,
|
||||
"suggestedApk",
|
||||
"fdroid_app.suggestedVercode = suggestedApk.vercode AND fdroid_app.id = suggestedApk.id");
|
||||
}
|
||||
appendField(fieldName, "suggestedApk", alias);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String PROVIDER_NAME = "AppProvider";
|
||||
|
||||
private static final UriMatcher matcher = new UriMatcher(-1);
|
||||
@ -274,18 +332,18 @@ public class AppProvider extends FDroidProvider {
|
||||
private QuerySelection queryCanUpdate() {
|
||||
Map<String, PackageInfo> installedApps = Utils.getInstalledApps(getContext());
|
||||
|
||||
String ignoreCurrent = " ignoreThisUpdate != curVercode ";
|
||||
String ignoreAll = " ignoreAllUpdates != 1 ";
|
||||
String ignoreCurrent = " fdroid_app.ignoreThisUpdate != fdroid_app.suggestedVercode ";
|
||||
String ignoreAll = " fdroid_app.ignoreAllUpdates != 1 ";
|
||||
String ignore = " ( " + ignoreCurrent + " AND " + ignoreAll + " ) ";
|
||||
|
||||
StringBuilder where = new StringBuilder( ignore + " AND ( 0 ");
|
||||
String[] selectionArgs = new String[installedApps.size() * 2];
|
||||
int i = 0;
|
||||
for (PackageInfo info : installedApps.values() ) {
|
||||
where.append(" OR ( ")
|
||||
.append(AppProvider.DataColumns.APP_ID)
|
||||
.append(" = ? AND ")
|
||||
.append(DataColumns.CURRENT_VERSION_CODE)
|
||||
where.append(" OR ( fdroid_app.")
|
||||
.append(DataColumns.APP_ID)
|
||||
.append(" = ? AND fdroid_app.")
|
||||
.append(DataColumns.SUGGESTED_VERSION_CODE)
|
||||
.append(" > ?) ");
|
||||
selectionArgs[ i * 2 ] = info.packageName;
|
||||
selectionArgs[ i * 2 + 1 ] = Integer.toString(info.versionCode);
|
||||
@ -302,7 +360,7 @@ public class AppProvider extends FDroidProvider {
|
||||
String[] selectionArgs = new String[installedApps.size()];
|
||||
int i = 0;
|
||||
for (Map.Entry<String, PackageInfo> entry : installedApps.entrySet() ) {
|
||||
where.append(" OR ")
|
||||
where.append(" OR fdroid_app.")
|
||||
.append(AppProvider.DataColumns.APP_ID)
|
||||
.append(" = ? ");
|
||||
selectionArgs[i] = entry.getKey();
|
||||
@ -316,27 +374,29 @@ public class AppProvider extends FDroidProvider {
|
||||
private QuerySelection querySearch(String keywords) {
|
||||
keywords = "%" + keywords + "%";
|
||||
String selection =
|
||||
"id like ? OR " +
|
||||
"name like ? OR " +
|
||||
"summary like ? OR " +
|
||||
"description like ? ";
|
||||
"fdroid_app.id like ? OR " +
|
||||
"fdroid_app.name like ? OR " +
|
||||
"fdroid_app.summary like ? OR " +
|
||||
"fdroid_app.description like ? ";
|
||||
String[] args = new String[] { keywords, keywords, keywords, keywords};
|
||||
return new QuerySelection(selection, args);
|
||||
}
|
||||
|
||||
private QuerySelection querySingle(String id) {
|
||||
String selection = "fdroid_app.id = ?";
|
||||
String[] args = { id };
|
||||
return new QuerySelection(selection, args);
|
||||
}
|
||||
|
||||
private QuerySelection queryNewlyAdded() {
|
||||
String selection = "added > ?";
|
||||
String[] args = new String[] {
|
||||
Utils.DATE_FORMAT.format(Preferences.get().calcMaxHistory())
|
||||
};
|
||||
String selection = "fdroid_app.added > ?";
|
||||
String[] args = { Utils.DATE_FORMAT.format(Preferences.get().calcMaxHistory()) };
|
||||
return new QuerySelection(selection, args);
|
||||
}
|
||||
|
||||
private QuerySelection queryRecentlyUpdated() {
|
||||
String selection = "added != lastUpdated AND lastUpdated > ?";
|
||||
String[] args = new String[] {
|
||||
Utils.DATE_FORMAT.format(Preferences.get().calcMaxHistory())
|
||||
};
|
||||
String selection = "fdroid_app.added != fdroid_app.lastUpdated AND fdroid_app.lastUpdated > ?";
|
||||
String[] args = { Utils.DATE_FORMAT.format(Preferences.get().calcMaxHistory()) };
|
||||
return new QuerySelection(selection, args);
|
||||
}
|
||||
|
||||
@ -344,11 +404,11 @@ public class AppProvider extends FDroidProvider {
|
||||
// TODO: In the future, add a new table for categories,
|
||||
// so we can join onto it.
|
||||
String selection =
|
||||
" categories = ? OR " + // Only category e.g. "internet"
|
||||
" categories LIKE ? OR " + // First category e.g. "internet,%"
|
||||
" categories LIKE ? OR " + // Last category e.g. "%,internet"
|
||||
" categories LIKE ? "; // One of many categories e.g. "%,internet,%"
|
||||
String[] args = new String[] {
|
||||
" fdroid_app.categories = ? OR " + // Only category e.g. "internet"
|
||||
" fdroid_app.categories LIKE ? OR " + // First category e.g. "internet,%"
|
||||
" fdroid_app.categories LIKE ? OR " + // Last category e.g. "%,internet"
|
||||
" fdroid_app.categories LIKE ? "; // One of many categories e.g. "%,internet,%"
|
||||
String[] args = {
|
||||
category,
|
||||
category + ",%",
|
||||
"%," + category,
|
||||
@ -364,7 +424,7 @@ public class AppProvider extends FDroidProvider {
|
||||
|
||||
private QuerySelection queryApps(String appIds) {
|
||||
String[] args = appIds.split(",");
|
||||
String selection = "id IN (" + generateQuestionMarksForInClause(args.length) + ")";
|
||||
String selection = "fdroid_app.id IN (" + generateQuestionMarksForInClause(args.length) + ")";
|
||||
return new QuerySelection(selection, args);
|
||||
}
|
||||
|
||||
@ -376,9 +436,7 @@ public class AppProvider extends FDroidProvider {
|
||||
break;
|
||||
|
||||
case CODE_SINGLE:
|
||||
query = query.add(
|
||||
DataColumns.APP_ID + " = ?",
|
||||
new String[] { uri.getLastPathSegment() } );
|
||||
query = query.add(querySingle(uri.getLastPathSegment()));
|
||||
break;
|
||||
|
||||
case CAN_UPDATE:
|
||||
@ -406,12 +464,12 @@ public class AppProvider extends FDroidProvider {
|
||||
break;
|
||||
|
||||
case RECENTLY_UPDATED:
|
||||
sortOrder = DataColumns.LAST_UPDATED + " DESC";
|
||||
sortOrder = " fdroid_app.lastUpdated DESC";
|
||||
query = query.add(queryRecentlyUpdated());
|
||||
break;
|
||||
|
||||
case NEWLY_ADDED:
|
||||
sortOrder = DataColumns.ADDED + " DESC";
|
||||
sortOrder = " fdroid_app.added DESC";
|
||||
query = query.add(queryNewlyAdded());
|
||||
break;
|
||||
|
||||
@ -421,18 +479,15 @@ public class AppProvider extends FDroidProvider {
|
||||
}
|
||||
|
||||
if (AppProvider.DataColumns.NAME.equals(sortOrder)) {
|
||||
sortOrder = " lower( " + sortOrder + " ) ";
|
||||
sortOrder = " lower( fdroid_app." + sortOrder + " ) ";
|
||||
}
|
||||
|
||||
for (String field : projection) {
|
||||
if (field.equals(DataColumns._COUNT)) {
|
||||
projection = new String[] { "COUNT(*) AS " + DataColumns._COUNT };
|
||||
break;
|
||||
}
|
||||
}
|
||||
Query q = new Query();
|
||||
q.addFields(projection);
|
||||
q.addSelection(query.getSelection());
|
||||
q.addOrderBy(sortOrder);
|
||||
|
||||
Cursor cursor = read().query(getTableName(), projection, query.getSelection(),
|
||||
query.getArgs(), null, null, sortOrder);
|
||||
Cursor cursor = read().rawQuery(q.toString(), query.getArgs());
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
return cursor;
|
||||
}
|
||||
@ -472,7 +527,7 @@ public class AppProvider extends FDroidProvider {
|
||||
switch (matcher.match(uri)) {
|
||||
|
||||
case CODE_SINGLE:
|
||||
query = query.add(new QuerySelection("id = ?", new String[] { uri.getLastPathSegment()}));
|
||||
query = query.add(querySingle(uri.getLastPathSegment()));
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -67,8 +67,9 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
+ "webURL text, "
|
||||
+ "trackerURL text, "
|
||||
+ "sourceURL text, "
|
||||
+ "curVersion text,"
|
||||
+ "curVercode integer,"
|
||||
+ "suggestedVercode text,"
|
||||
+ "upstreamVersion text,"
|
||||
+ "upstreamVercode integer,"
|
||||
+ "antiFeatures string,"
|
||||
+ "donateURL string,"
|
||||
+ "bitcoinAddr string,"
|
||||
@ -85,7 +86,7 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
+ "iconUrl text, "
|
||||
+ "primary key(id));";
|
||||
|
||||
private static final int DB_VERSION = 40;
|
||||
private static final int DB_VERSION = 41;
|
||||
|
||||
private Context context;
|
||||
|
||||
|
105
src/org/fdroid/fdroid/data/QueryBuilder.java
Normal file
105
src/org/fdroid/fdroid/data/QueryBuilder.java
Normal file
@ -0,0 +1,105 @@
|
||||
package org.fdroid.fdroid.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
abstract class QueryBuilder {
|
||||
|
||||
private List<String> fields = new ArrayList<String>();
|
||||
private StringBuilder tables = new StringBuilder(getRequiredTables());
|
||||
private String selection = null;
|
||||
private String orderBy = null;
|
||||
|
||||
protected abstract String getRequiredTables();
|
||||
|
||||
public abstract void addField(String field);
|
||||
|
||||
protected int fieldCount() {
|
||||
return fields.size();
|
||||
}
|
||||
|
||||
public void addFields(String[] fields) {
|
||||
for (String field : fields) {
|
||||
addField(field);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isDistinct() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void appendField(String field) {
|
||||
appendField(field, null, null);
|
||||
}
|
||||
|
||||
protected void appendField(String field, String tableAlias) {
|
||||
appendField(field, tableAlias, null);
|
||||
}
|
||||
|
||||
protected final void appendField(String field, String tableAlias,
|
||||
String fieldAlias) {
|
||||
|
||||
StringBuilder fieldBuilder = new StringBuilder();
|
||||
|
||||
if (tableAlias != null) {
|
||||
fieldBuilder.append(tableAlias).append('.');
|
||||
}
|
||||
|
||||
fieldBuilder.append(field);
|
||||
|
||||
if (fieldAlias != null) {
|
||||
fieldBuilder.append(" AS ").append(fieldAlias);
|
||||
}
|
||||
|
||||
fields.add(fieldBuilder.toString());
|
||||
}
|
||||
|
||||
public void addSelection(String selection) {
|
||||
this.selection = selection;
|
||||
}
|
||||
|
||||
public void addOrderBy(String orderBy) {
|
||||
this.orderBy = orderBy;
|
||||
}
|
||||
|
||||
protected final void leftJoin(String table, String alias,
|
||||
String condition) {
|
||||
tables.append(" LEFT JOIN ");
|
||||
tables.append(table);
|
||||
if (alias != null) {
|
||||
tables.append(" AS ");
|
||||
tables.append(alias);
|
||||
}
|
||||
tables.append(" ON (");
|
||||
tables.append(condition);
|
||||
tables.append(")");
|
||||
}
|
||||
|
||||
private String fieldsSql() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < fields.size(); i ++) {
|
||||
if (i > 0) {
|
||||
sb.append(',');
|
||||
}
|
||||
sb.append(fields.get(i));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String whereSql() {
|
||||
return selection != null ? " WHERE " + selection : "";
|
||||
}
|
||||
|
||||
private String orderBySql() {
|
||||
return orderBy != null ? " ORDER BY " + orderBy : "";
|
||||
}
|
||||
|
||||
private String tablesSql() {
|
||||
return tables.toString();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
String distinct = isDistinct() ? " DISTINCT " : "";
|
||||
return "SELECT " + distinct + fieldsSql() + " FROM " + tablesSql() + whereSql() + orderBySql();
|
||||
}
|
||||
}
|
@ -174,11 +174,9 @@ public class RepoProvider extends FDroidProvider {
|
||||
|
||||
public static int countAppsForRepo(Context context, long repoId) {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
String[] projection = { "COUNT(distinct id)" };
|
||||
String selection = "repo = ?";
|
||||
String[] args = { Long.toString(repoId) };
|
||||
Uri apkUri = ApkProvider.getContentUri();
|
||||
Cursor result = resolver.query(apkUri, projection, selection, args, null);
|
||||
String[] projection = { ApkProvider.DataColumns._COUNT_DISTINCT_ID };
|
||||
Uri apkUri = ApkProvider.getRepoUri(repoId);
|
||||
Cursor result = resolver.query(apkUri, projection, null, null, null);
|
||||
if (result != null && result.getCount() > 0) {
|
||||
result.moveToFirst();
|
||||
return result.getInt(0);
|
||||
@ -189,6 +187,7 @@ public class RepoProvider extends FDroidProvider {
|
||||
}
|
||||
|
||||
public interface DataColumns extends BaseColumns {
|
||||
|
||||
public static String ADDRESS = "address";
|
||||
public static String NAME = "name";
|
||||
public static String DESCRIPTION = "description";
|
||||
|
@ -130,14 +130,14 @@ abstract public class AppListAdapter extends CursorAdapter {
|
||||
|
||||
private String getVersionInfo(App app) {
|
||||
|
||||
if (app.curVercode <= 0) {
|
||||
if (app.suggestedVercode <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PackageInfo installedInfo = app.getInstalledInfo(mContext);
|
||||
|
||||
if (installedInfo == null) {
|
||||
return ellipsize(app.curVersion, 12);
|
||||
return ellipsize(app.getSuggestedVersion(), 12);
|
||||
}
|
||||
|
||||
String installedVersionString = installedInfo.versionName;
|
||||
@ -145,7 +145,7 @@ abstract public class AppListAdapter extends CursorAdapter {
|
||||
|
||||
if (app.canAndWantToUpdate(mContext) && showStatusUpdate()) {
|
||||
return ellipsize(installedVersionString, 8) +
|
||||
" → " + ellipsize(app.curVersion, 8);
|
||||
" → " + ellipsize(app.getSuggestedVersion(), 8);
|
||||
}
|
||||
|
||||
if (installedVersionCode > 0 && showStatusInstalled()) {
|
||||
|
@ -27,7 +27,7 @@ abstract public class AppListFragment extends ListFragment implements
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
|
||||
public static final String[] APP_PROJECTION = {
|
||||
AppProvider.DataColumns._ID,
|
||||
AppProvider.DataColumns._ID, // Required for cursor loader to work.
|
||||
AppProvider.DataColumns.APP_ID,
|
||||
AppProvider.DataColumns.NAME,
|
||||
AppProvider.DataColumns.SUMMARY,
|
||||
@ -35,8 +35,8 @@ abstract public class AppListFragment extends ListFragment implements
|
||||
AppProvider.DataColumns.LICENSE,
|
||||
AppProvider.DataColumns.ICON,
|
||||
AppProvider.DataColumns.ICON_URL,
|
||||
AppProvider.DataColumns.CURRENT_VERSION,
|
||||
AppProvider.DataColumns.CURRENT_VERSION_CODE,
|
||||
AppProvider.DataColumns.SuggestedApk.VERSION,
|
||||
AppProvider.DataColumns.SUGGESTED_VERSION_CODE,
|
||||
AppProvider.DataColumns.REQUIREMENTS, // Needed for filtering apps that require root.
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user