diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index 1f5c10e49..aa2c7dfa8 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -37,9 +37,6 @@ android:key="antiNonFreeDep" /> - diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java index 0a91d0b6f..5eb7e030d 100644 --- a/src/org/fdroid/fdroid/AppDetails.java +++ b/src/org/fdroid/fdroid/AppDetails.java @@ -22,8 +22,7 @@ import java.io.File; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; - -import org.fdroid.fdroid.DB.Apk.CompatibilityChecker; +import java.util.Vector; import android.app.AlertDialog; import android.app.ListActivity; @@ -130,12 +129,6 @@ public class AppDetails extends ListActivity { } else { added.setVisibility(View.GONE); } - if (!compatChecker.isCompatible(apk)) { - View[] views = { v, version, status, size, buildtype, added }; - for (View view : views) { - view.setEnabled(false); - } - } return v; } } @@ -163,7 +156,6 @@ public class AppDetails extends ListActivity { private int app_currentvercode; private DB.Apk curapk; private String appid; - private CompatibilityChecker compatChecker; private PackageManager mPm; private DownloadHandler downloadHandler; private boolean stateRetained; @@ -217,15 +209,6 @@ public class AppDetails extends ListActivity { if (viewResetRequired) { reset(); viewResetRequired = false; - } else { - // Doing the reset() will usually get our compatChecker, but if - // we're skipping that we'd better get one now... - try { - DB db = DB.getDB(); - compatChecker = db.getCompatibilityChecker(); - } finally { - DB.releaseDB(); - } } if (downloadHandler != null) { downloadHandler.startUpdates(); @@ -280,16 +263,22 @@ public class AppDetails extends ListActivity { // Reset the display and list contents. Used when entering the activity, and // also when something has been installed/uninstalled. private void reset() { + Log.d("FDroid", "Getting application details for " + appid); - DB.Apk curver; - try { - DB db = DB.getDB(); - compatChecker = db.getCompatibilityChecker(); - app = db.getApps(appid, null, true, false).get(0); - curver = app.getCurrentVersion(compatChecker); - } finally { - DB.releaseDB(); + app = null; + Vector apps = ((FDroidApp) getApplication()).getApps(); + for (DB.App tapp : apps) { + if (tapp.id.equals(appid)) { + app = tapp; + break; + } } + if (app == null) { + finish(); + return; + } + + DB.Apk curver = app.getCurrentVersion(); app_currentvercode = curver == null ? 0 : curver.vercode; // Get the signature of the installed package... @@ -359,7 +348,7 @@ public class AppDetails extends ListActivity { if (app.installedVersion != null && app.installedVersion.equals(curapk.version)) { removeApk(app.id); - } else if (compatChecker.isCompatible(curapk)) { + } else { install(); } } @@ -369,7 +358,7 @@ public class AppDetails extends ListActivity { super.onCreateOptionsMenu(menu); menu.clear(); - DB.Apk curver = app.getCurrentVersion(compatChecker); + DB.Apk curver = app.getCurrentVersion(); if (app.installedVersion != null && curver != null && !app.installedVersion.equals(curver.version)) { menu.add(Menu.NONE, INSTALL, 0, R.string.menu_update).setIcon( @@ -413,7 +402,7 @@ public class AppDetails extends ListActivity { case INSTALL: // Note that this handles updating as well as installing. - curapk = app.getCurrentVersion(compatChecker); + curapk = app.getCurrentVersion(); if (curapk != null) install(); return true; @@ -480,6 +469,8 @@ public class AppDetails extends ListActivity { Uri uri = Uri.fromParts("package", pkginfo.packageName, null); Intent intent = new Intent(Intent.ACTION_DELETE, uri); startActivityForResult(intent, REQUEST_UNINSTALL); + ((FDroidApp) getApplication()).invalidateApps(); + } private void installApk(String file) { @@ -488,6 +479,7 @@ public class AppDetails extends ListActivity { intent.setDataAndType(Uri.parse("file://" + file), "application/vnd.android.package-archive"); startActivityForResult(intent, REQUEST_INSTALL); + ((FDroidApp) getApplication()).invalidateApps(); } private ProgressDialog createProgressDialog(String file, int p, int max) { diff --git a/src/org/fdroid/fdroid/AppFilter.java b/src/org/fdroid/fdroid/AppFilter.java new file mode 100644 index 000000000..3ffb55b51 --- /dev/null +++ b/src/org/fdroid/fdroid/AppFilter.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010-12 Ciaran Gultnieks, ciaran@ciarang.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.fdroid.fdroid; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +public class AppFilter { + + boolean pref_antiAds; + boolean pref_antiTracking; + boolean pref_antiNonFreeAdd; + boolean pref_antiNonFreeNet; + boolean pref_antiNonFreeDep; + boolean pref_rooted; + + public AppFilter(Context ctx) { + + // Read preferences and cache them so we can do quick lookups. + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(ctx); + pref_antiAds = prefs.getBoolean("antiAds", false); + pref_antiTracking = prefs.getBoolean("antiTracking", false); + pref_antiNonFreeAdd = prefs.getBoolean("antiNonFreeAdd", false); + pref_antiNonFreeNet = prefs.getBoolean("antiNonFreeNet", false); + pref_antiNonFreeDep = prefs.getBoolean("antiNonFreeDep", false); + pref_rooted = prefs.getBoolean("rooted", true); + } + + // Return true if the given app should be filtered based on user + // preferences, and false otherwise. + public boolean filter(DB.App app) { + boolean filtered = false; + if (app.antiFeatures != null) { + for (String af : app.antiFeatures) { + if (af.equals("Ads") && !pref_antiAds) + filtered = true; + else if (af.equals("Tracking") && !pref_antiTracking) + filtered = true; + else if (af.equals("NonFreeNet") && !pref_antiNonFreeNet) + filtered = true; + else if (af.equals("NonFreeAdd") && !pref_antiNonFreeAdd) + filtered = true; + else if (af.equals("NonFreeDep") && !pref_antiNonFreeDep) + filtered = true; + } + } + if (app.requirements != null) { + for (String r : app.requirements) { + if (r.equals("root") && !pref_rooted) + filtered = true; + } + } + return filtered; + } + +} diff --git a/src/org/fdroid/fdroid/DB.java b/src/org/fdroid/fdroid/DB.java index 5d11483f0..5bbfa8a9a 100644 --- a/src/org/fdroid/fdroid/DB.java +++ b/src/org/fdroid/fdroid/DB.java @@ -20,7 +20,9 @@ package org.fdroid.fdroid; import java.text.SimpleDateFormat; +import java.util.Collections; import java.util.Date; +import java.util.Dictionary; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -52,7 +54,7 @@ public class DB { static void initDB(Context ctx) { dbInstance = new DB(ctx); } - + // Get access to the database. Must be called before any database activity, // and releaseDB must be called subsequently. Returns null in the event of // failure. @@ -90,7 +92,7 @@ public class DB { + "installedVersion text," + "hasUpdates int not null," + "primary key(id));"; - public static class App { + public static class App implements Comparable { public App() { name = "Unknown"; @@ -154,25 +156,22 @@ public class DB { // 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. - // To skip compatibility checks, pass null as the checker. - public Apk getCurrentVersion(DB.Apk.CompatibilityChecker checker) { + public Apk getCurrentVersion() { // Try and return the real current version first... if (marketVersion != null && marketVercode > 0) { for (Apk apk : apks) { - if (apk.vercode == marketVercode - && (checker == null || checker.isCompatible(apk))) + if (apk.vercode == marketVercode) return apk; } } // If we don't know the current version, or we don't have it, we - // return the most recent compatible version we have... + // return the most recent version we have... int latestcode = -1; Apk latestapk = null; for (Apk apk : apks) { - if (apk.vercode > latestcode - && (checker == null || checker.isCompatible(apk))) { + if (apk.vercode > latestcode) { latestapk = apk; latestcode = apk.vercode; } @@ -180,6 +179,11 @@ public class DB { return latestapk; } + @Override + public int compareTo(App arg0) { + return name.compareTo(arg0.name); + } + } // The TABLE_APK table stores details of all the application versions we @@ -298,12 +302,6 @@ public class DB { } } - // Let other classes reuse the already instantiated compatibility - // checker, mostly to avoid redundant log output. - public Apk.CompatibilityChecker getCompatibilityChecker() { - return compatChecker; - } - // 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 " @@ -377,7 +375,10 @@ public class DB { // Version 14... { "alter table " + TABLE_APK + " add added string", "alter table " + TABLE_APP + " add added string", - "alter table " + TABLE_APP + " add lastUpdated string" } }; + "alter table " + TABLE_APP + " add lastUpdated string" }, + + // Version 15... + { "create index apk_vercode on " + TABLE_APK + " (vercode);" } }; private class DBHelper extends SQLiteOpenHelper { @@ -416,7 +417,7 @@ public class DB { private PackageManager mPm; private Context mContext; - private Apk.CompatibilityChecker compatChecker; + private Apk.CompatibilityChecker compatChecker = null; // The date format used for storing dates (e.g. lastupdated, added) in the // database. @@ -441,7 +442,6 @@ public class DB { sync_mode = null; if (sync_mode != null) Log.d("FDroid", "Database synchronization mode: " + sync_mode); - compatChecker = Apk.CompatibilityChecker.getChecker(ctx); } public void close() { @@ -462,9 +462,10 @@ public class DB { } } - // Get the number of apps that have updates available. + // Get the number of apps that have updates available. This can be a + // time consuming operation. public int getNumUpdates() { - Vector apps = getApps(null, null, false, true); + Vector apps = getApps(true); int count = 0; for (App app : apps) { if (app.hasUpdates) @@ -482,7 +483,6 @@ public class DB { c.moveToFirst(); while (!c.isAfterLast()) { String s = c.getString(c.getColumnIndex("category")); - Log.d("FDroid", "Category: " + s); if (s == null) { s = "none"; } @@ -504,157 +504,85 @@ public class DB { // Return a list of apps matching the given criteria. Filtering is // also done based on compatibility and anti-features according to // the user's current preferences. - // '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. - // 'exclusions' - apply filtering for compatibility, anti-features, etc. - public Vector getApps(String appid, String filter, boolean update, - boolean exclusions) { + public Vector getApps(boolean getinstalledinfo) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(mContext); - boolean pref_antiAds = prefs.getBoolean("antiAds", false); - boolean pref_antiTracking = prefs.getBoolean("antiTracking", false); - boolean pref_antiNonFreeAdd = prefs.getBoolean("antiNonFreeAdd", false); - boolean pref_antiNonFreeNet = prefs.getBoolean("antiNonFreeNet", false); - boolean pref_antiNonFreeDep = prefs.getBoolean("antiNonFreeDep", false); - boolean pref_showIncompat = prefs.getBoolean("showIncompatible", false); - boolean pref_rooted = prefs.getBoolean("rooted", true); - - Vector result = new Vector(); + Map apps = new HashMap(); Cursor c = null; - Cursor c2 = null; + long startTime = System.currentTimeMillis(); try { - 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"; - - c = db.rawQuery(query, null); + c = db.query(TABLE_APP, null, null, null, null, null, null); c.moveToFirst(); while (!c.isAfterLast()) { App app = new App(); app.antiFeatures = DB.CommaSeparatedList.make(c.getString(c .getColumnIndex("antiFeatures"))); - boolean include = true; - if (app.antiFeatures != null && exclusions) { - for (String af : app.antiFeatures) { - if (af.equals("Ads") && !pref_antiAds) - include = false; - else if (af.equals("Tracking") && !pref_antiTracking) - include = false; - else if (af.equals("NonFreeNet") - && !pref_antiNonFreeNet) - include = false; - else if (af.equals("NonFreeAdd") - && !pref_antiNonFreeAdd) - include = false; - else if (af.equals("NonFreeDep") - && !pref_antiNonFreeDep) - include = false; - } - } app.requirements = DB.CommaSeparatedList.make(c.getString(c .getColumnIndex("requirements"))); - if (app.requirements != null && exclusions) { - for (String r : app.requirements) { - if (r.equals("root") && !pref_rooted) { - include = false; - } - } - } + 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.category = c.getString(c.getColumnIndex("category")); + app.webURL = c.getString(c.getColumnIndex("webURL")); + app.trackerURL = c.getString(c.getColumnIndex("trackerURL")); + app.sourceURL = c.getString(c.getColumnIndex("sourceURL")); + app.donateURL = c.getString(c.getColumnIndex("donateURL")); + app.marketVersion = c.getString(c + .getColumnIndex("marketVersion")); + app.marketVercode = c.getInt(c.getColumnIndex("marketVercode")); + String sAdded = c.getString(c.getColumnIndex("added")); + app.added = (sAdded == null || sAdded.length() == 0) ? null + : mDateFormat.parse(sAdded); + String sLastUpdated = c.getString(c + .getColumnIndex("lastUpdated")); + app.lastUpdated = (sLastUpdated == null || sLastUpdated + .length() == 0) ? null : mDateFormat + .parse(sLastUpdated); + app.hasUpdates = false; - if (include) { - 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.category = c.getString(c.getColumnIndex("category")); - app.webURL = c.getString(c.getColumnIndex("webURL")); - app.trackerURL = c - .getString(c.getColumnIndex("trackerURL")); - app.sourceURL = c.getString(c.getColumnIndex("sourceURL")); - app.donateURL = c.getString(c.getColumnIndex("donateURL")); - 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")); - String sAdded = c.getString(c.getColumnIndex("added")); - app.added = (sAdded == null || sAdded.length() == 0) ? null - : mDateFormat.parse(sAdded); - String sLastUpdated = c.getString(c - .getColumnIndex("lastUpdated")); - app.lastUpdated = (sLastUpdated == null || sLastUpdated - .length() == 0) ? null : mDateFormat - .parse(sLastUpdated); - app.hasUpdates = false; - - c2 = db.rawQuery("select * from " + TABLE_APK - + " where id = ? order by vercode desc", - new String[] { app.id }); - c2.moveToFirst(); - boolean compatible = pref_showIncompat || !exclusions; - 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.hashType = c2.getString(c2 - .getColumnIndex("hashType")); - apk.sig = c2.getString(c2.getColumnIndex("sig")); - apk.srcname = c2 - .getString(c2.getColumnIndex("srcname")); - apk.size = c2.getInt(c2.getColumnIndex("size")); - apk.apkName = c2 - .getString(c2.getColumnIndex("apkName")); - apk.apkSource = c2.getString(c2 - .getColumnIndex("apkSource")); - apk.minSdkVersion = c2.getInt(c2 - .getColumnIndex("minSdkVersion")); - String sApkAdded = c2.getString(c2 - .getColumnIndex("added")); - apk.added = (sApkAdded == null || sApkAdded.length() == 0) ? null - : mDateFormat.parse(sApkAdded); - apk.permissions = CommaSeparatedList.make(c2 - .getString(c2.getColumnIndex("permissions"))); - apk.features = CommaSeparatedList.make(c2.getString(c2 - .getColumnIndex("features"))); - app.apks.add(apk); - if (!compatible && compatChecker.isCompatible(apk)) { - // At least one compatible APK. - compatible = true; - } - c2.moveToNext(); - } - c2.close(); - - if (compatible) { - result.add(app); - } else { - Log.d("FDroid", "Excluding incompatible application: " - + app.id); - } - } + apps.put(app.id, app); c.moveToNext(); } + c.close(); + c = null; + + Log.d("FDroid", "Read app data from database " + " (took " + + (System.currentTimeMillis() - startTime) + " ms)"); + + c = db.query(TABLE_APK, null, null, null, null, null, + "vercode desc"); + c.moveToFirst(); + while (!c.isAfterLast()) { + Apk apk = new Apk(); + apk.id = c.getString(c.getColumnIndex("id")); + apk.version = c.getString(c.getColumnIndex("version")); + apk.vercode = c.getInt(c.getColumnIndex("vercode")); + apk.server = c.getString(c.getColumnIndex("server")); + apk.hash = c.getString(c.getColumnIndex("hash")); + apk.hashType = c.getString(c.getColumnIndex("hashType")); + apk.sig = c.getString(c.getColumnIndex("sig")); + apk.srcname = c.getString(c.getColumnIndex("srcname")); + apk.size = c.getInt(c.getColumnIndex("size")); + apk.apkName = c.getString(c.getColumnIndex("apkName")); + apk.apkSource = c.getString(c.getColumnIndex("apkSource")); + apk.minSdkVersion = c.getInt(c + .getColumnIndex("minSdkVersion")); + String sApkAdded = c.getString(c.getColumnIndex("added")); + apk.added = (sApkAdded == null || sApkAdded.length() == 0) ? null + : mDateFormat.parse(sApkAdded); + apk.permissions = CommaSeparatedList.make(c.getString(c + .getColumnIndex("permissions"))); + apk.features = CommaSeparatedList.make(c.getString(c + .getColumnIndex("features"))); + apps.get(apk.id).apks.add(apk); + c.moveToNext(); + } + c.close(); } catch (Exception e) { Log.e("FDroid", @@ -664,35 +592,28 @@ public class DB { if (c != null) { c.close(); } - if (c2 != null) { - c2.close(); - } + + Log.d("FDroid", "Read app and apk data from database " + " (took " + + (System.currentTimeMillis() - startTime) + " ms)"); } - if (update) { - db.beginTransaction(); - try { - getUpdates(result); - db.setTransactionSuccessful(); - } catch (Exception e) { - Log.e("FDroid", - "Exception while getting updates: " - + Log.getStackTraceString(e)); - } finally { - db.endTransaction(); - } - } + Vector result = new Vector(apps.values()); + Collections.sort(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(compatChecker); - if (curver != null && app.installedVersion != null - && !app.installedVersion.equals(curver.version)) { - if (app.installedVerCode < curver.vercode) { - app.hasUpdates = true; - app.currentVersion = curver.version; + if (getinstalledinfo) { + getInstalledInfo(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; + app.currentVersion = curver.version; + } } } } @@ -700,8 +621,8 @@ public class DB { return result; } - // Verify installed status against the system's package list. - private void getUpdates(Vector apps) { + // Get installation status for all apps. + private void getInstalledInfo(Vector apps) { List installedPackages = mPm.getInstalledPackages(0); Map systemApks = new HashMap(); Log.d("FDroid", "Reading installed packages"); @@ -712,19 +633,11 @@ public class DB { 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; - app.installedVerCode = vercode; - } + app.installedVersion = sysapk.versionName; + app.installedVerCode = sysapk.versionCode; } else { - if (app.installedVersion != null) { - setInstalledVersion(app.id, null, 0); - app.installedVersion = null; - } + app.installedVersion = null; + app.installedVerCode = 0; } } } @@ -760,21 +673,27 @@ public class DB { private Vector updateApps = null; - // Called before a repo update starts. - public void beginUpdate() { + // Called before a repo update starts. Returns the number of updates + // available beforehand. + public int beginUpdate(Vector apps) { // 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, false); + updateApps = apps; Log.d("FDroid", "AppUpdate: " + updateApps.size() + " apps before starting."); // Wrap the whole update in a transaction. Make sure to call // either endUpdate or cancelUpdate to commit or discard it, // respectively. db.beginTransaction(); + + int count = 0; + for (App app : updateApps) { + if (app.hasUpdates) + count++; + } + return count; } // Called when a repo update ends. Any applications that have not been @@ -831,6 +750,21 @@ public class DB { return; } + // Lazy initialise this... + if (compatChecker == null) + compatChecker = Apk.CompatibilityChecker.getChecker(mContext); + + // See if it's compatible (by which we mean if it has at least one + // compatible apk - if it's not, leave it out) + // Also keep a list of which were compatible, because they're the + // only ones we'll add. + Vector compatibleapks = new Vector(); + for (Apk apk : upapp.apks) + if (compatChecker.isCompatible(apk)) + compatibleapks.add(apk); + if (compatibleapks.size() == 0) + return; + boolean found = false; for (App app : updateApps) { if (app.id.equals(upapp.id)) { @@ -839,7 +773,7 @@ public class DB { updateApp(app, upapp); app.updated = true; found = true; - for (Apk upapk : upapp.apks) { + for (Apk upapk : compatibleapks) { boolean afound = false; for (Apk apk : app.apks) { if (apk.version.equals(upapk.version)) { @@ -869,7 +803,7 @@ public class DB { // .d("FDroid", "AppUpdate: " + upapp.id // + " is a new application."); updateApp(null, upapp); - for (Apk upapk : upapp.apks) { + for (Apk upapk : compatibleapks) { updateApkIfDifferent(null, upapk); upapk.updated = true; } diff --git a/src/org/fdroid/fdroid/FDroid.java b/src/org/fdroid/fdroid/FDroid.java index b37a2e2bf..291017851 100644 --- a/src/org/fdroid/fdroid/FDroid.java +++ b/src/org/fdroid/fdroid/FDroid.java @@ -33,12 +33,14 @@ import android.app.TabActivity; import android.app.AlertDialog.Builder; import android.content.DialogInterface; import android.content.Intent; +import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.ResultReceiver; +import android.preference.PreferenceManager; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -144,7 +146,7 @@ public class FDroid extends TabActivity implements OnItemClickListener, @Override protected void onStart() { super.onStart(); - populateLists(true, true); + populateLists(); } @Override @@ -322,11 +324,7 @@ public class FDroid extends TabActivity implements OnItemClickListener, } // Populate the lists. - // 'update' - true to update the installed status of the applications - // by asking the system. - // 'refreshdb' - true to refresh the list from the database rather - // than using the last one we got. - private void populateLists(boolean update, boolean refreshdb) { + private void populateLists() { apps_in.clear(); apps_av.clear(); @@ -348,7 +346,6 @@ public class FDroid extends TabActivity implements OnItemClickListener, cat_recentlyupdated = getString(R.string.category_recentlyupdated); categories.add(cat_all); for (String s : db.getCategories()) { - Log.d("FDroid", "s: " + s); categories.add(s); } categories.add(cat_whatsnew); @@ -356,13 +353,12 @@ public class FDroid extends TabActivity implements OnItemClickListener, if (currentCategory == null) currentCategory = cat_all; - if(apps == null || refreshdb || update) - apps = db.getApps(null, null, update, true); - } finally { DB.releaseDB(); } + apps = ((FDroidApp) getApplication()).getApps(); + if (apps.isEmpty()) { // Don't attempt this more than once - we may have invalid // repositories. @@ -382,6 +378,8 @@ public class FDroid extends TabActivity implements OnItemClickListener, recent.add(Calendar.DAY_OF_YEAR, -14); Date recentDate = recent.getTime(); + AppFilter appfilter = new AppFilter(this); + for (DB.App app : apps) { if (currentCategory.equals(cat_all)) { // Let everything through! @@ -403,15 +401,21 @@ public class FDroid extends TabActivity implements OnItemClickListener, if (!currentCategory.equals(app.category)) continue; } + + boolean filtered = appfilter.filter(app); + + // Add it to the list(s). Always to installed and updates, but + // only to available if it's not filtered. if (app.installedVersion == null) { apps_av.addItem(app); } else { - apps_in.addItem(app); + if (!filtered) + apps_in.addItem(app); if (app.hasUpdates) apps_up.addItem(app); } } - + // Update the count on the 'Updates' tab to show the number available. // This is quite unpleasant, but seems to be the only way to do it. TextView uptext = (TextView) tabHost.getTabWidget().getChildAt(2) @@ -444,7 +448,7 @@ public class FDroid extends TabActivity implements OnItemClickListener, Toast.makeText(FDroid.this, resultData.getString("errmsg"), Toast.LENGTH_LONG).show(); } else { - populateLists(true, true); + populateLists(); } if (pd.isShowing()) pd.dismiss(); @@ -470,7 +474,7 @@ public class FDroid extends TabActivity implements OnItemClickListener, public void onItemSelected(AdapterView parent, View view, int pos, long id) { currentCategory = parent.getItemAtPosition(pos).toString(); - populateLists(false, false); + populateLists(); } public void onNothingSelected(AdapterView parent) { diff --git a/src/org/fdroid/fdroid/FDroidApp.java b/src/org/fdroid/fdroid/FDroidApp.java index 05d0d375a..ce4756900 100644 --- a/src/org/fdroid/fdroid/FDroidApp.java +++ b/src/org/fdroid/fdroid/FDroidApp.java @@ -18,6 +18,9 @@ package org.fdroid.fdroid; +import java.util.Vector; +import java.util.concurrent.Semaphore; + import android.app.Application; public class FDroidApp extends Application { @@ -25,10 +28,60 @@ public class FDroidApp extends Application { @Override public void onCreate() { super.onCreate(); - + + apps = null; DB.initDB(getApplicationContext()); } + // Global list of all known applications. + private Vector apps; + + // Set when something has changed (database or installed apps) so we know + // we should invalidate the apps. + private volatile boolean appsInvalid = false; + private Semaphore appsInvalidLock = new Semaphore(1, false); + + // Set apps invalid. Call this when the database has been updated with + // new app information, or when the installed packages have changed. + public void invalidateApps() { + try { + appsInvalidLock.acquire(); + appsInvalid = true; + } catch (InterruptedException e) { + // Don't care + } finally { + appsInvalidLock.release(); + } + } + + // Get a list of all known applications. Should not be called when the + // database is locked (i.e. between DB.getDB() and db.releaseDB(). The + // contents should never be modified, it's for reading only. + public Vector getApps() { + + boolean invalid = false; + try { + appsInvalidLock.acquire(); + invalid = appsInvalid; + if (invalid) + appsInvalid = false; + } catch (InterruptedException e) { + // Don't care + } finally { + appsInvalidLock.release(); + } + + if (apps == null || invalid) { + try { + DB db = DB.getDB(); + apps = db.getApps(true); + } finally { + DB.releaseDB(); + } + } + if (apps == null) + return new Vector(); + return apps; + } } - diff --git a/src/org/fdroid/fdroid/SearchResults.java b/src/org/fdroid/fdroid/SearchResults.java index 18707401a..82dac1280 100644 --- a/src/org/fdroid/fdroid/SearchResults.java +++ b/src/org/fdroid/fdroid/SearchResults.java @@ -67,13 +67,18 @@ public class SearchResults extends ListActivity { } private void updateView() { - Vector apps; - try { - DB db = DB.getDB(); - apps = db.getApps(null, mQuery, false, true); - } finally { - DB.releaseDB(); + + Vector apps= new Vector(); + AppFilter appfilter = new AppFilter(this); + String mq = mQuery.toLowerCase(); + Vector tapps = ((FDroidApp) getApplication()).getApps(); + for(DB.App tapp : tapps) { + if(tapp.name.toLowerCase().contains(mq) || tapp.description.toLowerCase().contains(mq)) { + if(!appfilter.filter(tapp)) + apps.add(tapp); + } } + TextView tv = (TextView) findViewById(R.id.description); String headertext; if(apps.size()==0) diff --git a/src/org/fdroid/fdroid/UpdateService.java b/src/org/fdroid/fdroid/UpdateService.java index bf07087c5..3c72d0a10 100644 --- a/src/org/fdroid/fdroid/UpdateService.java +++ b/src/org/fdroid/fdroid/UpdateService.java @@ -102,16 +102,7 @@ public class UpdateService extends IntentService { int newUpdates = 0; Vector repos; try { - - // 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) - if (notify) - prevUpdates = db.getNumUpdates(); - repos = db.getRepos(); - } finally { DB.releaseDB(); } @@ -136,13 +127,15 @@ public class UpdateService extends IntentService { } if (success) { + Vector prevapps = ((FDroidApp)getApplication()).getApps(); db = DB.getDB(); try { - db.beginUpdate(); + prevUpdates = db.beginUpdate(prevapps); for (DB.App app : apps) { db.updateApplication(app); } db.endUpdate(); + ((FDroidApp)getApplication()).invalidateApps(); if (notify) newUpdates = db.getNumUpdates(); } catch (Exception ex) {