From 24f5f0a5d80d0f459cbc554c492f28ad765cbb9b Mon Sep 17 00:00:00 2001 From: Ciaran Gultnieks Date: Tue, 14 Dec 2010 22:54:07 +0000 Subject: [PATCH] Fixed an inconsistency in the updates listed after a repo update --- src/org/fdroid/fdroid/DB.java | 1012 +++++++++++---------- src/org/fdroid/fdroid/RepoXMLHandler.java | 5 +- src/org/fdroid/fdroid/UpdateService.java | 206 +++-- 3 files changed, 617 insertions(+), 606 deletions(-) diff --git a/src/org/fdroid/fdroid/DB.java b/src/org/fdroid/fdroid/DB.java index c3cf9b00e..914d62832 100644 --- a/src/org/fdroid/fdroid/DB.java +++ b/src/org/fdroid/fdroid/DB.java @@ -35,571 +35,575 @@ import android.util.Log; public class DB { - private static final String DATABASE_NAME = "fdroid"; + private static final String DATABASE_NAME = "fdroid"; - private SQLiteDatabase db; + private SQLiteDatabase db; - // The TABLE_APP table stores details of all the applications we know about. - // This information is retrieved from the repositories. - private static final String TABLE_APP = "fdroid_app"; - private static final String CREATE_TABLE_APP = "create table " + TABLE_APP - + " ( " + "id text not null, " + "name text not null, " - + "summary text not null, " + "icon text, " - + "description text not null, " + "license text not null, " - + "webURL text, " + "trackerURL text, " + "sourceURL text, " - + "installedVersion text," + "hasUpdates int not null," - + "primary key(id));"; + // The TABLE_APP table stores details of all the applications we know about. + // This information is retrieved from the repositories. + private static final String TABLE_APP = "fdroid_app"; + private static final String CREATE_TABLE_APP = "create table " + TABLE_APP + + " ( " + "id text not null, " + "name text not null, " + + "summary text not null, " + "icon text, " + + "description text not null, " + "license text not null, " + + "webURL text, " + "trackerURL text, " + "sourceURL text, " + + "installedVersion text," + "hasUpdates int not null," + + "primary key(id));"; - public static class App { + public static class App { - public App() { - name = "Unknown"; - summary = "Unknown application"; - icon = "noicon.png"; - id = "unknown"; - license = "Unknown"; - trackerURL = ""; - sourceURL = ""; - webURL = ""; - hasUpdates = false; - updated = false; - apks = new Vector(); - } + public App() { + name = "Unknown"; + summary = "Unknown application"; + icon = "noicon.png"; + id = "unknown"; + license = "Unknown"; + trackerURL = ""; + sourceURL = ""; + webURL = ""; + hasUpdates = false; + updated = false; + apks = new Vector(); + } - public String id; - public String name; - public String summary; - public String icon; - public String description; - public String license; - public String webURL; - public String trackerURL; - public String sourceURL; - public String installedVersion; - public int installedVerCode; - public String marketVersion; - public int marketVercode; + public String id; + public String name; + public String summary; + public String icon; + public String description; + public String license; + public String webURL; + public String trackerURL; + public String sourceURL; + public String installedVersion; + public int installedVerCode; + public String marketVersion; + public int marketVercode; - // True if there are new versions (apks) that the user hasn't - // explicitly ignored. (We're currently not using the database - // field for this - we make the decision on the fly in getApps(). - public boolean hasUpdates; + // True if there are new versions (apks) that the user hasn't + // explicitly ignored. (We're currently not using the database + // field for this - we make the decision on the fly in getApps(). + public boolean hasUpdates; - // Used internally for tracking during repo updates. - public boolean updated; + // Used internally for tracking during repo updates. + public boolean updated; - public Vector apks; + public Vector apks; - // Get the current version - this will be one of the Apks from 'apks'. - // Can return null if there are no available versions. - // This should be the 'current' version, as in the most recent stable - // one, that most users would want by default. It might not be the - // most recent, if for example there are betas etc. - public Apk getCurrentVersion() { + // Get the current version - this will be one of the Apks from 'apks'. + // Can return null if there are no available versions. + // This should be the 'current' version, as in the most recent stable + // one, that most users would want by default. It might not be the + // most recent, if for example there are betas etc. + public Apk getCurrentVersion() { - // Try and return the version that's in Google's market first... - if (marketVersion != null && marketVercode > 0) { - for (Apk apk : apks) { - if (apk.vercode == marketVercode) - return apk; - } - } + // Try and return the version that's in Google's market first... + if (marketVersion != null && marketVercode > 0) { + for (Apk apk : apks) { + if (apk.vercode == marketVercode) + return apk; + } + } - // If we don't know the market version, or we don't have it, we - // return the most recent version we have... - int latestcode = -1; - Apk latestapk = null; - for (Apk apk : apks) { - if (apk.vercode > latestcode) { - latestapk = apk; - latestcode = apk.vercode; - } - } - return latestapk; - } + // If we don't know the market version, or we don't have it, we + // return the most recent version we have... + int latestcode = -1; + Apk latestapk = null; + for (Apk apk : apks) { + if (apk.vercode > latestcode) { + latestapk = apk; + latestcode = apk.vercode; + } + } + return latestapk; + } - } + } - // The TABLE_APK table stores details of all the application versions we - // know about. Each relates directly back to an entry in TABLE_APP. - // This information is retrieved from the repositories. - private static final String TABLE_APK = "fdroid_apk"; - private static final String CREATE_TABLE_APK = "create table " + TABLE_APK - + " ( " + "id text not null, " + "version text not null, " - + "server text not null, " + "hash text not null, " - + "vercode int not null," + "apkName text not null, " - + "size int not null," + "primary key(id,version));"; + // The TABLE_APK table stores details of all the application versions we + // know about. Each relates directly back to an entry in TABLE_APP. + // This information is retrieved from the repositories. + private static final String TABLE_APK = "fdroid_apk"; + private static final String CREATE_TABLE_APK = "create table " + TABLE_APK + + " ( " + "id text not null, " + "version text not null, " + + "server text not null, " + "hash text not null, " + + "vercode int not null," + "apkName text not null, " + + "size int not null," + "primary key(id,version));"; - public static class Apk { + public static class Apk { - public Apk() { - updated = false; - size = 0; - apkSource = null; - } + public Apk() { + updated = false; + size = 0; + apkSource = null; + } - public String id; - public String version; - public int vercode; - public int size; // Size in bytes - 0 means we don't know! - public String server; - public String hash; - public String apkName; + public String id; + public String version; + public int vercode; + public int size; // Size in bytes - 0 means we don't know! + public String server; + public String hash; + public String apkName; - // If null, the apk comes from the same server as the repo index. - // Otherwise - // this is the complete URL to download the apk from. - public String apkSource; + // If null, the apk comes from the same server as the repo index. + // Otherwise + // this is the complete URL to download the apk from. + public String apkSource; - // Used internally for tracking during repo updates. - public boolean updated; + // Used internally for tracking during repo updates. + public boolean updated; - public String getURL() { - String path = apkName.replace(" ", "%20"); - return server + "/" + path; - } - } + public String getURL() { + String path = apkName.replace(" ", "%20"); + return server + "/" + path; + } + } - // The TABLE_REPO table stores the details of the repositories in use. - private static final String TABLE_REPO = "fdroid_repo"; - private static final String CREATE_TABLE_REPO = "create table " - + TABLE_REPO + " (" + "address text primary key, " - + "inuse integer not null, " + "priority integer not null);"; + // The TABLE_REPO table stores the details of the repositories in use. + private static final String TABLE_REPO = "fdroid_repo"; + private static final String CREATE_TABLE_REPO = "create table " + + TABLE_REPO + " (" + "address text primary key, " + + "inuse integer not null, " + "priority integer not null);"; - public static class Repo { - public String address; - public boolean inuse; - public int priority; - } + public static class Repo { + public String address; + public boolean inuse; + public int priority; + } - // SQL to update the database to versions beyond the first. Here is - // how the database works: - // - // * The SQL to create the database tables always creates version - // 1. This SQL will never be altered. - // * In the array below there is SQL for each subsequent version - // from 2 onwards. - // * For a new install, the database is always initialised to version - // 1. - // * Then, whether it's a new install or not, all the upgrade SQL in - // the array below is executed in order to bring the database up to - // the latest version. - // * The current version is tracked by an entry in the TABLE_VERSION - // table. - // - private static final String[][] DB_UPGRADES = { + // SQL to update the database to versions beyond the first. Here is + // how the database works: + // + // * The SQL to create the database tables always creates version + // 1. This SQL will never be altered. + // * In the array below there is SQL for each subsequent version + // from 2 onwards. + // * For a new install, the database is always initialised to version + // 1. + // * Then, whether it's a new install or not, all the upgrade SQL in + // the array below is executed in order to bring the database up to + // the latest version. + // * The current version is tracked by an entry in the TABLE_VERSION + // table. + // + private static final String[][] DB_UPGRADES = { - // Version 2... - { "alter table " + TABLE_APP + " add marketVersion text", - "alter table " + TABLE_APP + " add marketVercode integer" }, + // Version 2... + { "alter table " + TABLE_APP + " add marketVersion text", + "alter table " + TABLE_APP + " add marketVercode integer" }, - // Version 3... - { "alter table " + TABLE_APK + " add apkSource text" }, + // Version 3... + { "alter table " + TABLE_APK + " add apkSource text" }, - // Version 4... - { "alter table " + TABLE_APP + " add installedVerCode integer" } + // Version 4... + { "alter table " + TABLE_APP + " add installedVerCode integer" } - }; + }; - private class DBHelper extends SQLiteOpenHelper { + private class DBHelper extends SQLiteOpenHelper { - public DBHelper(Context context) { - super(context, DATABASE_NAME, null, DB_UPGRADES.length + 1); - } + public DBHelper(Context context) { + super(context, DATABASE_NAME, null, DB_UPGRADES.length + 1); + } - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL(CREATE_TABLE_REPO); - db.execSQL(CREATE_TABLE_APP); - db.execSQL(CREATE_TABLE_APK); - ContentValues values = new ContentValues(); - values.put("address", "http://f-droid.org/repo"); - values.put("inuse", 1); - values.put("priority", 10); - db.insert(TABLE_REPO, null, values); - onUpgrade(db, 1, DB_UPGRADES.length + 1); - } + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(CREATE_TABLE_REPO); + db.execSQL(CREATE_TABLE_APP); + db.execSQL(CREATE_TABLE_APK); + ContentValues values = new ContentValues(); + values.put("address", "http://f-droid.org/repo"); + values.put("inuse", 1); + values.put("priority", 10); + db.insert(TABLE_REPO, null, values); + onUpgrade(db, 1, DB_UPGRADES.length + 1); + } - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - for (int v = oldVersion + 1; v <= newVersion; v++) - for (int i = 0; i < DB_UPGRADES[v - 2].length; i++) - db.execSQL(DB_UPGRADES[v - 2][i]); - } + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + for (int v = oldVersion + 1; v <= newVersion; v++) + for (int i = 0; i < DB_UPGRADES[v - 2].length; i++) + db.execSQL(DB_UPGRADES[v - 2][i]); + } - } + } - public static String getIconsPath() { - return "/sdcard/.fdroid/icons/"; - } + public static String getIconsPath() { + return "/sdcard/.fdroid/icons/"; + } - private PackageManager mPm; + private PackageManager mPm; - public DB(Context ctx) { + public DB(Context ctx) { - DBHelper h = new DBHelper(ctx); - db = h.getWritableDatabase(); - mPm = ctx.getPackageManager(); - } + DBHelper h = new DBHelper(ctx); + db = h.getWritableDatabase(); + mPm = ctx.getPackageManager(); + } - public void close() { - db.close(); - db = null; - } + public void close() { + db.close(); + db = null; + } - // Delete the database, which should cause it to be re-created next time - // it's used. - public static void delete(Context ctx) { - try { - ctx.deleteDatabase(DATABASE_NAME); - // Also try and delete the old one, from versions 0.13 and earlier. - ctx.deleteDatabase("fdroid_db"); - } catch (Exception ex) { - Log.d("FDroid", "Exception in DB.delete: " + ex.getMessage()); - } - } + // Delete the database, which should cause it to be re-created next time + // it's used. + public static void delete(Context ctx) { + try { + ctx.deleteDatabase(DATABASE_NAME); + // Also try and delete the old one, from versions 0.13 and earlier. + ctx.deleteDatabase("fdroid_db"); + } catch (Exception ex) { + Log.d("FDroid", "Exception in DB.delete: " + ex.getMessage()); + } + } - // Return a list of apps matching the given criteria. - // 'appid' - specific app id to retrieve, or null - // 'filter' - search text to filter on. - // 'update' - update installed version information from device, rather than - // simply using values cached in the database. Slower. - public Vector getApps(String appid, String filter, boolean update) { - Vector result = new Vector(); - Cursor c = null; - Cursor c2 = null; - try { + // Get the number of apps that have updates available. + public int getNumUpdates() { + Vector apps = getApps(null, null, false); + int count = 0; + for (App app : apps) { + if (app.hasUpdates) + count++; + } + return count; + } - String query = "select * from " + TABLE_APP; - if (appid != null) { - query += " where id = '" + appid + "'"; - } else if (filter != null) { - query += " where name like '%" + filter + "%'" - + " or description like '%" + filter + "%'"; - } - query += " order by name collate nocase"; + // Return a list of apps matching the given criteria. + // 'appid' - specific app id to retrieve, or null + // 'filter' - search text to filter on, or null + // 'update' - update installed version information from device, rather than + // simply using values cached in the database. Slower. + public Vector getApps(String appid, String filter, boolean update) { + Vector result = new Vector(); + Cursor c = null; + Cursor c2 = null; + try { - c = db.rawQuery(query, null); - c.moveToFirst(); - while (!c.isAfterLast()) { + String query = "select * from " + TABLE_APP; + if (appid != null) { + query += " where id = '" + appid + "'"; + } else if (filter != null) { + query += " where name like '%" + filter + "%'" + + " or description like '%" + filter + "%'"; + } + query += " order by name collate nocase"; - App app = new App(); - app.id = c.getString(c.getColumnIndex("id")); - app.name = c.getString(c.getColumnIndex("name")); - app.summary = c.getString(c.getColumnIndex("summary")); - app.icon = c.getString(c.getColumnIndex("icon")); - app.description = c.getString(c.getColumnIndex("description")); - app.license = c.getString(c.getColumnIndex("license")); - app.webURL = c.getString(c.getColumnIndex("webURL")); - app.trackerURL = c.getString(c.getColumnIndex("trackerURL")); - app.sourceURL = c.getString(c.getColumnIndex("sourceURL")); - app.installedVersion = c.getString(c - .getColumnIndex("installedVersion")); - app.installedVerCode = c.getInt(c - .getColumnIndex("installedVerCode")); - app.marketVersion = c.getString(c - .getColumnIndex("marketVersion")); - app.marketVercode = c.getInt(c.getColumnIndex("marketVercode")); - app.hasUpdates = false; + c = db.rawQuery(query, null); + c.moveToFirst(); + while (!c.isAfterLast()) { - c2 = db.rawQuery("select * from " + TABLE_APK - + " where id = ? order by vercode desc", - new String[] { app.id }); - c2.moveToFirst(); - while (!c2.isAfterLast()) { - Apk apk = new Apk(); - apk.id = app.id; - apk.version = c2.getString(c2.getColumnIndex("version")); - apk.vercode = c2.getInt(c2.getColumnIndex("vercode")); - apk.server = c2.getString(c2.getColumnIndex("server")); - apk.hash = c2.getString(c2.getColumnIndex("hash")); - apk.size = c2.getInt(c2.getColumnIndex("size")); - apk.apkName = c2.getString(c2.getColumnIndex("apkName")); - apk.apkSource = c2 - .getString(c2.getColumnIndex("apkSource")); - app.apks.add(apk); - c2.moveToNext(); - } - c2.close(); + App app = new App(); + app.id = c.getString(c.getColumnIndex("id")); + app.name = c.getString(c.getColumnIndex("name")); + app.summary = c.getString(c.getColumnIndex("summary")); + app.icon = c.getString(c.getColumnIndex("icon")); + app.description = c.getString(c.getColumnIndex("description")); + app.license = c.getString(c.getColumnIndex("license")); + app.webURL = c.getString(c.getColumnIndex("webURL")); + app.trackerURL = c.getString(c.getColumnIndex("trackerURL")); + app.sourceURL = c.getString(c.getColumnIndex("sourceURL")); + app.installedVersion = c.getString(c + .getColumnIndex("installedVersion")); + app.installedVerCode = c.getInt(c + .getColumnIndex("installedVerCode")); + app.marketVersion = c.getString(c + .getColumnIndex("marketVersion")); + app.marketVercode = c.getInt(c.getColumnIndex("marketVercode")); + app.hasUpdates = false; - result.add(app); - c.moveToNext(); - } + c2 = db.rawQuery("select * from " + TABLE_APK + + " where id = ? order by vercode desc", + new String[] { app.id }); + c2.moveToFirst(); + while (!c2.isAfterLast()) { + Apk apk = new Apk(); + apk.id = app.id; + apk.version = c2.getString(c2.getColumnIndex("version")); + apk.vercode = c2.getInt(c2.getColumnIndex("vercode")); + apk.server = c2.getString(c2.getColumnIndex("server")); + apk.hash = c2.getString(c2.getColumnIndex("hash")); + apk.size = c2.getInt(c2.getColumnIndex("size")); + apk.apkName = c2.getString(c2.getColumnIndex("apkName")); + apk.apkSource = c2 + .getString(c2.getColumnIndex("apkSource")); + app.apks.add(apk); + c2.moveToNext(); + } + c2.close(); - } catch (Exception e) { - Log.d("FDroid", "Exception during database reading - " - + e.getMessage() + " ... " + e.toString()); - } finally { - if (c != null) { - c.close(); - } - if (c2 != null) { - c2.close(); - } - } + result.add(app); + c.moveToNext(); + } - if (update) { - getUpdates(result); - } + } catch (Exception e) { + Log.d("FDroid", "Exception during database reading - " + + e.getMessage() + " ... " + e.toString()); + } finally { + if (c != null) { + c.close(); + } + if (c2 != null) { + c2.close(); + } + } - // We'll say an application has updates if it's installed AND the - // installed version is not the 'current' one AND the installed - // version is older than the current one. - for (App app : result) { - Apk curver = app.getCurrentVersion(); - if (curver != null && app.installedVersion != null - && !app.installedVersion.equals(curver.version)) { - if (app.installedVerCode < curver.vercode) - app.hasUpdates = true; - } - } + if (update) { + getUpdates(result); + } - return result; - } + // We'll say an application has updates if it's installed AND the + // installed version is not the 'current' one AND the installed + // version is older than the current one. + for (App app : result) { + Apk curver = app.getCurrentVersion(); + if (curver != null && app.installedVersion != null + && !app.installedVersion.equals(curver.version)) { + if (app.installedVerCode < curver.vercode) + app.hasUpdates = true; + } + } - // Verify installed status against the system's package list. - private void getUpdates(Vector apps) { - List installedPackages = mPm.getInstalledPackages(0); - Map systemApks = new HashMap(); - Log.d("FDroid", "Reading installed packages"); - for (PackageInfo appInfo : installedPackages) { - systemApks.put(appInfo.packageName, appInfo); - } + return result; + } - for (DB.App app : apps) { - if (systemApks.containsKey(app.id)) { - PackageInfo sysapk = systemApks.get(app.id); - String version = sysapk.versionName; - int vercode = sysapk.versionCode; - if (app.installedVersion == null - || !app.installedVersion.equals(version)) { - setInstalledVersion(app.id, version, vercode); - app.installedVersion = version; - } - } else { - if (app.installedVersion != null) { - setInstalledVersion(app.id, null, 0); - app.installedVersion = null; - } - } - } - } + // Verify installed status against the system's package list. + private void getUpdates(Vector apps) { + List installedPackages = mPm.getInstalledPackages(0); + Map systemApks = new HashMap(); + Log.d("FDroid", "Reading installed packages"); + for (PackageInfo appInfo : installedPackages) { + systemApks.put(appInfo.packageName, appInfo); + } - private Vector updateApps = null; - private int updateNewUpdates; + for (DB.App app : apps) { + if (systemApks.containsKey(app.id)) { + PackageInfo sysapk = systemApks.get(app.id); + String version = sysapk.versionName; + int vercode = sysapk.versionCode; + if (app.installedVersion == null + || !app.installedVersion.equals(version)) { + setInstalledVersion(app.id, version, vercode); + app.installedVersion = version; + } + } else { + if (app.installedVersion != null) { + setInstalledVersion(app.id, null, 0); + app.installedVersion = null; + } + } + } + } - // Called before a repo update starts. - public void beginUpdate() { - // Get a list of all apps. All the apps and apks in this list will - // have 'updated' set to false at this point, and we will only set - // it to true when we see the app/apk in a repository. Thus, at the - // end, any that are still false can be removed. - // TODO: Need to ensure that UI and UpdateService can't both be doing - // an update at the same time. - updateApps = getApps(null, null, true); - updateNewUpdates = 0; - Log.d("FDroid", "AppUpdate: " + updateApps.size() - + " apps before starting."); - } + private Vector updateApps = null; - // Called when a repo update ends. Any applications that have not been - // updated (by a call to updateApplication) are assumed to be no longer - // in the repos. - // Returns the number of new updates (installed applications for which - // there is a new version available) - public int endUpdate() { - if (updateApps == null) - return 0; - for (App app : updateApps) { - if (!app.updated) { - // The application hasn't been updated, so it's no longer - // in the repos. - Log.d("FDroid", "AppUpdate: " + app.name - + " is no longer in any repository - removing"); - db.delete(TABLE_APP, "id = ?", new String[] { app.id }); - db.delete(TABLE_APK, "id = ?", new String[] { app.id }); - } else { - for (Apk apk : app.apks) { - if (!apk.updated) { - // The package hasn't been updated, so this is a - // version that's no longer available. - Log.d("FDroid", "AppUpdate: Package " + apk.id + "/" - + apk.version - + " is no longer in any repository - removing"); - db.delete(TABLE_APK, "id = ? and version = ?", - new String[] { app.id, apk.version }); - } - } - } - } - Log.d("FDroid", "AppUpdate: " + updateApps.size() - + " apps on completion."); - updateApps = null; - return updateNewUpdates; - } + // Called before a repo update starts. + public void beginUpdate() { + // Get a list of all apps. All the apps and apks in this list will + // have 'updated' set to false at this point, and we will only set + // it to true when we see the app/apk in a repository. Thus, at the + // end, any that are still false can be removed. + // TODO: Need to ensure that UI and UpdateService can't both be doing + // an update at the same time. + updateApps = getApps(null, null, true); + Log.d("FDroid", "AppUpdate: " + updateApps.size() + + " apps before starting."); + } - // Called during update to supply new details for an application (or - // details of a completely new one). Calls to this must be wrapped by - // a call to beginUpdate and a call to endUpdate. - public void updateApplication(App upapp) { + // Called when a repo update ends. Any applications that have not been + // updated (by a call to updateApplication) are assumed to be no longer + // in the repos. + public void endUpdate() { + if (updateApps == null) + return; + for (App app : updateApps) { + if (!app.updated) { + // The application hasn't been updated, so it's no longer + // in the repos. + Log.d("FDroid", "AppUpdate: " + app.name + + " is no longer in any repository - removing"); + db.delete(TABLE_APP, "id = ?", new String[] { app.id }); + db.delete(TABLE_APK, "id = ?", new String[] { app.id }); + } else { + for (Apk apk : app.apks) { + if (!apk.updated) { + // The package hasn't been updated, so this is a + // version that's no longer available. + Log.d("FDroid", "AppUpdate: Package " + apk.id + "/" + + apk.version + + " is no longer in any repository - removing"); + db.delete(TABLE_APK, "id = ? and version = ?", + new String[] { app.id, apk.version }); + } + } + } + } + Log.d("FDroid", "AppUpdate: " + updateApps.size() + + " apps on completion."); + updateApps = null; + return; + } - if (updateApps == null) { - return; - } + // Called during update to supply new details for an application (or + // details of a completely new one). Calls to this must be wrapped by + // a call to beginUpdate and a call to endUpdate. + public void updateApplication(App upapp) { - boolean found = false; - for (App app : updateApps) { - if (app.id.equals(upapp.id)) { - Log.d("FDroid", "AppUpdate: " + app.id - + " is already in the database."); - updateAppIfDifferent(app, upapp); - app.updated = true; - found = true; - for (Apk upapk : upapp.apks) { - boolean afound = false; - for (Apk apk : app.apks) { - if (apk.version.equals(upapk.version)) { - Log.d("FDroid", "AppUpdate: " + apk.version - + " is a known version."); - updateApkIfDifferent(apk, upapk); - apk.updated = true; - afound = true; - break; - } - } - if (!afound) { - // A new version of this application. - Log.d("FDroid", "AppUpdate: " + upapk.version - + " is a new version."); - updateApkIfDifferent(null, upapk); - upapk.updated = true; - app.apks.add(upapk); - if (!app.hasUpdates && app.installedVersion != null) - updateNewUpdates++; - app.hasUpdates = true; - } - } - break; - } - } - if (!found) { - // It's a brand new application... - Log - .d("FDroid", "AppUpdate: " + upapp.id - + " is a new application."); - updateAppIfDifferent(null, upapp); - for (Apk upapk : upapp.apks) { - updateApkIfDifferent(null, upapk); - upapk.updated = true; - } - upapp.updated = true; - updateApps.add(upapp); - } + if (updateApps == null) { + return; + } - } + boolean found = false; + for (App app : updateApps) { + if (app.id.equals(upapp.id)) { + Log.d("FDroid", "AppUpdate: " + app.id + + " is already in the database."); + updateAppIfDifferent(app, upapp); + app.updated = true; + found = true; + for (Apk upapk : upapp.apks) { + boolean afound = false; + for (Apk apk : app.apks) { + if (apk.version.equals(upapk.version)) { + Log.d("FDroid", "AppUpdate: " + apk.version + + " is a known version."); + updateApkIfDifferent(apk, upapk); + apk.updated = true; + afound = true; + break; + } + } + if (!afound) { + // A new version of this application. + Log.d("FDroid", "AppUpdate: " + upapk.version + + " is a new version."); + updateApkIfDifferent(null, upapk); + upapk.updated = true; + app.apks.add(upapk); + } + } + break; + } + } + if (!found) { + // It's a brand new application... + Log + .d("FDroid", "AppUpdate: " + upapp.id + + " is a new application."); + updateAppIfDifferent(null, upapp); + for (Apk upapk : upapp.apks) { + updateApkIfDifferent(null, upapk); + upapk.updated = true; + } + upapp.updated = true; + updateApps.add(upapp); + } - // Update application details in the database, if different to the - // previous ones. - // 'oldapp' - previous details - i.e. what's in the database. - // If null, this app is not in the database at all and - // should be added. - // 'upapp' - updated details - private void updateAppIfDifferent(App oldapp, App upapp) { - ContentValues values = new ContentValues(); - values.put("id", upapp.id); - values.put("name", upapp.name); - values.put("summary", upapp.summary); - values.put("icon", upapp.icon); - values.put("description", upapp.description); - values.put("license", upapp.license); - values.put("webURL", upapp.webURL); - values.put("trackerURL", upapp.trackerURL); - values.put("sourceURL", upapp.sourceURL); - values.put("installedVersion", upapp.installedVersion); - values.put("installedVerCode", upapp.installedVerCode); - values.put("marketVersion", upapp.marketVersion); - values.put("marketVercode", upapp.marketVercode); - values.put("hasUpdates", upapp.hasUpdates ? 1 : 0); - if (oldapp != null) { - db.update(TABLE_APP, values, "id = ?", new String[] { oldapp.id }); - } else { - db.insert(TABLE_APP, null, values); - } - } + } - // Update apk details in the database, if different to the - // previous ones. - // 'oldapk' - previous details - i.e. what's in the database. - // If null, this apk is not in the database at all and - // should be added. - // 'upapk' - updated details - private void updateApkIfDifferent(Apk oldapk, Apk upapk) { - ContentValues values = new ContentValues(); - values.put("id", upapk.id); - values.put("version", upapk.version); - values.put("vercode", upapk.vercode); - values.put("server", upapk.server); - values.put("hash", upapk.hash); - values.put("size", upapk.size); - values.put("apkName", upapk.apkName); - values.put("apkSource", upapk.apkSource); - if (oldapk != null) { - db.update(TABLE_APK, values, "id = ? and version =?", new String[] { - oldapk.id, oldapk.version }); - } else { - db.insert(TABLE_APK, null, values); - } - } + // Update application details in the database, if different to the + // previous ones. + // 'oldapp' - previous details - i.e. what's in the database. + // If null, this app is not in the database at all and + // should be added. + // 'upapp' - updated details + private void updateAppIfDifferent(App oldapp, App upapp) { + ContentValues values = new ContentValues(); + values.put("id", upapp.id); + values.put("name", upapp.name); + values.put("summary", upapp.summary); + values.put("icon", upapp.icon); + values.put("description", upapp.description); + values.put("license", upapp.license); + values.put("webURL", upapp.webURL); + values.put("trackerURL", upapp.trackerURL); + values.put("sourceURL", upapp.sourceURL); + values.put("installedVersion", upapp.installedVersion); + values.put("installedVerCode", upapp.installedVerCode); + values.put("marketVersion", upapp.marketVersion); + values.put("marketVercode", upapp.marketVercode); + values.put("hasUpdates", upapp.hasUpdates ? 1 : 0); + if (oldapp != null) { + db.update(TABLE_APP, values, "id = ?", new String[] { oldapp.id }); + } else { + db.insert(TABLE_APP, null, values); + } + } - public void setInstalledVersion(String id, String version, int vercode) { - ContentValues values = new ContentValues(); - values.put("installedVersion", version); - values.put("installedVerCode", vercode); - db.update(TABLE_APP, values, "id = ?", new String[] { id }); - } + // Update apk details in the database, if different to the + // previous ones. + // 'oldapk' - previous details - i.e. what's in the database. + // If null, this apk is not in the database at all and + // should be added. + // 'upapk' - updated details + private void updateApkIfDifferent(Apk oldapk, Apk upapk) { + ContentValues values = new ContentValues(); + values.put("id", upapk.id); + values.put("version", upapk.version); + values.put("vercode", upapk.vercode); + values.put("server", upapk.server); + values.put("hash", upapk.hash); + values.put("size", upapk.size); + values.put("apkName", upapk.apkName); + values.put("apkSource", upapk.apkSource); + if (oldapk != null) { + db.update(TABLE_APK, values, "id = ? and version =?", new String[] { + oldapk.id, oldapk.version }); + } else { + db.insert(TABLE_APK, null, values); + } + } - // Get a list of the configured repositories. - public Vector getRepos() { - Vector repos = new Vector(); - Cursor c = null; - try { - c = db.rawQuery("select address, inuse, priority from " - + TABLE_REPO + " order by priority", null); - c.moveToFirst(); - while (!c.isAfterLast()) { - Repo repo = new Repo(); - repo.address = c.getString(0); - repo.inuse = (c.getInt(1) == 1); - repo.priority = c.getInt(2); - repos.add(repo); - c.moveToNext(); - } - } catch (Exception e) { - } finally { - if (c != null) { - c.close(); - } - } - return repos; - } + public void setInstalledVersion(String id, String version, int vercode) { + ContentValues values = new ContentValues(); + values.put("installedVersion", version); + values.put("installedVerCode", vercode); + db.update(TABLE_APP, values, "id = ?", new String[] { id }); + } - public void changeServerStatus(String address) { - db.rawQuery("update " + TABLE_REPO - + " set inuse=1-inuse where address= ?", - new String[] { address }); - } + // Get a list of the configured repositories. + public Vector getRepos() { + Vector repos = new Vector(); + Cursor c = null; + try { + c = db.rawQuery("select address, inuse, priority from " + + TABLE_REPO + " order by priority", null); + c.moveToFirst(); + while (!c.isAfterLast()) { + Repo repo = new Repo(); + repo.address = c.getString(0); + repo.inuse = (c.getInt(1) == 1); + repo.priority = c.getInt(2); + repos.add(repo); + c.moveToNext(); + } + } catch (Exception e) { + } finally { + if (c != null) { + c.close(); + } + } + return repos; + } - public void addServer(String address, int priority) { - ContentValues values = new ContentValues(); - values.put("address", address); - values.put("inuse", 1); - values.put("priority", priority); - db.insert(TABLE_REPO, null, values); - } + public void changeServerStatus(String address) { + db.rawQuery("update " + TABLE_REPO + + " set inuse=1-inuse where address= ?", + new String[] { address }); + } - public void removeServers(Vector addresses) { - for (String address : addresses) { - db.delete(TABLE_REPO, "address = ?", new String[] { address }); - } - } + public void addServer(String address, int priority) { + ContentValues values = new ContentValues(); + values.put("address", address); + values.put("inuse", 1); + values.put("priority", priority); + db.insert(TABLE_REPO, null, values); + } + + public void removeServers(Vector addresses) { + for (String address : addresses) { + db.delete(TABLE_REPO, "address = ?", new String[] { address }); + } + } } diff --git a/src/org/fdroid/fdroid/RepoXMLHandler.java b/src/org/fdroid/fdroid/RepoXMLHandler.java index d012a7687..46023ed44 100644 --- a/src/org/fdroid/fdroid/RepoXMLHandler.java +++ b/src/org/fdroid/fdroid/RepoXMLHandler.java @@ -184,8 +184,7 @@ public class RepoXMLHandler extends DefaultHandler { } } - // Returns the number of applications with updates. - public static int doUpdates(Context ctx, DB db) { + public static void doUpdates(Context ctx, DB db) { db.beginUpdate(); Vector repos = db.getRepos(); for (DB.Repo repo : repos) { @@ -236,7 +235,7 @@ public class RepoXMLHandler extends DefaultHandler { } } - return db.endUpdate(); + db.endUpdate(); } diff --git a/src/org/fdroid/fdroid/UpdateService.java b/src/org/fdroid/fdroid/UpdateService.java index a7ce2024c..29e785c2f 100644 --- a/src/org/fdroid/fdroid/UpdateService.java +++ b/src/org/fdroid/fdroid/UpdateService.java @@ -35,121 +35,129 @@ import android.util.Log; public class UpdateService extends Service { - // Schedule (or cancel schedule for) this service, according to the - // current preferences. Should be called a) at boot, or b) if the preference - // is changed. - // TODO: What if we get upgraded? - public static void schedule(Context ctx) { + // Schedule (or cancel schedule for) this service, according to the + // current preferences. Should be called a) at boot, or b) if the preference + // is changed. + // TODO: What if we get upgraded? + public static void schedule(Context ctx) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(ctx); - String sint = prefs.getString("updateInterval", "0"); - int interval = Integer.parseInt(sint); + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(ctx); + String sint = prefs.getString("updateInterval", "0"); + int interval = Integer.parseInt(sint); - Intent intent = new Intent(ctx, UpdateService.class); - PendingIntent pending = PendingIntent.getService(ctx, 0, intent, 0); + Intent intent = new Intent(ctx, UpdateService.class); + PendingIntent pending = PendingIntent.getService(ctx, 0, intent, 0); - AlarmManager alarm = (AlarmManager) ctx - .getSystemService(Context.ALARM_SERVICE); - alarm.cancel(pending); - if (interval > 0) { - alarm.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, - SystemClock.elapsedRealtime() + 5000, - AlarmManager.INTERVAL_HOUR, pending); - } + AlarmManager alarm = (AlarmManager) ctx + .getSystemService(Context.ALARM_SERVICE); + alarm.cancel(pending); + if (interval > 0) { + alarm.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, + SystemClock.elapsedRealtime() + 5000, + AlarmManager.INTERVAL_HOUR, pending); + } - } + } - // For API levels <5 - @Override - public void onStart(Intent intent, int startId) { - handleCommand(); - } + // For API levels <5 + @Override + public void onStart(Intent intent, int startId) { + handleCommand(); + } - // For API levels >=5 - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - handleCommand(); - return START_REDELIVER_INTENT; - } + // For API levels >=5 + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + handleCommand(); + return START_REDELIVER_INTENT; + } - private void handleCommand() { + private void handleCommand() { - new Thread() { - public void run() { + new Thread() { + public void run() { - // If we're in one of our list activities, we don't want - // to run an update because the database will be out of - // sync with the display. - if (((FDroidApp) getApplication()).inActivity != 0) - return; + // If we're in one of our list activities, we don't want + // to run an update because the database will be out of + // sync with the display. + if (((FDroidApp) getApplication()).inActivity != 0) + return; - // See if it's time to actually do anything yet... - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(getBaseContext()); - long lastUpdate = prefs.getLong("lastUpdateCheck", 0); - String sint = prefs.getString("updateInterval", "0"); - int interval = Integer.parseInt(sint); - if (interval == 0) - return; - if (lastUpdate + (interval * 60 * 60) > System - .currentTimeMillis()) - return; + // See if it's time to actually do anything yet... + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(getBaseContext()); + long lastUpdate = prefs.getLong("lastUpdateCheck", 0); + String sint = prefs.getString("updateInterval", "0"); + int interval = Integer.parseInt(sint); + if (interval == 0) + return; + if (lastUpdate + (interval * 60 * 60) > System + .currentTimeMillis()) + return; - // Make sure we have a connection... - ConnectivityManager netstate = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - if (netstate.getNetworkInfo(1).getState() != NetworkInfo.State.CONNECTED - && netstate.getNetworkInfo(0).getState() != NetworkInfo.State.CONNECTED) - return; + // Make sure we have a connection... + ConnectivityManager netstate = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + if (netstate.getNetworkInfo(1).getState() != NetworkInfo.State.CONNECTED + && netstate.getNetworkInfo(0).getState() != NetworkInfo.State.CONNECTED) + return; - // Do the update... - DB db = null; - try { - db = new DB(getBaseContext()); - int updateNum = RepoXMLHandler.doUpdates(getBaseContext(), - db); + // Do the update... + DB db = null; + try { + db = new DB(getBaseContext()); + boolean notify = prefs.getBoolean("updateNotify", false); - if (updateNum != 0) { - // We have updates. - if (prefs.getBoolean("updateNotify", false)) { - // And the user wants to know. - NotificationManager n = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - Notification notification = new Notification( - R.drawable.icon, - "FDroid Updates Available", System - .currentTimeMillis()); - Context context = getApplicationContext(); - CharSequence contentTitle = "FDroid"; - CharSequence contentText = "Updates are available."; - Intent notificationIntent = new Intent( - UpdateService.this, FDroid.class); - PendingIntent contentIntent = PendingIntent - .getActivity(UpdateService.this, 0, - notificationIntent, 0); - notification.setLatestEventInfo(context, - contentTitle, contentText, contentIntent); - notification.flags |= Notification.FLAG_AUTO_CANCEL; - n.notify(1, notification); - } - } + // Get the number of updates available before we + // start, so we can notify if there are new ones. + // (But avoid doing it if the user doesn't want + // notifications, since it may be time consuming) + int prevUpdates = 0; + if (notify) + prevUpdates = db.getNumUpdates(); - } catch (Exception e) { - Log.d("FDroid", "Exception during handleCommand() - " - + e.getMessage()); - } finally { - if (db != null) - db.close(); - stopSelf(); - } + RepoXMLHandler.doUpdates(getBaseContext(), db); - } - }.start(); + if (notify) { + if (db.getNumUpdates() > prevUpdates) { + // And the user wants to know. + NotificationManager n = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + Notification notification = new Notification( + R.drawable.icon, + "FDroid Updates Available", System + .currentTimeMillis()); + Context context = getApplicationContext(); + CharSequence contentTitle = "FDroid"; + CharSequence contentText = "Updates are available."; + Intent notificationIntent = new Intent( + UpdateService.this, FDroid.class); + PendingIntent contentIntent = PendingIntent + .getActivity(UpdateService.this, 0, + notificationIntent, 0); + notification.setLatestEventInfo(context, + contentTitle, contentText, contentIntent); + notification.flags |= Notification.FLAG_AUTO_CANCEL; + n.notify(1, notification); + } + } - } + } catch (Exception e) { + Log.d("FDroid", "Exception during handleCommand() - " + + e.getMessage()); + } finally { + if (db != null) + db.close(); + stopSelf(); + } - @Override - public IBinder onBind(Intent intent) { - return null; - } + } + }.start(); + + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } }