Performence improvements, work in progress

This commit is contained in:
Ciaran Gultnieks 2012-09-13 21:28:19 +01:00
parent 003d65627e
commit 0efc4c6794
8 changed files with 319 additions and 267 deletions

View File

@ -37,9 +37,6 @@
android:key="antiNonFreeDep" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/appcompatibility">
<CheckBoxPreference android:title="@string/showincompat"
android:defaultValue="false" android:summary="@string/showincompat_long"
android:key="showIncompatible" />
<CheckBoxPreference android:title="@string/rooted"
android:defaultValue="true" android:summary="@string/rooted_long"
android:key="rooted" />

View File

@ -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<DB.App> 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) {

View File

@ -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;
}
}

View File

@ -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;
@ -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<App> {
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<App> apps = getApps(null, null, false, true);
Vector<App> 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,95 +504,36 @@ 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<App> getApps(String appid, String filter, boolean update,
boolean exclusions) {
public Vector<App> 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<App> result = new Vector<App>();
Map<String, App> apps = new HashMap<String, App>();
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;
}
}
}
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.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.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"));
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);
@ -603,58 +544,45 @@ public class DB {
.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,30 +592,22 @@ 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<App> result = new Vector<App>(apps.values());
Collections.sort(result);
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(compatChecker);
Apk curver = app.getCurrentVersion();
if (curver != null && app.installedVersion != null
&& !app.installedVersion.equals(curver.version)) {
if (app.installedVerCode < curver.vercode) {
@ -696,12 +616,13 @@ public class DB {
}
}
}
}
return result;
}
// Verify installed status against the system's package list.
private void getUpdates(Vector<DB.App> apps) {
// Get installation status for all apps.
private void getInstalledInfo(Vector<DB.App> apps) {
List<PackageInfo> installedPackages = mPm.getInstalledPackages(0);
Map<String, PackageInfo> systemApks = new HashMap<String, PackageInfo>();
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.installedVerCode = 0;
}
}
}
@ -760,21 +673,27 @@ public class DB {
private Vector<App> 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<DB.App> 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<Apk> compatibleapks = new Vector<Apk>();
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;
}

View File

@ -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,9 +401,15 @@ 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 {
if (!filtered)
apps_in.addItem(app);
if (app.hasUpdates)
apps_up.addItem(app);
@ -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) {

View File

@ -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 {
@ -26,9 +29,59 @@ public class FDroidApp extends Application {
public void onCreate() {
super.onCreate();
apps = null;
DB.initDB(getApplicationContext());
}
// Global list of all known applications.
private Vector<DB.App> 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<DB.App> 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<DB.App>();
return apps;
}
}

View File

@ -67,13 +67,18 @@ public class SearchResults extends ListActivity {
}
private void updateView() {
Vector<DB.App> apps;
try {
DB db = DB.getDB();
apps = db.getApps(null, mQuery, false, true);
} finally {
DB.releaseDB();
Vector<DB.App> apps= new Vector<DB.App>();
AppFilter appfilter = new AppFilter(this);
String mq = mQuery.toLowerCase();
Vector<DB.App> 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)

View File

@ -102,16 +102,7 @@ public class UpdateService extends IntentService {
int newUpdates = 0;
Vector<DB.Repo> 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<DB.App> 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) {