Merge branch 'compatcheck'
This commit is contained in:
commit
ce3e4bd34f
@ -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>
|
||||||
|
@ -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" />
|
||||||
|
@ -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 {
|
||||||
|
@ -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 });
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user