Refactoring Apk references into content provider.
Removed DB.Apk in favour of stand-alone Apk class. Conflicts: src/org/fdroid/fdroid/DB.java
This commit is contained in:
parent
2a8c570a00
commit
b3773a1561
@ -36,10 +36,15 @@
|
|||||||
android:supportsRtl="false" >
|
android:supportsRtl="false" >
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:authorities="org.fdroid.fdroid.data"
|
android:authorities="org.fdroid.fdroid.data.RepoProvider"
|
||||||
android:name="org.fdroid.fdroid.data.RepoProvider"
|
android:name="org.fdroid.fdroid.data.RepoProvider"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:authorities="org.fdroid.fdroid.data.ApkProvider"
|
||||||
|
android:name="org.fdroid.fdroid.data.ApkProvider"
|
||||||
|
android:exported="false"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".FDroid"
|
android:name=".FDroid"
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize" >
|
android:configChanges="keyboardHidden|orientation|screenSize" >
|
||||||
|
3
TODO
Normal file
3
TODO
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
incompatible_reasons needs to be implemented correctly for the data.Apk class (rather than DB.Apk).
|
||||||
|
Currently, it is set during a CompatabilityChecker call to isCompatible(), which means we don't really
|
||||||
|
know whether the field has been set or not.
|
@ -25,6 +25,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.data.Apk;
|
||||||
import org.fdroid.fdroid.data.Repo;
|
import org.fdroid.fdroid.data.Repo;
|
||||||
import org.fdroid.fdroid.data.RepoProvider;
|
import org.fdroid.fdroid.data.RepoProvider;
|
||||||
import org.xml.sax.XMLReader;
|
import org.xml.sax.XMLReader;
|
||||||
@ -74,13 +75,9 @@ import org.fdroid.fdroid.compat.ActionBarCompat;
|
|||||||
import org.fdroid.fdroid.compat.MenuManager;
|
import org.fdroid.fdroid.compat.MenuManager;
|
||||||
import org.fdroid.fdroid.DB.CommaSeparatedList;
|
import org.fdroid.fdroid.DB.CommaSeparatedList;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
|
|
||||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
|
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
|
||||||
import com.nostra13.universalimageloader.utils.StorageUtils;
|
|
||||||
|
|
||||||
import android.os.Environment;
|
|
||||||
|
|
||||||
public class AppDetails extends ListActivity {
|
public class AppDetails extends ListActivity {
|
||||||
|
|
||||||
@ -99,13 +96,13 @@ public class AppDetails extends ListActivity {
|
|||||||
|
|
||||||
private class ApkListAdapter extends BaseAdapter {
|
private class ApkListAdapter extends BaseAdapter {
|
||||||
|
|
||||||
private List<DB.Apk> items;
|
private List<Apk> items;
|
||||||
private LayoutInflater mInflater;
|
private LayoutInflater mInflater;
|
||||||
|
|
||||||
public ApkListAdapter(Context context, List<DB.Apk> items) {
|
public ApkListAdapter(Context context, List<Apk> items) {
|
||||||
this.items = new ArrayList<DB.Apk>();
|
this.items = new ArrayList<Apk>();
|
||||||
if (items != null) {
|
if (items != null) {
|
||||||
for (DB.Apk apk : items) {
|
for (Apk apk : items) {
|
||||||
this.addItem(apk);
|
this.addItem(apk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,13 +110,13 @@ public class AppDetails extends ListActivity {
|
|||||||
Context.LAYOUT_INFLATER_SERVICE);
|
Context.LAYOUT_INFLATER_SERVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addItem(DB.Apk apk) {
|
public void addItem(Apk apk) {
|
||||||
if (apk.compatible || pref_incompatibleVersions) {
|
if (apk.compatible || pref_incompatibleVersions) {
|
||||||
items.add(apk);
|
items.add(apk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DB.Apk> getItems() {
|
public List<Apk> getItems() {
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +139,7 @@ public class AppDetails extends ListActivity {
|
|||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
|
||||||
java.text.DateFormat df = DateFormat.getDateFormat(mctx);
|
java.text.DateFormat df = DateFormat.getDateFormat(mctx);
|
||||||
DB.Apk apk = items.get(position);
|
Apk apk = items.get(position);
|
||||||
ViewHolder holder;
|
ViewHolder holder;
|
||||||
|
|
||||||
if (convertView == null) {
|
if (convertView == null) {
|
||||||
@ -983,7 +980,7 @@ public class AppDetails extends ListActivity {
|
|||||||
private boolean updating;
|
private boolean updating;
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
public DownloadHandler(DB.Apk apk, String repoaddress, File destdir) {
|
public DownloadHandler(Apk apk, String repoaddress, File destdir) {
|
||||||
id = apk.id;
|
id = apk.id;
|
||||||
download = new Downloader(apk, repoaddress, destdir);
|
download = new Downloader(apk, repoaddress, destdir);
|
||||||
download.start();
|
download.start();
|
||||||
|
@ -21,16 +21,17 @@ package org.fdroid.fdroid;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
import java.security.cert.CertificateEncodingException;
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Formatter;
|
import java.util.Formatter;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -55,8 +56,7 @@ import android.util.Log;
|
|||||||
import org.fdroid.fdroid.compat.Compatibility;
|
import org.fdroid.fdroid.compat.Compatibility;
|
||||||
import org.fdroid.fdroid.compat.ContextCompat;
|
import org.fdroid.fdroid.compat.ContextCompat;
|
||||||
import org.fdroid.fdroid.compat.SupportedArchitectures;
|
import org.fdroid.fdroid.compat.SupportedArchitectures;
|
||||||
import org.fdroid.fdroid.data.DBHelper;
|
import org.fdroid.fdroid.data.*;
|
||||||
import org.fdroid.fdroid.data.Repo;
|
|
||||||
|
|
||||||
public class DB {
|
public class DB {
|
||||||
|
|
||||||
@ -270,155 +270,6 @@ public class DB {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The TABLE_APK table stores details of all the application versions we
|
|
||||||
// know about. Each relates directly back to an entry in TABLE_APP.
|
|
||||||
// This information is retrieved from the repositories.
|
|
||||||
public static final String TABLE_APK = "fdroid_apk";
|
|
||||||
|
|
||||||
public static class Apk {
|
|
||||||
|
|
||||||
public Apk() {
|
|
||||||
updated = false;
|
|
||||||
detail_size = 0;
|
|
||||||
added = null;
|
|
||||||
repo = 0;
|
|
||||||
detail_hash = null;
|
|
||||||
detail_hashType = null;
|
|
||||||
detail_permissions = null;
|
|
||||||
compatible = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String id;
|
|
||||||
public String version;
|
|
||||||
public int vercode;
|
|
||||||
public int detail_size; // Size in bytes - 0 means we don't know!
|
|
||||||
public long repo; // ID of the repo it comes from
|
|
||||||
public String detail_hash;
|
|
||||||
public String detail_hashType;
|
|
||||||
public int minSdkVersion; // 0 if unknown
|
|
||||||
public Date added;
|
|
||||||
public CommaSeparatedList detail_permissions; // null if empty or
|
|
||||||
// unknown
|
|
||||||
public CommaSeparatedList features; // null if empty or unknown
|
|
||||||
|
|
||||||
public CommaSeparatedList nativecode; // null if empty or unknown
|
|
||||||
|
|
||||||
public CommaSeparatedList incompatible_reasons; // null if empty or
|
|
||||||
// unknown
|
|
||||||
// ID (md5 sum of public key) of signature. Might be null, in the
|
|
||||||
// transition to this field existing.
|
|
||||||
public String sig;
|
|
||||||
|
|
||||||
// True if compatible with the device.
|
|
||||||
public boolean compatible;
|
|
||||||
|
|
||||||
public String apkName;
|
|
||||||
|
|
||||||
// If not null, this is the name of the source tarball for the
|
|
||||||
// application. Null indicates that it's a developer's binary
|
|
||||||
// build - otherwise it's built from source.
|
|
||||||
public String srcname;
|
|
||||||
|
|
||||||
// Used internally for tracking during repo updates.
|
|
||||||
public boolean updated;
|
|
||||||
|
|
||||||
// Call isCompatible(apk) on an instance of this class to
|
|
||||||
// check if an APK is compatible with the user's device.
|
|
||||||
private static class CompatibilityChecker extends Compatibility {
|
|
||||||
|
|
||||||
private Set<String> features;
|
|
||||||
private Set<String> cpuAbis;
|
|
||||||
private String cpuAbisDesc;
|
|
||||||
private boolean ignoreTouchscreen;
|
|
||||||
|
|
||||||
public CompatibilityChecker(Context ctx) {
|
|
||||||
|
|
||||||
SharedPreferences prefs = PreferenceManager
|
|
||||||
.getDefaultSharedPreferences(ctx);
|
|
||||||
ignoreTouchscreen = prefs
|
|
||||||
.getBoolean(Preferences.PREF_IGN_TOUCH, false);
|
|
||||||
|
|
||||||
PackageManager pm = ctx.getPackageManager();
|
|
||||||
StringBuilder logMsg = new StringBuilder();
|
|
||||||
logMsg.append("Available device features:");
|
|
||||||
features = new HashSet<String>();
|
|
||||||
if (pm != null) {
|
|
||||||
for (FeatureInfo fi : pm.getSystemAvailableFeatures()) {
|
|
||||||
features.add(fi.name);
|
|
||||||
logMsg.append('\n');
|
|
||||||
logMsg.append(fi.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuAbis = SupportedArchitectures.getAbis();
|
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
boolean first = true;
|
|
||||||
for (String abi : cpuAbis) {
|
|
||||||
if (first) first = false;
|
|
||||||
else builder.append(", ");
|
|
||||||
builder.append(abi);
|
|
||||||
}
|
|
||||||
cpuAbisDesc = builder.toString();
|
|
||||||
builder = null;
|
|
||||||
|
|
||||||
Log.d("FDroid", logMsg.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean compatibleApi(CommaSeparatedList nativecode) {
|
|
||||||
if (nativecode == null) return true;
|
|
||||||
for (String abi : nativecode) {
|
|
||||||
if (cpuAbis.contains(abi)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCompatible(Apk apk) {
|
|
||||||
if (!hasApi(apk.minSdkVersion)) {
|
|
||||||
apk.incompatible_reasons = CommaSeparatedList.make(String.valueOf(apk.minSdkVersion));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (apk.features != null) {
|
|
||||||
for (String feat : apk.features) {
|
|
||||||
if (ignoreTouchscreen
|
|
||||||
&& feat.equals("android.hardware.touchscreen")) {
|
|
||||||
// Don't check it!
|
|
||||||
} else if (!features.contains(feat)) {
|
|
||||||
apk.incompatible_reasons = CommaSeparatedList.make(feat);
|
|
||||||
Log.d("FDroid", apk.id + " vercode " + apk.vercode
|
|
||||||
+ " is incompatible based on lack of "
|
|
||||||
+ feat);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!compatibleApi(apk.nativecode)) {
|
|
||||||
apk.incompatible_reasons = apk.nativecode;
|
|
||||||
Log.d("FDroid", apk.id + " vercode " + apk.vercode
|
|
||||||
+ " only supports " + CommaSeparatedList.str(apk.nativecode)
|
|
||||||
+ " while your architectures are " + cpuAbisDesc);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int countAppsForRepo(long id) {
|
|
||||||
String[] selection = { "COUNT(distinct id)" };
|
|
||||||
String[] selectionArgs = { Long.toString(id) };
|
|
||||||
Cursor result = db.query(
|
|
||||||
TABLE_APK, selection, "repo = ?", selectionArgs, "repo", null, null);
|
|
||||||
if (result.getCount() > 0) {
|
|
||||||
result.moveToFirst();
|
|
||||||
return result.getInt(0);
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String calcFingerprint(String keyHexString) {
|
public static String calcFingerprint(String keyHexString) {
|
||||||
if (TextUtils.isEmpty(keyHexString))
|
if (TextUtils.isEmpty(keyHexString))
|
||||||
return null;
|
return null;
|
||||||
@ -569,33 +420,21 @@ public class DB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String[] POPULATE_APK_COLS = new String[] { "hash", "hashType", "size", "permissions" };
|
private static final String[] POPULATE_APK_COLS = new String[] {
|
||||||
|
ApkProvider.DataColumns.HASH,
|
||||||
|
ApkProvider.DataColumns.HASH_TYPE,
|
||||||
|
ApkProvider.DataColumns.SIZE,
|
||||||
|
ApkProvider.DataColumns.PERMISSIONS
|
||||||
|
};
|
||||||
|
|
||||||
private void populateApkDetails(Apk apk, long repo) {
|
private void populateApkDetails(Apk apk, long repo) {
|
||||||
if (repo == 0 || repo == apk.repo) {
|
if (repo == 0 || repo == apk.repo) {
|
||||||
Cursor cursor = null;
|
Apk loadedApk = ApkProvider.Helper.find(
|
||||||
try {
|
mContext, apk.id, apk.vercode, POPULATE_APK_COLS);
|
||||||
cursor = db.query(
|
apk.detail_hash = loadedApk.detail_hash;
|
||||||
TABLE_APK,
|
apk.detail_hashType = loadedApk.detail_hashType;
|
||||||
POPULATE_APK_COLS,
|
apk.detail_size = loadedApk.detail_size;
|
||||||
"id = ? and vercode = ?",
|
apk.detail_permissions = loadedApk.detail_permissions;
|
||||||
new String[] { apk.id,
|
|
||||||
Integer.toString(apk.vercode) }, null,
|
|
||||||
null, null, null);
|
|
||||||
cursor.moveToFirst();
|
|
||||||
apk.detail_hash = cursor.getString(0);
|
|
||||||
apk.detail_hashType = cursor.getString(1);
|
|
||||||
apk.detail_size = cursor.getInt(2);
|
|
||||||
apk.detail_permissions = CommaSeparatedList.make(cursor
|
|
||||||
.getString(3));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.d("FDroid", "Error populating apk details for " + apk.id + " (version " + apk.version + ")");
|
|
||||||
Log.d("FDroid", e.getMessage());
|
|
||||||
} finally {
|
|
||||||
if (cursor != null) {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Log.d("FDroid", "Not setting details for apk '" + apk.id + "' (version " + apk.version +") because it belongs to a different repo.");
|
Log.d("FDroid", "Not setting details for apk '" + apk.id + "' (version " + apk.version +") because it belongs to a different repo.");
|
||||||
}
|
}
|
||||||
@ -695,18 +534,6 @@ public class DB {
|
|||||||
Log.d("FDroid", "Read app data from database " + " (took "
|
Log.d("FDroid", "Read app data from database " + " (took "
|
||||||
+ (System.currentTimeMillis() - startTime) + " ms)");
|
+ (System.currentTimeMillis() - startTime) + " ms)");
|
||||||
|
|
||||||
String query = "SELECT apk.id, apk.version, apk.vercode, apk.sig,"
|
|
||||||
+ " apk.srcname, apk.apkName, apk.minSdkVersion, "
|
|
||||||
+ " apk.added, apk.features, apk.nativecode, "
|
|
||||||
+ " apk.compatible, apk.repo, repo.version, repo.address "
|
|
||||||
+ " FROM " + TABLE_APK + " as apk "
|
|
||||||
+ " LEFT JOIN " + DBHelper.TABLE_REPO + " as repo "
|
|
||||||
+ " ON repo._id = apk.repo "
|
|
||||||
+ " ORDER BY apk.vercode DESC";
|
|
||||||
|
|
||||||
c = db.rawQuery(query, null);
|
|
||||||
c.moveToFirst();
|
|
||||||
|
|
||||||
DisplayMetrics metrics = mContext.getResources()
|
DisplayMetrics metrics = mContext.getResources()
|
||||||
.getDisplayMetrics();
|
.getDisplayMetrics();
|
||||||
String iconsDir = null;
|
String iconsDir = null;
|
||||||
@ -726,43 +553,21 @@ public class DB {
|
|||||||
metrics = null;
|
metrics = null;
|
||||||
Log.d("FDroid", "Density-specific icons dir is " + iconsDir);
|
Log.d("FDroid", "Density-specific icons dir is " + iconsDir);
|
||||||
|
|
||||||
while (!c.isAfterLast()) {
|
List<Apk> apks = ApkProvider.Helper.all(mContext);
|
||||||
String id = c.getString(0);
|
for (Apk apk : apks) {
|
||||||
App app = apps.get(id);
|
App app = apps.get(apk.id);
|
||||||
if (app == null) {
|
if (app == null) {
|
||||||
c.moveToNext();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
boolean compatible = c.getInt(10) == 1;
|
|
||||||
int repoid = c.getInt(11);
|
|
||||||
Apk apk = new Apk();
|
|
||||||
apk.id = id;
|
|
||||||
apk.version = c.getString(1);
|
|
||||||
apk.vercode = c.getInt(2);
|
|
||||||
apk.sig = c.getString(3);
|
|
||||||
apk.srcname = c.getString(4);
|
|
||||||
apk.apkName = c.getString(5);
|
|
||||||
apk.minSdkVersion = c.getInt(6);
|
|
||||||
String sApkAdded = c.getString(7);
|
|
||||||
apk.added = (sApkAdded == null || sApkAdded.length() == 0) ? null
|
|
||||||
: DATE_FORMAT.parse(sApkAdded);
|
|
||||||
apk.features = CommaSeparatedList.make(c.getString(8));
|
|
||||||
apk.nativecode = CommaSeparatedList.make(c.getString(9));
|
|
||||||
apk.compatible = compatible;
|
|
||||||
apk.repo = repoid;
|
|
||||||
app.apks.add(apk);
|
app.apks.add(apk);
|
||||||
if (app.iconUrl == null && app.icon != null) {
|
if (app.iconUrl == null && app.icon != null) {
|
||||||
int repoVersion = c.getInt(12);
|
if (apk.repoVersion >= 11) {
|
||||||
String repoAddress = c.getString(13);
|
app.iconUrl = apk.repoAddress + iconsDir + app.icon;
|
||||||
if (repoVersion >= 11) {
|
|
||||||
app.iconUrl = repoAddress + iconsDir + app.icon;
|
|
||||||
} else {
|
} else {
|
||||||
app.iconUrl = repoAddress + "/icons/" + app.icon;
|
app.iconUrl = apk.repoAddress + "/icons/" + app.icon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.moveToNext();
|
|
||||||
}
|
}
|
||||||
c.close();
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e("FDroid",
|
Log.e("FDroid",
|
||||||
@ -948,8 +753,8 @@ public class DB {
|
|||||||
// in the repos.
|
// in the repos.
|
||||||
Log.d("FDroid", "AppUpdate: " + app.name
|
Log.d("FDroid", "AppUpdate: " + app.name
|
||||||
+ " is no longer in any repository - removing");
|
+ " is no longer in any repository - removing");
|
||||||
db.delete(TABLE_APP, "id = ?", new String[] { app.id });
|
db.delete(TABLE_APP, "id = ?", new String[]{app.id});
|
||||||
db.delete(TABLE_APK, "id = ?", new String[] { app.id });
|
ApkProvider.Helper.deleteApksByApp(mContext, app);
|
||||||
} else {
|
} else {
|
||||||
for (Apk apk : app.apks) {
|
for (Apk apk : app.apks) {
|
||||||
if (!apk.updated) {
|
if (!apk.updated) {
|
||||||
@ -958,8 +763,7 @@ public class DB {
|
|||||||
Log.d("FDroid", "AppUpdate: Package " + apk.id + "/"
|
Log.d("FDroid", "AppUpdate: Package " + apk.id + "/"
|
||||||
+ apk.version
|
+ apk.version
|
||||||
+ " is no longer in any repository - removing");
|
+ " is no longer in any repository - removing");
|
||||||
db.delete(TABLE_APK, "id = ? and version = ?",
|
ApkProvider.Helper.delete(mContext, app.id, apk.vercode);
|
||||||
new String[] { app.id, apk.version });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1014,9 +818,13 @@ public class DB {
|
|||||||
boolean afound = false;
|
boolean afound = false;
|
||||||
for (Apk apk : app.apks) {
|
for (Apk apk : app.apks) {
|
||||||
if (apk.vercode == upapk.vercode) {
|
if (apk.vercode == upapk.vercode) {
|
||||||
// Log.d("FDroid", "AppUpdate: " + apk.version
|
|
||||||
// + " is a known version.");
|
ApkProvider.Helper.update(
|
||||||
updateApkIfDifferent(apk, upapk);
|
mContext,
|
||||||
|
upapk,
|
||||||
|
apk.id,
|
||||||
|
apk.vercode);
|
||||||
|
|
||||||
apk.updated = true;
|
apk.updated = true;
|
||||||
afound = true;
|
afound = true;
|
||||||
break;
|
break;
|
||||||
@ -1024,7 +832,7 @@ public class DB {
|
|||||||
}
|
}
|
||||||
if (!afound) {
|
if (!afound) {
|
||||||
// A new version of this application.
|
// A new version of this application.
|
||||||
updateApkIfDifferent(null, upapk);
|
ApkProvider.Helper.insert(mContext, upapk);
|
||||||
upapk.updated = true;
|
upapk.updated = true;
|
||||||
app.apks.add(upapk);
|
app.apks.add(upapk);
|
||||||
}
|
}
|
||||||
@ -1036,7 +844,7 @@ public class DB {
|
|||||||
// It's a brand new application...
|
// It's a brand new application...
|
||||||
updateApp(null, upapp);
|
updateApp(null, upapp);
|
||||||
for (Apk upapk : upapp.apks) {
|
for (Apk upapk : upapp.apks) {
|
||||||
updateApkIfDifferent(null, upapk);
|
ApkProvider.Helper.insert(mContext, upapk);
|
||||||
upapk.updated = true;
|
upapk.updated = true;
|
||||||
}
|
}
|
||||||
upapp.updated = true;
|
upapp.updated = true;
|
||||||
@ -1095,41 +903,6 @@ public class DB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update apk details in the database, if different to the
|
|
||||||
// previous ones.
|
|
||||||
// 'oldapk' - previous details - i.e. what's in the database.
|
|
||||||
// If null, this apk is not in the database at all and
|
|
||||||
// should be added.
|
|
||||||
// 'upapk' - updated details
|
|
||||||
private void updateApkIfDifferent(Apk oldapk, Apk upapk) {
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put("id", upapk.id);
|
|
||||||
values.put("version", upapk.version);
|
|
||||||
values.put("vercode", upapk.vercode);
|
|
||||||
values.put("repo", upapk.repo);
|
|
||||||
values.put("hash", upapk.detail_hash);
|
|
||||||
values.put("hashType", upapk.detail_hashType);
|
|
||||||
values.put("sig", upapk.sig);
|
|
||||||
values.put("srcname", upapk.srcname);
|
|
||||||
values.put("size", upapk.detail_size);
|
|
||||||
values.put("apkName", upapk.apkName);
|
|
||||||
values.put("minSdkVersion", upapk.minSdkVersion);
|
|
||||||
values.put("added",
|
|
||||||
upapk.added == null ? "" : DATE_FORMAT.format(upapk.added));
|
|
||||||
values.put("permissions",
|
|
||||||
CommaSeparatedList.str(upapk.detail_permissions));
|
|
||||||
values.put("features", CommaSeparatedList.str(upapk.features));
|
|
||||||
values.put("nativecode", CommaSeparatedList.str(upapk.nativecode));
|
|
||||||
values.put("compatible", upapk.compatible ? 1 : 0);
|
|
||||||
if (oldapk != null) {
|
|
||||||
db.update(TABLE_APK, values,
|
|
||||||
"id = ? and vercode = ?",
|
|
||||||
new String[] { oldapk.id, Integer.toString(oldapk.vercode) });
|
|
||||||
} else {
|
|
||||||
db.insert(TABLE_APK, null, values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIgnoreUpdates(String appid, boolean All, int This) {
|
public void setIgnoreUpdates(String appid, boolean All, int This) {
|
||||||
db.execSQL("update " + TABLE_APP + " set"
|
db.execSQL("update " + TABLE_APP + " set"
|
||||||
+ " ignoreAllUpdates=" + (All ? '1' : '0')
|
+ " ignoreAllUpdates=" + (All ? '1' : '0')
|
||||||
@ -1141,7 +914,7 @@ public class DB {
|
|||||||
db.beginTransaction();
|
db.beginTransaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.delete(TABLE_APK, "repo = ?", new String[] { Long.toString(repo.getId()) });
|
ApkProvider.Helper.deleteApksByRepo(mContext, repo);
|
||||||
List<App> apps = getApps(false);
|
List<App> apps = getApps(false);
|
||||||
for (App app : apps) {
|
for (App app : apps) {
|
||||||
if (app.apks.isEmpty()) {
|
if (app.apks.isEmpty()) {
|
||||||
|
@ -27,10 +27,11 @@ import java.io.OutputStream;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import org.fdroid.fdroid.data.Apk;
|
||||||
|
|
||||||
public class Downloader extends Thread {
|
public class Downloader extends Thread {
|
||||||
|
|
||||||
private DB.Apk curapk;
|
private Apk curapk;
|
||||||
private String repoaddress;
|
private String repoaddress;
|
||||||
private String filename;
|
private String filename;
|
||||||
private File destdir;
|
private File destdir;
|
||||||
@ -38,11 +39,11 @@ public class Downloader extends Thread {
|
|||||||
|
|
||||||
public static enum Status {
|
public static enum Status {
|
||||||
STARTING, RUNNING, ERROR, DONE, CANCELLED
|
STARTING, RUNNING, ERROR, DONE, CANCELLED
|
||||||
};
|
}
|
||||||
|
|
||||||
public static enum Error {
|
public static enum Error {
|
||||||
CORRUPT, UNKNOWN
|
CORRUPT, UNKNOWN
|
||||||
};
|
}
|
||||||
|
|
||||||
private Status status = Status.STARTING;
|
private Status status = Status.STARTING;
|
||||||
private Error error;
|
private Error error;
|
||||||
@ -52,7 +53,7 @@ public class Downloader extends Thread {
|
|||||||
|
|
||||||
// Constructor - creates a Downloader to download the given Apk,
|
// Constructor - creates a Downloader to download the given Apk,
|
||||||
// which must have its detail populated.
|
// which must have its detail populated.
|
||||||
Downloader(DB.Apk apk, String repoaddress, File destdir) {
|
Downloader(Apk apk, String repoaddress, File destdir) {
|
||||||
curapk = apk;
|
curapk = apk;
|
||||||
this.repoaddress = repoaddress;
|
this.repoaddress = repoaddress;
|
||||||
this.destdir = destdir;
|
this.destdir = destdir;
|
||||||
@ -91,7 +92,7 @@ public class Downloader extends Thread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The APK being downloaded
|
// The APK being downloaded
|
||||||
public synchronized DB.Apk getApk() {
|
public synchronized Apk getApk() {
|
||||||
return curapk;
|
return curapk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
package org.fdroid.fdroid;
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import org.fdroid.fdroid.data.Apk;
|
||||||
import org.fdroid.fdroid.data.Repo;
|
import org.fdroid.fdroid.data.Repo;
|
||||||
import org.fdroid.fdroid.updater.RepoUpdater;
|
import org.fdroid.fdroid.updater.RepoUpdater;
|
||||||
import org.xml.sax.Attributes;
|
import org.xml.sax.Attributes;
|
||||||
@ -40,7 +41,7 @@ public class RepoXMLHandler extends DefaultHandler {
|
|||||||
private List<DB.App> appsList;
|
private List<DB.App> appsList;
|
||||||
|
|
||||||
private DB.App curapp = null;
|
private DB.App curapp = null;
|
||||||
private DB.Apk curapk = null;
|
private Apk curapk = null;
|
||||||
private StringBuilder curchars = new StringBuilder();
|
private StringBuilder curchars = new StringBuilder();
|
||||||
|
|
||||||
// After processing the XML, these will be -1 if the index didn't specify
|
// After processing the XML, these will be -1 if the index didn't specify
|
||||||
@ -280,7 +281,7 @@ public class RepoXMLHandler extends DefaultHandler {
|
|||||||
totalAppCount, progressData));
|
totalAppCount, progressData));
|
||||||
|
|
||||||
} else if (localName.equals("package") && curapp != null && curapk == null) {
|
} else if (localName.equals("package") && curapp != null && curapk == null) {
|
||||||
curapk = new DB.Apk();
|
curapk = new Apk();
|
||||||
curapk.id = curapp.id;
|
curapk.id = curapp.id;
|
||||||
curapk.repo = repo.getId();
|
curapk.repo = repo.getId();
|
||||||
hashType = null;
|
hashType = null;
|
||||||
|
@ -49,6 +49,7 @@ import android.text.TextUtils;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.data.Apk;
|
||||||
import org.fdroid.fdroid.data.Repo;
|
import org.fdroid.fdroid.data.Repo;
|
||||||
import org.fdroid.fdroid.data.RepoProvider;
|
import org.fdroid.fdroid.data.RepoProvider;
|
||||||
import org.fdroid.fdroid.updater.RepoUpdater;
|
import org.fdroid.fdroid.updater.RepoUpdater;
|
||||||
@ -351,7 +352,7 @@ public class UpdateService extends IntentService implements ProgressListener {
|
|||||||
for (long keep : keeprepos) {
|
for (long keep : keeprepos) {
|
||||||
for (DB.App app : apps) {
|
for (DB.App app : apps) {
|
||||||
boolean keepapp = false;
|
boolean keepapp = false;
|
||||||
for (DB.Apk apk : app.apks) {
|
for (Apk apk : app.apks) {
|
||||||
if (apk.repo == keep) {
|
if (apk.repo == keep) {
|
||||||
keepapp = true;
|
keepapp = true;
|
||||||
break;
|
break;
|
||||||
@ -371,7 +372,7 @@ public class UpdateService extends IntentService implements ProgressListener {
|
|||||||
}
|
}
|
||||||
app_k.updated = true;
|
app_k.updated = true;
|
||||||
db.populateDetails(app_k, keep);
|
db.populateDetails(app_k, keep);
|
||||||
for (DB.Apk apk : app.apks)
|
for (Apk apk : app.apks)
|
||||||
if (apk.repo == keep)
|
if (apk.repo == keep)
|
||||||
apk.updated = true;
|
apk.updated = true;
|
||||||
}
|
}
|
||||||
|
216
src/org/fdroid/fdroid/data/Apk.java
Normal file
216
src/org/fdroid/fdroid/data/Apk.java
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
package org.fdroid.fdroid.data;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.FeatureInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.Log;
|
||||||
|
import org.fdroid.fdroid.DB;
|
||||||
|
import org.fdroid.fdroid.compat.Compatibility;
|
||||||
|
import org.fdroid.fdroid.compat.SupportedArchitectures;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
public class Apk {
|
||||||
|
|
||||||
|
public String id;
|
||||||
|
public String version;
|
||||||
|
public int vercode;
|
||||||
|
public int detail_size; // Size in bytes - 0 means we don't know!
|
||||||
|
public long repo; // ID of the repo it comes from
|
||||||
|
public String detail_hash;
|
||||||
|
public String detail_hashType;
|
||||||
|
public int minSdkVersion; // 0 if unknown
|
||||||
|
public Date added;
|
||||||
|
public DB.CommaSeparatedList detail_permissions; // null if empty or
|
||||||
|
// unknown
|
||||||
|
public DB.CommaSeparatedList features; // null if empty or unknown
|
||||||
|
|
||||||
|
public DB.CommaSeparatedList nativecode; // null if empty or unknown
|
||||||
|
|
||||||
|
// ID (md5 sum of public key) of signature. Might be null, in the
|
||||||
|
// transition to this field existing.
|
||||||
|
public String sig;
|
||||||
|
|
||||||
|
// True if compatible with the device.
|
||||||
|
public boolean compatible;
|
||||||
|
|
||||||
|
public String apkName;
|
||||||
|
|
||||||
|
// If not null, this is the name of the source tarball for the
|
||||||
|
// application. Null indicates that it's a developer's binary
|
||||||
|
// build - otherwise it's built from source.
|
||||||
|
public String srcname;
|
||||||
|
|
||||||
|
// Used internally for tracking during repo updates.
|
||||||
|
public boolean updated;
|
||||||
|
|
||||||
|
public int repoVersion;
|
||||||
|
public String repoAddress;
|
||||||
|
public DB.CommaSeparatedList incompatible_reasons;
|
||||||
|
|
||||||
|
public Apk() {
|
||||||
|
updated = false;
|
||||||
|
detail_size = 0;
|
||||||
|
added = null;
|
||||||
|
repo = 0;
|
||||||
|
detail_hash = null;
|
||||||
|
detail_hashType = null;
|
||||||
|
detail_permissions = null;
|
||||||
|
compatible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Apk(Cursor cursor) {
|
||||||
|
for(int i = 0; i < cursor.getColumnCount(); i ++ ) {
|
||||||
|
String column = cursor.getColumnName(i);
|
||||||
|
if (column.equals(ApkProvider.DataColumns.HASH)) {
|
||||||
|
detail_hash = cursor.getString(i);
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.HASH_TYPE)) {
|
||||||
|
detail_hashType = cursor.getString(i);
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.ADDED_DATE)) {
|
||||||
|
added = ValueObject.toDate(cursor.getString(i));
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.FEATURES)) {
|
||||||
|
features = DB.CommaSeparatedList.make(cursor.getString(i));
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.APK_ID)) {
|
||||||
|
id = cursor.getString(i);
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.IS_COMPATIBLE)) {
|
||||||
|
compatible = cursor.getInt(i) == 1;
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.MIN_SDK_VERSION)) {
|
||||||
|
minSdkVersion = cursor.getInt(i);
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.NAME)) {
|
||||||
|
apkName = cursor.getString(i);
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.PERMISSIONS)) {
|
||||||
|
detail_permissions = DB.CommaSeparatedList.make(cursor.getString(i));
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.NATIVE_CODE)) {
|
||||||
|
nativecode = DB.CommaSeparatedList.make(cursor.getString(i));
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.REPO_ID)) {
|
||||||
|
repo = cursor.getInt(i);
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.SIGNATURE)) {
|
||||||
|
sig = cursor.getString(i);
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.SIZE)) {
|
||||||
|
detail_size = cursor.getInt(i);
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.SOURCE_NAME)) {
|
||||||
|
srcname = cursor.getString(i);
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.VERSION)) {
|
||||||
|
version = cursor.getString(i);
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.VERSION_CODE)) {
|
||||||
|
vercode = cursor.getInt(i);
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.REPO_VERSION)) {
|
||||||
|
repoVersion = cursor.getInt(i);
|
||||||
|
} else if (column.equals(ApkProvider.DataColumns.REPO_ADDRESS)) {
|
||||||
|
repoAddress = cursor.getString(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContentValues toContentValues() {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(ApkProvider.DataColumns.APK_ID, id);
|
||||||
|
values.put(ApkProvider.DataColumns.VERSION, version);
|
||||||
|
values.put(ApkProvider.DataColumns.VERSION_CODE, vercode);
|
||||||
|
values.put(ApkProvider.DataColumns.REPO_ID, repo);
|
||||||
|
values.put(ApkProvider.DataColumns.HASH, detail_hash);
|
||||||
|
values.put(ApkProvider.DataColumns.HASH_TYPE, detail_hashType);
|
||||||
|
values.put(ApkProvider.DataColumns.SIGNATURE, sig);
|
||||||
|
values.put(ApkProvider.DataColumns.SOURCE_NAME, srcname);
|
||||||
|
values.put(ApkProvider.DataColumns.SIZE, detail_size);
|
||||||
|
values.put(ApkProvider.DataColumns.NAME, apkName);
|
||||||
|
values.put(ApkProvider.DataColumns.MIN_SDK_VERSION, minSdkVersion);
|
||||||
|
values.put(ApkProvider.DataColumns.ADDED_DATE,
|
||||||
|
added == null ? "" : DB.DATE_FORMAT.format(added));
|
||||||
|
values.put(ApkProvider.DataColumns.PERMISSIONS,
|
||||||
|
DB.CommaSeparatedList.str(detail_permissions));
|
||||||
|
values.put(ApkProvider.DataColumns.FEATURES, DB.CommaSeparatedList.str(features));
|
||||||
|
values.put(ApkProvider.DataColumns.NATIVE_CODE, DB.CommaSeparatedList.str(nativecode));
|
||||||
|
values.put(ApkProvider.DataColumns.IS_COMPATIBLE, compatible ? 1 : 0);
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call isCompatible(apk) on an instance of this class to
|
||||||
|
// check if an APK is compatible with the user's device.
|
||||||
|
public static class CompatibilityChecker extends Compatibility {
|
||||||
|
|
||||||
|
private Set<String> features;
|
||||||
|
private Set<String> cpuAbis;
|
||||||
|
private String cpuAbisDesc;
|
||||||
|
private boolean ignoreTouchscreen;
|
||||||
|
|
||||||
|
public CompatibilityChecker(Context ctx) {
|
||||||
|
|
||||||
|
SharedPreferences prefs = PreferenceManager
|
||||||
|
.getDefaultSharedPreferences(ctx);
|
||||||
|
ignoreTouchscreen = prefs
|
||||||
|
.getBoolean("ignoreTouchscreen", false);
|
||||||
|
|
||||||
|
PackageManager pm = ctx.getPackageManager();
|
||||||
|
StringBuilder logMsg = new StringBuilder();
|
||||||
|
logMsg.append("Available device features:");
|
||||||
|
features = new HashSet<String>();
|
||||||
|
if (pm != null) {
|
||||||
|
for (FeatureInfo fi : pm.getSystemAvailableFeatures()) {
|
||||||
|
features.add(fi.name);
|
||||||
|
logMsg.append('\n');
|
||||||
|
logMsg.append(fi.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuAbis = SupportedArchitectures.getAbis();
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
boolean first = true;
|
||||||
|
for (String abi : cpuAbis) {
|
||||||
|
if (first) first = false;
|
||||||
|
else builder.append(", ");
|
||||||
|
builder.append(abi);
|
||||||
|
}
|
||||||
|
cpuAbisDesc = builder.toString();
|
||||||
|
builder = null;
|
||||||
|
|
||||||
|
Log.d("FDroid", logMsg.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean compatibleApi(DB.CommaSeparatedList nativecode) {
|
||||||
|
if (nativecode == null) return true;
|
||||||
|
for (String abi : nativecode) {
|
||||||
|
if (cpuAbis.contains(abi)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompatible(Apk apk) {
|
||||||
|
if (!hasApi(apk.minSdkVersion)) {
|
||||||
|
apk.incompatible_reasons = DB.CommaSeparatedList.make(String.valueOf(apk.minSdkVersion));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (apk.features != null) {
|
||||||
|
for (String feat : apk.features) {
|
||||||
|
if (ignoreTouchscreen
|
||||||
|
&& feat.equals("android.hardware.touchscreen")) {
|
||||||
|
// Don't check it!
|
||||||
|
} else if (!features.contains(feat)) {
|
||||||
|
apk.incompatible_reasons = DB.CommaSeparatedList.make(feat);
|
||||||
|
Log.d("FDroid", apk.id + " vercode " + apk.vercode
|
||||||
|
+ " is incompatible based on lack of "
|
||||||
|
+ feat);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!compatibleApi(apk.nativecode)) {
|
||||||
|
apk.incompatible_reasons = apk.nativecode;
|
||||||
|
Log.d("FDroid", apk.id + " vercode " + apk.vercode
|
||||||
|
+ " only supports " + DB.CommaSeparatedList.str(apk.nativecode)
|
||||||
|
+ " while your architectures are " + cpuAbisDesc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
370
src/org/fdroid/fdroid/data/ApkProvider.java
Normal file
370
src/org/fdroid/fdroid/data/ApkProvider.java
Normal file
@ -0,0 +1,370 @@
|
|||||||
|
package org.fdroid.fdroid.data;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.UriMatcher;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
|
import android.util.Log;
|
||||||
|
import org.fdroid.fdroid.DB;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class ApkProvider extends FDroidProvider {
|
||||||
|
|
||||||
|
public static final class Helper {
|
||||||
|
|
||||||
|
private Helper() {}
|
||||||
|
|
||||||
|
public static void update(Context context, Apk apk,
|
||||||
|
String id, int versionCode) {
|
||||||
|
ContentResolver resolver = context.getContentResolver();
|
||||||
|
Uri uri = getContentUri(id, versionCode);
|
||||||
|
resolver.update(uri, apk.toContentValues(), null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void update(Context context, Apk apk) {
|
||||||
|
ContentResolver resolver = context.getContentResolver();
|
||||||
|
Uri uri = getContentUri(apk.id, apk.vercode);
|
||||||
|
resolver.update(uri, apk.toContentValues(), null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This doesn't do anything other than call "insert" on the content
|
||||||
|
* resolver, but I thought I'd put it here in the interests of having
|
||||||
|
* each of the CRUD methods available in the helper class.
|
||||||
|
*/
|
||||||
|
public static void insert(Context context, ContentValues values) {
|
||||||
|
ContentResolver resolver = context.getContentResolver();
|
||||||
|
resolver.insert(getContentUri(), values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void insert(Context context, Apk apk) {
|
||||||
|
insert(context, apk.toContentValues());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Apk> all(Context context) {
|
||||||
|
return all(context, DataColumns.ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Apk> all(Context context, String[] projection) {
|
||||||
|
|
||||||
|
ContentResolver resolver = context.getContentResolver();
|
||||||
|
Uri uri = ApkProvider.getContentUri();
|
||||||
|
Cursor cursor = resolver.query(uri, projection, null, null, null);
|
||||||
|
return cursorToList(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Apk> cursorToList(Cursor cursor) {
|
||||||
|
List<Apk> apks = new ArrayList<Apk>();
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.moveToFirst();
|
||||||
|
while (!cursor.isAfterLast()) {
|
||||||
|
apks.add(new Apk(cursor));
|
||||||
|
cursor.moveToNext();
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return apks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void deleteApksByRepo(Context context, Repo repo) {
|
||||||
|
ContentResolver resolver = context.getContentResolver();
|
||||||
|
Uri uri = getContentUri();
|
||||||
|
String[] args = { Long.toString(repo.getId()) };
|
||||||
|
String selection = DataColumns.REPO_ID + " = ?";
|
||||||
|
resolver.delete(uri, selection + " = ?", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void deleteApksByApp(Context context, DB.App app) {
|
||||||
|
ContentResolver resolver = context.getContentResolver();
|
||||||
|
Uri uri = getContentUri();
|
||||||
|
String[] args = { app.id };
|
||||||
|
String selection = DataColumns.APK_ID + " = ?";
|
||||||
|
resolver.delete(uri, selection, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Apk find(Context context, String id, int versionCode) {
|
||||||
|
return find(context, id, versionCode, DataColumns.ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Apk find(Context context, String id, int versionCode, String[] projection) {
|
||||||
|
ContentResolver resolver = context.getContentResolver();
|
||||||
|
Uri uri = getContentUri(id, versionCode);
|
||||||
|
Cursor cursor = resolver.query(uri, projection, null, null, null);
|
||||||
|
if (cursor != null && cursor.getCount() > 0) {
|
||||||
|
return new Apk(cursor);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void delete(Context context, String id, int versionCode) {
|
||||||
|
ContentResolver resolver = context.getContentResolver();
|
||||||
|
Uri uri = getContentUri(id, versionCode);
|
||||||
|
resolver.delete(uri, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface DataColumns extends BaseColumns {
|
||||||
|
|
||||||
|
public static String APK_ID = "id";
|
||||||
|
public static String VERSION = "version";
|
||||||
|
public static String REPO_ID = "repo";
|
||||||
|
public static String HASH = "hash";
|
||||||
|
public static String VERSION_CODE = "vercode";
|
||||||
|
public static String NAME = "apkName";
|
||||||
|
public static String SIZE = "size";
|
||||||
|
public static String SIGNATURE = "sig";
|
||||||
|
public static String SOURCE_NAME = "srcname";
|
||||||
|
public static String MIN_SDK_VERSION = "minSdkVersion";
|
||||||
|
public static String PERMISSIONS = "permissions";
|
||||||
|
public static String FEATURES = "features";
|
||||||
|
public static String NATIVE_CODE = "nativecode";
|
||||||
|
public static String HASH_TYPE = "hashType";
|
||||||
|
public static String ADDED_DATE = "added";
|
||||||
|
public static String IS_COMPATIBLE = "compatible";
|
||||||
|
public static String REPO_VERSION = "repoVersion";
|
||||||
|
public static String REPO_ADDRESS = "repoAddress";
|
||||||
|
|
||||||
|
public static String[] ALL = {
|
||||||
|
_ID, APK_ID, VERSION, REPO_ID, HASH, VERSION_CODE, NAME, SIZE,
|
||||||
|
SIGNATURE, SOURCE_NAME, MIN_SDK_VERSION, PERMISSIONS, FEATURES,
|
||||||
|
NATIVE_CODE, HASH_TYPE, ADDED_DATE, IS_COMPATIBLE,
|
||||||
|
|
||||||
|
REPO_VERSION, REPO_ADDRESS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String PROVIDER_NAME = "ApkProvider";
|
||||||
|
|
||||||
|
private static final UriMatcher matcher = new UriMatcher(-1);
|
||||||
|
|
||||||
|
public static Map<String,String> REPO_FIELDS = new HashMap<String,String>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
REPO_FIELDS.put(DataColumns.REPO_VERSION, RepoProvider.DataColumns.VERSION);
|
||||||
|
REPO_FIELDS.put(DataColumns.REPO_ADDRESS, RepoProvider.DataColumns.ADDRESS);
|
||||||
|
|
||||||
|
matcher.addURI(AUTHORITY + "." + PROVIDER_NAME, null, CODE_LIST);
|
||||||
|
matcher.addURI(AUTHORITY + "." + PROVIDER_NAME, "/*/#", CODE_SINGLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Uri getContentUri() {
|
||||||
|
return Uri.parse("content://" + AUTHORITY + "." + PROVIDER_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Uri getContentUri(String id, int versionCode) {
|
||||||
|
return getContentUri()
|
||||||
|
.buildUpon()
|
||||||
|
.appendPath(Integer.toString(versionCode))
|
||||||
|
.appendPath(id)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getTableName() {
|
||||||
|
return DBHelper.TABLE_APK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getProviderName() {
|
||||||
|
return PROVIDER_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected UriMatcher getMatcher() {
|
||||||
|
return matcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class QueryBuilder {
|
||||||
|
|
||||||
|
private StringBuilder fields = new StringBuilder();
|
||||||
|
private StringBuilder tables = new StringBuilder(DBHelper.TABLE_APK + " AS apk");
|
||||||
|
private String selection = null;
|
||||||
|
private String orderBy = null;
|
||||||
|
|
||||||
|
private boolean repoTableRequired = false;
|
||||||
|
|
||||||
|
public void addField(String field) {
|
||||||
|
if (REPO_FIELDS.containsKey(field)) {
|
||||||
|
addRepoField(REPO_FIELDS.get(field), field);
|
||||||
|
} else if (field.equals(DataColumns._ID)) {
|
||||||
|
appendField("rowid", "apk", "_id");
|
||||||
|
} else if (field.startsWith("COUNT")) {
|
||||||
|
appendField(field);
|
||||||
|
} else {
|
||||||
|
appendField(field, "apk");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addRepoField(String field, String alias) {
|
||||||
|
if (!repoTableRequired) {
|
||||||
|
repoTableRequired = true;
|
||||||
|
tables.append(" LEFT JOIN ");
|
||||||
|
tables.append(DBHelper.TABLE_REPO);
|
||||||
|
tables.append(" AS repo ON (apk.repo = repo._id) ");
|
||||||
|
}
|
||||||
|
appendField(field, "repo", alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendField(String field) {
|
||||||
|
appendField(field, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendField(String field, String tableAlias) {
|
||||||
|
appendField(field, tableAlias, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendField(String field, String tableAlias,
|
||||||
|
String fieldAlias) {
|
||||||
|
if (fields.length() != 0) {
|
||||||
|
fields.append(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tableAlias != null) {
|
||||||
|
fields.append(tableAlias).append('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
fields.append(field);
|
||||||
|
|
||||||
|
if (fieldAlias != null) {
|
||||||
|
fields.append(" AS ").append(fieldAlias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSelection(String selection) {
|
||||||
|
this.selection = selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addOrderBy(String orderBy) {
|
||||||
|
this.orderBy = orderBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
|
||||||
|
StringBuilder suffix = new StringBuilder();
|
||||||
|
if (selection != null) {
|
||||||
|
suffix.append(" WHERE ").append(selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orderBy != null) {
|
||||||
|
suffix.append(" ORDER BY ").append(orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "SELECT " + fields + " FROM " + tables + suffix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String appendPrimaryKeyToSelection(String selection) {
|
||||||
|
return (selection == null ? "" : selection + " AND ") + " id = ? and vercode = ?";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] appendPrimaryKeyToArgs(Uri uri, String[] selectionArgs) {
|
||||||
|
List<String> args = new ArrayList<String>(selectionArgs.length + 2);
|
||||||
|
for (String arg : args) {
|
||||||
|
args.add(arg);
|
||||||
|
}
|
||||||
|
args.addAll(uri.getPathSegments());
|
||||||
|
return (String[])args.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||||
|
|
||||||
|
switch (matcher.match(uri)) {
|
||||||
|
case CODE_LIST:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CODE_SINGLE:
|
||||||
|
selection = appendPrimaryKeyToSelection(selection);
|
||||||
|
selectionArgs = appendPrimaryKeyToArgs(uri, selectionArgs);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log.e("FDroid", "Invalid URI for apk content provider: " + uri);
|
||||||
|
throw new UnsupportedOperationException("Invalid URI for apk content provider: " + uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder query = new QueryBuilder();
|
||||||
|
for (String field : projection) {
|
||||||
|
query.addField(field);
|
||||||
|
}
|
||||||
|
query.addSelection(selection);
|
||||||
|
query.addOrderBy(sortOrder);
|
||||||
|
|
||||||
|
Cursor cursor = read().rawQuery(query.toString(), selectionArgs);
|
||||||
|
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void removeRepoFields(ContentValues values) {
|
||||||
|
for (Map.Entry<String,String> repoField : REPO_FIELDS.entrySet()) {
|
||||||
|
String field = repoField.getKey();
|
||||||
|
if (values.containsKey(field)) {
|
||||||
|
Log.i("FDroid", "Cannot insert/update '" + field + "' field " +
|
||||||
|
"on apk table, as it belongs to the repo table. " +
|
||||||
|
"This field will be ignored.");
|
||||||
|
values.remove(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri insert(Uri uri, ContentValues values) {
|
||||||
|
|
||||||
|
removeRepoFields(values);
|
||||||
|
long id = write().insertOrThrow(getTableName(), null, values);
|
||||||
|
getContext().getContentResolver().notifyChange(uri, null);
|
||||||
|
return getContentUri(
|
||||||
|
values.getAsString(DataColumns.APK_ID),
|
||||||
|
values.getAsInteger(DataColumns.VERSION_CODE));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(Uri uri, String where, String[] whereArgs) {
|
||||||
|
|
||||||
|
switch (matcher.match(uri)) {
|
||||||
|
case CODE_LIST:
|
||||||
|
// Don't support deleting of multiple apks yet.
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case CODE_SINGLE:
|
||||||
|
where = appendPrimaryKeyToSelection(where);
|
||||||
|
whereArgs = appendPrimaryKeyToArgs(uri, whereArgs);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log.e("FDroid", "Invalid URI for apk content provider: " + uri);
|
||||||
|
throw new UnsupportedOperationException("Invalid URI for apk content provider: " + uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rowsAffected = write().delete(getTableName(), where, whereArgs);
|
||||||
|
getContext().getContentResolver().notifyChange(uri, null);
|
||||||
|
return rowsAffected;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
|
||||||
|
|
||||||
|
switch (matcher.match(uri)) {
|
||||||
|
case CODE_LIST:
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case CODE_SINGLE:
|
||||||
|
where = appendPrimaryKeyToSelection(where);
|
||||||
|
whereArgs = appendPrimaryKeyToArgs(uri, whereArgs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeRepoFields(values);
|
||||||
|
int numRows = write().update(getTableName(), values, where, whereArgs);
|
||||||
|
getContext().getContentResolver().notifyChange(uri, null);
|
||||||
|
return numRows;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -18,6 +18,11 @@ public class DBHelper extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
public static final String TABLE_REPO = "fdroid_repo";
|
public static final String TABLE_REPO = "fdroid_repo";
|
||||||
|
|
||||||
|
// The TABLE_APK table stores details of all the application versions we
|
||||||
|
// know about. Each relates directly back to an entry in TABLE_APP.
|
||||||
|
// This information is retrieved from the repositories.
|
||||||
|
public static final String TABLE_APK = "fdroid_apk";
|
||||||
|
|
||||||
private static final String CREATE_TABLE_REPO = "create table "
|
private static final String CREATE_TABLE_REPO = "create table "
|
||||||
+ TABLE_REPO + " (_id integer primary key, "
|
+ TABLE_REPO + " (_id integer primary key, "
|
||||||
+ "address text not null, "
|
+ "address text not null, "
|
||||||
@ -27,15 +32,26 @@ public class DBHelper extends SQLiteOpenHelper {
|
|||||||
+ "version integer not null default 0, "
|
+ "version integer not null default 0, "
|
||||||
+ "lastetag text, lastUpdated string);";
|
+ "lastetag text, lastUpdated string);";
|
||||||
|
|
||||||
private static final String CREATE_TABLE_APK = "create table " + DB.TABLE_APK
|
private static final String CREATE_TABLE_APK =
|
||||||
+ " ( " + "id text not null, " + "version text not null, "
|
"CREATE TABLE " + TABLE_APK + " ( "
|
||||||
+ "repo integer not null, " + "hash text not null, "
|
+ "id text not null, "
|
||||||
+ "vercode int not null," + "apkName text not null, "
|
+ "version text not null, "
|
||||||
+ "size int not null," + "sig string," + "srcname string,"
|
+ "repo integer not null, "
|
||||||
+ "minSdkVersion integer," + "permissions string,"
|
+ "hash text not null, "
|
||||||
+ "features string," + "nativecode string,"
|
+ "vercode int not null,"
|
||||||
+ "hashType string," + "added string,"
|
+ "apkName text not null, "
|
||||||
+ "compatible int not null," + "primary key(id,vercode));";
|
+ "size int not null, "
|
||||||
|
+ "sig string, "
|
||||||
|
+ "srcname string, "
|
||||||
|
+ "minSdkVersion integer, "
|
||||||
|
+ "permissions string, "
|
||||||
|
+ "features string, "
|
||||||
|
+ "nativecode string, "
|
||||||
|
+ "hashType string, "
|
||||||
|
+ "added string, "
|
||||||
|
+ "compatible int not null, "
|
||||||
|
+ "primary key(id, vercode)"
|
||||||
|
+ ");";
|
||||||
|
|
||||||
private static final String CREATE_TABLE_APP = "create table " + DB.TABLE_APP
|
private static final String CREATE_TABLE_APP = "create table " + DB.TABLE_APP
|
||||||
+ " ( " + "id text not null, " + "name text not null, "
|
+ " ( " + "id text not null, " + "name text not null, "
|
||||||
@ -308,7 +324,7 @@ public class DBHelper extends SQLiteOpenHelper {
|
|||||||
context.getSharedPreferences("FDroid", Context.MODE_PRIVATE).edit()
|
context.getSharedPreferences("FDroid", Context.MODE_PRIVATE).edit()
|
||||||
.putBoolean("triedEmptyUpdate", false).commit();
|
.putBoolean("triedEmptyUpdate", false).commit();
|
||||||
db.execSQL("drop table " + DB.TABLE_APP);
|
db.execSQL("drop table " + DB.TABLE_APP);
|
||||||
db.execSQL("drop table " + DB.TABLE_APK);
|
db.execSQL("drop table " + TABLE_APK);
|
||||||
db.execSQL("update " + TABLE_REPO + " set lastetag = NULL");
|
db.execSQL("update " + TABLE_REPO + " set lastetag = NULL");
|
||||||
createAppApk(db);
|
createAppApk(db);
|
||||||
}
|
}
|
||||||
@ -317,8 +333,8 @@ public class DBHelper extends SQLiteOpenHelper {
|
|||||||
db.execSQL(CREATE_TABLE_APP);
|
db.execSQL(CREATE_TABLE_APP);
|
||||||
db.execSQL("create index app_id on " + DB.TABLE_APP + " (id);");
|
db.execSQL("create index app_id on " + DB.TABLE_APP + " (id);");
|
||||||
db.execSQL(CREATE_TABLE_APK);
|
db.execSQL(CREATE_TABLE_APK);
|
||||||
db.execSQL("create index apk_vercode on " + DB.TABLE_APK + " (vercode);");
|
db.execSQL("create index apk_vercode on " + TABLE_APK + " (vercode);");
|
||||||
db.execSQL("create index apk_id on " + DB.TABLE_APK + " (id);");
|
db.execSQL("create index apk_id on " + TABLE_APK + " (id);");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean columnExists(SQLiteDatabase db,
|
private static boolean columnExists(SQLiteDatabase db,
|
||||||
|
@ -10,7 +10,7 @@ import java.net.URL;
|
|||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
public class Repo {
|
public class Repo extends ValueObject{
|
||||||
|
|
||||||
private long id;
|
private long id;
|
||||||
|
|
||||||
@ -46,14 +46,7 @@ public class Repo {
|
|||||||
} else if (column.equals(RepoProvider.DataColumns.IN_USE)) {
|
} else if (column.equals(RepoProvider.DataColumns.IN_USE)) {
|
||||||
inuse = cursor.getInt(i) == 1;
|
inuse = cursor.getInt(i) == 1;
|
||||||
} else if (column.equals(RepoProvider.DataColumns.LAST_UPDATED)) {
|
} else if (column.equals(RepoProvider.DataColumns.LAST_UPDATED)) {
|
||||||
String dateString = cursor.getString(i);
|
lastUpdated = toDate(cursor.getString(i));
|
||||||
if (dateString != null) {
|
|
||||||
try {
|
|
||||||
lastUpdated = DB.DATE_FORMAT.parse(dateString);
|
|
||||||
} catch (ParseException e) {
|
|
||||||
Log.e("FDroid", "Error parsing date " + dateString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (column.equals(RepoProvider.DataColumns.MAX_AGE)) {
|
} else if (column.equals(RepoProvider.DataColumns.MAX_AGE)) {
|
||||||
maxage = cursor.getInt(i);
|
maxage = cursor.getInt(i);
|
||||||
} else if (column.equals(RepoProvider.DataColumns.VERSION)) {
|
} else if (column.equals(RepoProvider.DataColumns.VERSION)) {
|
||||||
@ -78,13 +71,6 @@ public class Repo {
|
|||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumberOfApps() {
|
|
||||||
DB db = DB.getDB();
|
|
||||||
int count = db.countAppsForRepo(id);
|
|
||||||
DB.releaseDB();
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSigned() {
|
public boolean isSigned() {
|
||||||
return this.pubkey != null && this.pubkey.length() > 0;
|
return this.pubkey != null && this.pubkey.length() > 0;
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,7 @@ public class RepoProvider extends FDroidProvider {
|
|||||||
repos.add(new Repo(cursor));
|
repos.add(new Repo(cursor));
|
||||||
cursor.moveToNext();
|
cursor.moveToNext();
|
||||||
}
|
}
|
||||||
|
cursor.close();
|
||||||
}
|
}
|
||||||
return repos;
|
return repos;
|
||||||
}
|
}
|
||||||
@ -163,6 +164,20 @@ public class RepoProvider extends FDroidProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int countAppsForRepo(ContentResolver resolver,
|
||||||
|
long repoId) {
|
||||||
|
String[] projection = { "COUNT(distinct id)" };
|
||||||
|
String selection = "repo = ?";
|
||||||
|
String[] args = { Long.toString(repoId) };
|
||||||
|
Uri apkUri = ApkProvider.getContentUri();
|
||||||
|
Cursor result = resolver.query(apkUri, projection, selection, args, null);
|
||||||
|
if (result != null && result.getCount() > 0) {
|
||||||
|
result.moveToFirst();
|
||||||
|
return result.getInt(0);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface DataColumns extends BaseColumns {
|
public interface DataColumns extends BaseColumns {
|
||||||
@ -189,12 +204,12 @@ public class RepoProvider extends FDroidProvider {
|
|||||||
private static final UriMatcher matcher = new UriMatcher(-1);
|
private static final UriMatcher matcher = new UriMatcher(-1);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
matcher.addURI(AUTHORITY, PROVIDER_NAME, CODE_LIST);
|
matcher.addURI(AUTHORITY + "." + PROVIDER_NAME, null, CODE_LIST);
|
||||||
matcher.addURI(AUTHORITY, PROVIDER_NAME + "/#", CODE_SINGLE);
|
matcher.addURI(AUTHORITY + "." + PROVIDER_NAME, "#", CODE_SINGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Uri getContentUri() {
|
public static Uri getContentUri() {
|
||||||
return Uri.parse("content://" + AUTHORITY + "/" + PROVIDER_NAME);
|
return Uri.parse("content://" + AUTHORITY + "." + PROVIDER_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Uri getContentUri(long repoId) {
|
public static Uri getContentUri(long repoId) {
|
||||||
@ -226,8 +241,8 @@ public class RepoProvider extends FDroidProvider {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CODE_SINGLE:
|
case CODE_SINGLE:
|
||||||
selection = ( selection == null ? "" : selection ) +
|
selection = ( selection == null ? "" : selection + " AND " ) +
|
||||||
"_ID = " + uri.getLastPathSegment();
|
DataColumns._ID + " = " + uri.getLastPathSegment();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -287,7 +302,7 @@ public class RepoProvider extends FDroidProvider {
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case CODE_SINGLE:
|
case CODE_SINGLE:
|
||||||
where = ( where == null ? "" : where ) +
|
where = ( where == null ? "" : where + " AND " ) +
|
||||||
"_ID = " + uri.getLastPathSegment();
|
"_ID = " + uri.getLastPathSegment();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
23
src/org/fdroid/fdroid/data/ValueObject.java
Normal file
23
src/org/fdroid/fdroid/data/ValueObject.java
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package org.fdroid.fdroid.data;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import org.fdroid.fdroid.DB;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
abstract class ValueObject {
|
||||||
|
|
||||||
|
static Date toDate(String string) {
|
||||||
|
Date date = null;
|
||||||
|
if (string != null) {
|
||||||
|
try {
|
||||||
|
date = DB.DATE_FORMAT.parse(string);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
Log.e("FDroid", "Error parsing date " + string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -148,7 +148,10 @@ public class RepoDetailsFragment extends Fragment {
|
|||||||
TextView lastUpdated = (TextView)repoView.findViewById(R.id.text_last_update);
|
TextView lastUpdated = (TextView)repoView.findViewById(R.id.text_last_update);
|
||||||
|
|
||||||
name.setText(repo.getName());
|
name.setText(repo.getName());
|
||||||
numApps.setText(Integer.toString(repo.getNumberOfApps()));
|
|
||||||
|
int appCount = RepoProvider.Helper.countAppsForRepo(
|
||||||
|
getActivity().getContentResolver(), repo.getId());
|
||||||
|
numApps.setText(Integer.toString(appCount));
|
||||||
|
|
||||||
setupDescription(repoView, repo);
|
setupDescription(repoView, repo);
|
||||||
setupRepoFingerprint(repoView, repo);
|
setupRepoFingerprint(repoView, repo);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user