Merge branch 'compatcheck'

This commit is contained in:
Henrik Tunedal 2011-03-09 00:22:31 +01:00
commit ce3e4bd34f
6 changed files with 240 additions and 74 deletions

View File

@ -107,6 +107,7 @@
<string name="details_notinstalled">Not installed (%d available)</string> <string name="details_notinstalled">Not installed (%d available)</string>
<string name="inst">Installed</string> <string name="inst">Installed</string>
<string name="corrupt_download">Downloaded file is corrupt</string> <string name="corrupt_download">Downloaded file is corrupt</string>
<string name="download_cancelled">Download cancelled</string>
@ -128,4 +129,10 @@
<string name="db_sync_mode">Database sync mode</string> <string name="db_sync_mode">Database sync mode</string>
<string name="db_sync_mode_long">Set the value of SQLite\'s "synchronous" flag</string> <string name="db_sync_mode_long">Set the value of SQLite\'s "synchronous" flag</string>
<string name="appcompatibility">Application compatibility</string>
<string name="showincompat">Incompatible apps</string>
<string name="showincompat_long">Show apps written for newer Android versions or different hardware</string>
<string name="rooted">Root</string>
<string name="rooted_long">Show apps that require root privileges</string>
</resources> </resources>

View File

@ -33,6 +33,14 @@
android:defaultValue="false" android:summary="@string/antinonfreenetlong" android:defaultValue="false" android:summary="@string/antinonfreenetlong"
android:key="antiNonFreeNet" /> android:key="antiNonFreeNet" />
</PreferenceCategory> </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" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/maintenance"> <PreferenceCategory android:title="@string/maintenance">
<Preference android:title="@string/reset" android:summary="@string/clear_all_cached_data" <Preference android:title="@string/reset" android:summary="@string/clear_all_cached_data"
android:key="reset" /> android:key="reset" />

View File

@ -122,6 +122,12 @@ public class AppDetails extends ListActivity {
} else { } else {
buildtype.setText("bin"); buildtype.setText("bin");
} }
if (!compatChecker.isCompatible(apk)) {
View[] views = { v, version, status, size, buildtype };
for (View view : views) {
view.setEnabled(false);
}
}
return v; return v;
} }
} }
@ -152,6 +158,8 @@ public class AppDetails extends ListActivity {
private String appid; private String appid;
private PackageManager mPm; private PackageManager mPm;
private ProgressDialog pd; private ProgressDialog pd;
private DB.Apk.CompatibilityChecker compatChecker;
private volatile boolean cancelDownload;
private Context mctx = this; private Context mctx = this;
@ -192,6 +200,7 @@ public class AppDetails extends ListActivity {
pref_cacheDownloaded = prefs.getBoolean("cacheDownloaded", false); pref_cacheDownloaded = prefs.getBoolean("cacheDownloaded", false);
pref_expert = prefs.getBoolean("expert", false); pref_expert = prefs.getBoolean("expert", false);
viewResetRequired = true; viewResetRequired = true;
compatChecker = DB.Apk.CompatibilityChecker.getChecker(this);
} }
@ -218,7 +227,7 @@ public class AppDetails extends ListActivity {
Log.d("FDroid", "Getting application details for " + appid); Log.d("FDroid", "Getting application details for " + appid);
app = db.getApps(appid, null, true).get(0); app = db.getApps(appid, null, true).get(0);
DB.Apk curver = app.getCurrentVersion(); DB.Apk curver = app.getCurrentVersion(compatChecker);
app_currentvercode = curver == null ? 0 : curver.vercode; app_currentvercode = curver == null ? 0 : curver.vercode;
// Get the signature of the installed package... // Get the signature of the installed package...
@ -287,59 +296,13 @@ public class AppDetails extends ListActivity {
@Override @Override
protected void onListItemClick(ListView l, View v, int position, long id) { protected void onListItemClick(ListView l, View v, int position, long id) {
// Create alert dialog...
final AlertDialog p = new AlertDialog.Builder(this).create();
curapk = app.apks.get(position); curapk = app.apks.get(position);
if (app.installedVersion != null
// Set the title and icon... && app.installedVersion.equals(curapk.version)) {
String icon_path = DB.getIconsPath() + app.icon; removeApk(app.id);
File test_icon = new File(icon_path); } else if (compatChecker.isCompatible(curapk)) {
if (test_icon.exists()) {
p.setIcon(new BitmapDrawable(icon_path));
} else {
p.setIcon(android.R.drawable.sym_def_app_icon);
}
p.setTitle(app.name + " " + curapk.version);
boolean caninstall = true;
String installed = getString(R.string.no);
if (app.installedVersion != null) {
if (app.installedVersion.equals(curapk.version)) {
installed = getString(R.string.yes);
caninstall = false;
} else {
installed = app.installedVersion;
}
}
p.setMessage(getString(R.string.isinst) + " " + installed);
if (caninstall) {
p.setButton(getString(R.string.install),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
p.dismiss();
install(); install();
} }
});
} else {
p.setButton(getString(R.string.uninstall),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
p.dismiss();
removeApk(app.id);
}
});
}
p.setButton2(getString(R.string.cancel),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
return;
}
});
p.show();
} }
@Override @Override
@ -347,7 +310,7 @@ public class AppDetails extends ListActivity {
super.onCreateOptionsMenu(menu); super.onCreateOptionsMenu(menu);
menu.clear(); menu.clear();
DB.Apk curver = app.getCurrentVersion(); DB.Apk curver = app.getCurrentVersion(compatChecker);
if (app.installedVersion != null && curver != null if (app.installedVersion != null && curver != null
&& !app.installedVersion.equals(curver.version)) { && !app.installedVersion.equals(curver.version)) {
menu.add(Menu.NONE, INSTALL, 0, R.string.menu_update).setIcon( menu.add(Menu.NONE, INSTALL, 0, R.string.menu_update).setIcon(
@ -391,7 +354,7 @@ public class AppDetails extends ListActivity {
case INSTALL: case INSTALL:
// Note that this handles updating as well as installing. // Note that this handles updating as well as installing.
curapk = app.getCurrentVersion(); curapk = app.getCurrentVersion(compatChecker);
if (curapk != null) if (curapk != null)
install(); install();
return true; return true;
@ -435,7 +398,8 @@ public class AppDetails extends ListActivity {
&& !curapk.sig.equals(mInstalledSigID)) { && !curapk.sig.equals(mInstalledSigID)) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.SignatureMismatch).setPositiveButton( builder.setMessage(R.string.SignatureMismatch).setPositiveButton(
"Ok", new DialogInterface.OnClickListener() { getString(R.string.ok),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
dialog.cancel(); dialog.cancel();
} }
@ -445,9 +409,24 @@ public class AppDetails extends ListActivity {
return; return;
} }
cancelDownload = false;
pd = new ProgressDialog(this); pd = new ProgressDialog(this);
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setMessage(getString(R.string.download_server)); pd.setMessage(getString(R.string.download_server));
pd.setCancelable(true);
pd.setOnCancelListener(
new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
cancelDownload = true;
}
});
pd.setButton(getString(R.string.cancel),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
pd.show(); pd.show();
new Thread() { new Thread() {
@ -515,6 +494,10 @@ public class AppDetails extends ListActivity {
int totalRead = 0; int totalRead = 0;
int bytesRead = getit.read(data, 0, 1024); int bytesRead = getit.read(data, 0, 1024);
while (bytesRead != -1) { while (bytesRead != -1) {
if (cancelDownload) {
Log.d("FDroid", "Download cancelled!");
break;
}
bout.write(data, 0, bytesRead); bout.write(data, 0, bytesRead);
totalRead += bytesRead; totalRead += bytesRead;
msg = new Message(); msg = new Message();
@ -526,6 +509,12 @@ public class AppDetails extends ListActivity {
getit.close(); getit.close();
saveit.close(); saveit.close();
f = new File(localfile); f = new File(localfile);
if (cancelDownload) {
f.delete();
msg = download_cancelled_handler.obtainMessage();
msg.sendToTarget();
return;
}
Md5Handler hash = new Md5Handler(); Md5Handler hash = new Md5Handler();
String calcedhash = hash.md5Calc(f); String calcedhash = hash.md5Calc(f);
if (curapk.hash.equalsIgnoreCase(calcedhash)) { if (curapk.hash.equalsIgnoreCase(calcedhash)) {
@ -599,6 +588,14 @@ public class AppDetails extends ListActivity {
} }
}; };
private Handler download_cancelled_handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Toast.makeText(mctx, getString(R.string.download_cancelled),
Toast.LENGTH_SHORT).show();
}
};
private void removeApk(String id) { private void removeApk(String id) {
PackageInfo pkginfo; PackageInfo pkginfo;
try { try {

View File

@ -20,6 +20,8 @@
package org.fdroid.fdroid; package org.fdroid.fdroid;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Vector; import java.util.Vector;
@ -27,13 +29,16 @@ import java.util.Vector;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log; import android.util.Log;
import android.text.TextUtils.SimpleStringSplitter;
public class DB { public class DB {
@ -70,6 +75,7 @@ public class DB {
donateURL = null; donateURL = null;
webURL = ""; webURL = "";
antiFeatures = null; antiFeatures = null;
requirements = null;
hasUpdates = false; hasUpdates = false;
updated = false; updated = false;
apks = new Vector<Apk>(); apks = new Vector<Apk>();
@ -90,9 +96,13 @@ public class DB {
public String marketVersion; public String marketVersion;
public int marketVercode; public int marketVercode;
// Comma-separated list of anti-features (as defined in the metadata // List of anti-features (as defined in the metadata
// documentation) or null if there aren't any. // documentation) or null if there aren't any.
public String antiFeatures; public CommaSeparatedList antiFeatures;
// List of special requirements (such as root privileges) or
// null if there aren't any.
public CommaSeparatedList requirements;
// True if there are new versions (apks) that the user hasn't // True if there are new versions (apks) that the user hasn't
// explicitly ignored. (We're currently not using the database // explicitly ignored. (We're currently not using the database
@ -109,22 +119,25 @@ public class DB {
// This should be the 'current' version, as in the most recent stable // 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 // one, that most users would want by default. It might not be the
// most recent, if for example there are betas etc. // most recent, if for example there are betas etc.
public Apk getCurrentVersion() { // To skip compatibility checks, pass null as the checker.
public Apk getCurrentVersion(DB.Apk.CompatibilityChecker checker) {
// Try and return the version that's in Google's market first... // Try and return the version that's in Google's market first...
if (marketVersion != null && marketVercode > 0) { if (marketVersion != null && marketVercode > 0) {
for (Apk apk : apks) { for (Apk apk : apks) {
if (apk.vercode == marketVercode) if (apk.vercode == marketVercode
&& (checker == null || checker.isCompatible(apk)))
return apk; return apk;
} }
} }
// If we don't know the market version, or we don't have it, we // If we don't know the market version, or we don't have it, we
// return the most recent version we have... // return the most recent compatible version we have...
int latestcode = -1; int latestcode = -1;
Apk latestapk = null; Apk latestapk = null;
for (Apk apk : apks) { for (Apk apk : apks) {
if (apk.vercode > latestcode) { if (apk.vercode > latestcode
&& (checker == null || checker.isCompatible(apk))) {
latestapk = apk; latestapk = apk;
latestcode = apk.vercode; latestcode = apk.vercode;
} }
@ -158,6 +171,9 @@ public class DB {
public int size; // Size in bytes - 0 means we don't know! public int size; // Size in bytes - 0 means we don't know!
public String server; public String server;
public String hash; public String hash;
public int minSdkVersion; // 0 if unknown
public CommaSeparatedList permissions; // null if empty or unknown
public CommaSeparatedList features; // null if empty or unknown
// ID (md5 sum of public key) of signature. Might be null, in the // ID (md5 sum of public key) of signature. Might be null, in the
// transition to this field existing. // transition to this field existing.
@ -181,6 +197,58 @@ public class DB {
String path = apkName.replace(" ", "%20"); String path = apkName.replace(" ", "%20");
return server + "/" + path; return server + "/" + path;
} }
// Call isCompatible(apk) on an instance of this class to
// check if an APK is compatible with the user's device.
public static abstract class CompatibilityChecker {
// Because Build.VERSION.SDK_INT requires API level 5
protected final static int SDK_INT
= Integer.parseInt(Build.VERSION.SDK);
public abstract boolean isCompatible(Apk apk);
public static CompatibilityChecker getChecker(Context ctx) {
CompatibilityChecker checker;
if (SDK_INT >= 5)
checker = new EclairChecker(ctx);
else
checker = new BasicChecker();
Log.d("FDroid", "Compatibility checker for API level "
+ SDK_INT + ": " + checker.getClass().getName());
return checker;
}
}
private static class BasicChecker extends CompatibilityChecker {
public boolean isCompatible(Apk apk) {
return (apk.minSdkVersion <= SDK_INT);
}
}
private static class EclairChecker extends CompatibilityChecker {
private HashSet<String> features;
public EclairChecker(Context ctx) {
PackageManager pm = ctx.getPackageManager();
features = new HashSet<String>();
for (FeatureInfo fi : pm.getSystemAvailableFeatures()) {
features.add(fi.name);
}
}
public boolean isCompatible(Apk apk) {
if (apk.minSdkVersion > SDK_INT)
return false;
if (apk.features != null) {
for (String feat : apk.features) {
if (!features.contains(feat)) return false;
}
}
return true;
}
}
} }
// The TABLE_REPO table stores the details of the repositories in use. // The TABLE_REPO table stores the details of the repositories in use.
@ -236,7 +304,15 @@ public class DB {
{ "alter table " + TABLE_APP + " add donateURL string" }, { "alter table " + TABLE_APP + " add donateURL string" },
// Version 9... // Version 9...
{ "alter table " + TABLE_APK + " add srcname string" } }; { "alter table " + TABLE_APK + " add srcname string" },
// Version 10...
{ "alter table " + TABLE_APK + " add minSdkVersion integer",
"alter table " + TABLE_APK + " add permissions string",
"alter table " + TABLE_APK + " add features string" },
// Version 11...
{ "alter table " + TABLE_APP + " add requirements string" }};
private class DBHelper extends SQLiteOpenHelper { private class DBHelper extends SQLiteOpenHelper {
@ -275,6 +351,7 @@ public class DB {
private PackageManager mPm; private PackageManager mPm;
private Context mContext; private Context mContext;
private Apk.CompatibilityChecker compatChecker;
public DB(Context ctx) { public DB(Context ctx) {
@ -295,6 +372,7 @@ public class DB {
sync_mode = null; sync_mode = null;
if (sync_mode != null) if (sync_mode != null)
Log.d("FDroid", "Database synchronization mode: " + sync_mode); Log.d("FDroid", "Database synchronization mode: " + sync_mode);
compatChecker = Apk.CompatibilityChecker.getChecker(ctx);
} }
public void close() { public void close() {
@ -326,8 +404,9 @@ public class DB {
return count; return count;
} }
// Return a list of apps matching the given criteria. Filtering is also // Return a list of apps matching the given criteria. Filtering is
// done based on the user's current anti-features preferences. // also done based on compatibility and anti-features according to
// the user's current preferences.
// 'appid' - specific app id to retrieve, or null // 'appid' - specific app id to retrieve, or null
// 'filter' - search text to filter on, or null // 'filter' - search text to filter on, or null
// 'update' - update installed version information from device, rather than // 'update' - update installed version information from device, rather than
@ -340,6 +419,8 @@ public class DB {
boolean pref_antiTracking = prefs.getBoolean("antiTracking", false); boolean pref_antiTracking = prefs.getBoolean("antiTracking", false);
boolean pref_antiNonFreeAdd = prefs.getBoolean("antiNonFreeAdd", false); boolean pref_antiNonFreeAdd = prefs.getBoolean("antiNonFreeAdd", false);
boolean pref_antiNonFreeNet = prefs.getBoolean("antiNonFreeNet", false); boolean pref_antiNonFreeNet = prefs.getBoolean("antiNonFreeNet", false);
boolean pref_showIncompat = prefs.getBoolean("showIncompatible", false);
boolean pref_rooted = prefs.getBoolean("rooted", true);
Vector<App> result = new Vector<App>(); Vector<App> result = new Vector<App>();
Cursor c = null; Cursor c = null;
@ -360,12 +441,11 @@ public class DB {
while (!c.isAfterLast()) { while (!c.isAfterLast()) {
App app = new App(); App app = new App();
app.antiFeatures = c app.antiFeatures = DB.CommaSeparatedList.make(c
.getString(c.getColumnIndex("antiFeatures")); .getString(c.getColumnIndex("antiFeatures")));
boolean include = true; boolean include = true;
if (app.antiFeatures != null && app.antiFeatures.length() > 0) { if (app.antiFeatures != null) {
String[] afs = app.antiFeatures.split(","); for (String af : app.antiFeatures) {
for (String af : afs) {
if (af.equals("Ads") && !pref_antiAds) if (af.equals("Ads") && !pref_antiAds)
include = false; include = false;
else if (af.equals("Tracking") && !pref_antiTracking) else if (af.equals("Tracking") && !pref_antiTracking)
@ -378,6 +458,15 @@ public class DB {
include = false; include = false;
} }
} }
app.requirements = DB.CommaSeparatedList.make(c
.getString(c.getColumnIndex("requirements")));
if (app.requirements != null) {
for (String r : app.requirements) {
if (r.equals("root") && !pref_rooted) {
include = false;
}
}
}
if (include) { if (include) {
app.id = c.getString(c.getColumnIndex("id")); app.id = c.getString(c.getColumnIndex("id"));
@ -406,6 +495,7 @@ public class DB {
+ " where id = ? order by vercode desc", + " where id = ? order by vercode desc",
new String[] { app.id }); new String[] { app.id });
c2.moveToFirst(); c2.moveToFirst();
boolean compatible = pref_showIncompat;
while (!c2.isAfterLast()) { while (!c2.isAfterLast()) {
Apk apk = new Apk(); Apk apk = new Apk();
apk.id = app.id; apk.id = app.id;
@ -421,13 +511,29 @@ public class DB {
.getString(c2.getColumnIndex("apkName")); .getString(c2.getColumnIndex("apkName"));
apk.apkSource = c2.getString(c2 apk.apkSource = c2.getString(c2
.getColumnIndex("apkSource")); .getColumnIndex("apkSource"));
apk.minSdkVersion = c2.getInt(c2
.getColumnIndex("minSdkVersion"));
apk.permissions = CommaSeparatedList.make(c2
.getString(c2.getColumnIndex("permissions")));
apk.features = CommaSeparatedList.make(c2
.getString(c2.getColumnIndex("features")));
app.apks.add(apk); app.apks.add(apk);
if (!compatible && compatChecker.isCompatible(apk)) {
// At least one compatible APK.
compatible = true;
}
c2.moveToNext(); c2.moveToNext();
} }
c2.close(); c2.close();
if (compatible) {
result.add(app); result.add(app);
} }
else {
Log.d("FDroid", "Excluding incompatible application: "
+ app.id);
}
}
c.moveToNext(); c.moveToNext();
} }
@ -458,7 +564,7 @@ public class DB {
// installed version is not the 'current' one AND the installed // installed version is not the 'current' one AND the installed
// version is older than the current one. // version is older than the current one.
for (App app : result) { for (App app : result) {
Apk curver = app.getCurrentVersion(); Apk curver = app.getCurrentVersion(compatChecker);
if (curver != null && app.installedVersion != null if (curver != null && app.installedVersion != null
&& !app.installedVersion.equals(curver.version)) { && !app.installedVersion.equals(curver.version)) {
if (app.installedVerCode < curver.vercode) if (app.installedVerCode < curver.vercode)
@ -497,6 +603,35 @@ public class DB {
} }
} }
public static class CommaSeparatedList implements Iterable<String> {
private String value;
private CommaSeparatedList(String list) {
value = list;
}
public static CommaSeparatedList make(String list) {
if (list == null || list.length() == 0)
return null;
else
return new CommaSeparatedList(list);
}
public static String str(CommaSeparatedList instance) {
return (instance == null ? null : instance.toString());
}
public String toString() {
return value;
}
public Iterator<String> iterator() {
SimpleStringSplitter splitter = new SimpleStringSplitter(',');
splitter.setString(value);
return splitter.iterator();
}
}
private Vector<App> updateApps = null; private Vector<App> updateApps = null;
// Called before a repo update starts. // Called before a repo update starts.
@ -637,7 +772,8 @@ public class DB {
values.put("installedVerCode", upapp.installedVerCode); values.put("installedVerCode", upapp.installedVerCode);
values.put("marketVersion", upapp.marketVersion); values.put("marketVersion", upapp.marketVersion);
values.put("marketVercode", upapp.marketVercode); values.put("marketVercode", upapp.marketVercode);
values.put("antiFeatures", upapp.antiFeatures); values.put("antiFeatures", CommaSeparatedList.str(upapp.antiFeatures));
values.put("requirements", CommaSeparatedList.str(upapp.requirements));
values.put("hasUpdates", upapp.hasUpdates ? 1 : 0); values.put("hasUpdates", upapp.hasUpdates ? 1 : 0);
if (oldapp != null) { if (oldapp != null) {
db.update(TABLE_APP, values, "id = ?", new String[] { oldapp.id }); db.update(TABLE_APP, values, "id = ?", new String[] { oldapp.id });
@ -664,6 +800,9 @@ public class DB {
values.put("size", upapk.size); values.put("size", upapk.size);
values.put("apkName", upapk.apkName); values.put("apkName", upapk.apkName);
values.put("apkSource", upapk.apkSource); values.put("apkSource", upapk.apkSource);
values.put("minSdkVersion", upapk.minSdkVersion);
values.put("permissions", CommaSeparatedList.str(upapk.permissions));
values.put("features", CommaSeparatedList.str(upapk.features));
if (oldapk != null) { if (oldapk != null) {
db.update(TABLE_APK, values, "id = ? and version =?", new String[] { db.update(TABLE_APK, values, "id = ? and version =?", new String[] {
oldapk.id, oldapk.version }); oldapk.id, oldapk.version });

View File

@ -309,6 +309,7 @@ public class FDroid extends TabActivity implements OnItemClickListener {
apps_av.clear(); apps_av.clear();
apps_up.clear(); apps_up.clear();
long startTime = System.currentTimeMillis();
Vector<DB.App> apps = db.getApps(null, null, update); Vector<DB.App> apps = db.getApps(null, null, update);
if (apps.isEmpty()) { if (apps.isEmpty()) {
// Don't attempt this more than once - we may have invalid // Don't attempt this more than once - we may have invalid
@ -322,7 +323,9 @@ public class FDroid extends TabActivity implements OnItemClickListener {
triedEmptyUpdate = true; triedEmptyUpdate = true;
return; return;
} }
Log.d("FDroid", "Updating lists - " + apps.size() + " apps in total"); Log.d("FDroid", "Updating lists - " + apps.size() + " apps in total"
+ " (update took " + (System.currentTimeMillis() - startTime)
+ " ms)");
for (DB.App app : apps) { for (DB.App app : apps) {
if (app.installedVersion == null) { if (app.installedVersion == null) {

View File

@ -119,6 +119,16 @@ public class RepoXMLHandler extends DefaultHandler {
curapk.apkName = str; curapk.apkName = str;
} else if (curel.equals("apksource")) { } else if (curel.equals("apksource")) {
curapk.apkSource = str; curapk.apkSource = str;
} else if (curel.equals("sdkver")) {
try {
curapk.minSdkVersion = Integer.parseInt(str);
} catch (NumberFormatException ex) {
curapk.minSdkVersion = 0;
}
} else if (curel.equals("permissions")) {
curapk.permissions = DB.CommaSeparatedList.make(str);
} else if (curel.equals("features")) {
curapk.features = DB.CommaSeparatedList.make(str);
} }
} else if (curapp != null && str != null) { } else if (curapp != null && str != null) {
if (curel.equals("id")) { if (curel.equals("id")) {
@ -151,7 +161,9 @@ public class RepoXMLHandler extends DefaultHandler {
curapp.marketVercode = 0; curapp.marketVercode = 0;
} }
} else if (curel.equals("antifeatures")) { } else if (curel.equals("antifeatures")) {
curapp.antiFeatures = str; curapp.antiFeatures = DB.CommaSeparatedList.make(str);
} else if (curel.equals("requirements")) {
curapp.requirements = DB.CommaSeparatedList.make(str);
} }
} }