diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 57f95bb7f..89646a26b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -33,6 +33,12 @@
android:allowBackup="true"
android:theme="@style/AppThemeDark"
android:supportsRtl="false" >
+
+
+
diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java
index a63b4bb29..c01eccdb5 100644
--- a/src/org/fdroid/fdroid/AppDetails.java
+++ b/src/org/fdroid/fdroid/AppDetails.java
@@ -25,6 +25,8 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import org.fdroid.fdroid.data.Repo;
+import org.fdroid.fdroid.data.RepoProvider;
import org.xml.sax.XMLReader;
import android.app.AlertDialog;
@@ -859,20 +861,13 @@ public class AppDetails extends ListActivity {
// Install the version of this app denoted by 'app.curApk'.
private void install() {
- String ra = null;
- try {
- DB db = DB.getDB();
- DB.Repo repo = db.getRepo(app.curApk.repo);
- if (repo != null)
- ra = repo.address;
- } catch (Exception ex) {
- Log.d("FDroid", "Failed to get repo address - " + ex.getMessage());
- } finally {
- DB.releaseDB();
- }
- if (ra == null)
+ String [] projection = { RepoProvider.DataColumns.ADDRESS };
+ Repo repo = RepoProvider.Helper.findById(
+ getContentResolver(), app.curApk.repo, projection);
+ if (repo == null || repo.address == null) {
return;
- final String repoaddress = ra;
+ }
+ final String repoaddress = repo.address;
if (!app.curApk.compatible) {
AlertDialog.Builder ask_alrt = new AlertDialog.Builder(this);
diff --git a/src/org/fdroid/fdroid/DB.java b/src/org/fdroid/fdroid/DB.java
index 2ba046f4b..73099ee64 100644
--- a/src/org/fdroid/fdroid/DB.java
+++ b/src/org/fdroid/fdroid/DB.java
@@ -55,6 +55,7 @@ import org.fdroid.fdroid.compat.Compatibility;
import org.fdroid.fdroid.compat.ContextCompat;
import org.fdroid.fdroid.compat.SupportedArchitectures;
import org.fdroid.fdroid.data.DBHelper;
+import org.fdroid.fdroid.data.Repo;
public class DB {
@@ -290,7 +291,7 @@ public class DB {
public String version;
public int vercode;
public int detail_size; // Size in bytes - 0 means we don't know!
- public int repo; // ID of the repo it comes from
+ public long repo; // ID of the repo it comes from
public String detail_hash;
public String detail_hashType;
public int minSdkVersion; // 0 if unknown
@@ -404,111 +405,9 @@ public class DB {
}
}
- // The TABLE_REPO table stores the details of the repositories in use.
- public static final String TABLE_REPO = "fdroid_repo";
-
- public static class Repo {
- public int id;
- public String address;
- public String name;
- public String description;
- public int version; // index version, i.e. what fdroidserver built it - 0 if not specified
- public boolean inuse;
- public int priority;
- public String pubkey; // null for an unsigned repo
- public String fingerprint; // always null for an unsigned repo
- public int maxage; // maximum age of index that will be accepted - 0 for any
- public String lastetag; // last etag we updated from, null forces update
- public Date lastUpdated;
-
- /**
- * If we haven't run an update for this repo yet, then the name
- * will be unknown, in which case we will just take a guess at an
- * appropriate name based on the url (e.g. "fdroid.org/archive")
- */
- public String getName() {
- if (name == null) {
- String tempName = null;
- try {
- URL url = new URL(address);
- tempName = url.getHost() + url.getPath();
- } catch (MalformedURLException e) {
- tempName = address;
- }
- return tempName;
- } else {
- return name;
- }
- }
-
- public String toString() {
- return address;
- }
-
- public int getNumberOfApps() {
- DB db = DB.getDB();
- int count = db.countAppsForRepo(id);
- DB.releaseDB();
- return count;
- }
-
- /**
- * @param application In order invalidate the list of apps, we require
- * a reference to the top level application.
- */
- public void enable(FDroidApp application) {
- try {
- DB db = DB.getDB();
- List toEnable = new ArrayList(1);
- toEnable.add(this);
- db.enableRepos(toEnable);
- } finally {
- DB.releaseDB();
- }
- application.invalidateAllApps();
- }
-
- /**
- * @param application See DB.Repo.enable(application)
- */
- public void disable(FDroidApp application) {
- disableRemove(application, false);
- }
-
- /**
- * @param application See DB.Repo.enable(application)
- */
- public void remove(FDroidApp application) {
- disableRemove(application, true);
- }
-
- /**
- * @param application See DB.Repo.enable(application)
- */
- private void disableRemove(FDroidApp application, boolean removeAfterDisabling) {
- try {
- DB db = DB.getDB();
- List toDisable = new ArrayList(1);
- toDisable.add(this);
- db.doDisableRepos(toDisable, removeAfterDisabling);
- } finally {
- DB.releaseDB();
- }
- application.invalidateAllApps();
- }
-
- public boolean isSigned() {
- return this.pubkey != null && this.pubkey.length() > 0;
- }
-
- public boolean hasBeenUpdated() {
- return this.lastetag != null;
- }
- }
-
- private int countAppsForRepo(int id) {
+ public int countAppsForRepo(long id) {
String[] selection = { "COUNT(distinct id)" };
- String[] selectionArgs = { Integer.toString(id) };
+ String[] selectionArgs = { Long.toString(id) };
Cursor result = db.query(
TABLE_APK, selection, "repo = ?", selectionArgs, "repo", null, null);
if (result.getCount() > 0) {
@@ -554,8 +453,7 @@ public class DB {
// The date format used for storing dates (e.g. lastupdated, added) in the
// database.
- public static SimpleDateFormat dateFormat = new SimpleDateFormat(
- "yyyy-MM-dd", Locale.ENGLISH);
+ public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
private DB(Context ctx) {
@@ -659,7 +557,7 @@ public class DB {
private static final String[] POPULATE_APK_COLS = new String[] { "hash", "hashType", "size", "permissions" };
- private void populateApkDetails(Apk apk, int repo) {
+ private void populateApkDetails(Apk apk, long repo) {
if (repo == 0 || repo == apk.repo) {
Cursor cursor = null;
try {
@@ -692,7 +590,7 @@ public class DB {
// Populate the details for the given app, if necessary.
// If 'apkrepo' is non-zero, only apks from that repo are
// populated (this is used during the update process)
- public void populateDetails(App app, int apkRepo) {
+ public void populateDetails(App app, long apkRepo) {
if (!app.detail_Populated) {
populateAppDetails(app);
}
@@ -747,10 +645,10 @@ public class DB {
app.curVercode = c.getInt(9);
String sAdded = c.getString(10);
app.added = (sAdded == null || sAdded.length() == 0) ? null
- : dateFormat.parse(sAdded);
+ : DATE_FORMAT.parse(sAdded);
String sLastUpdated = c.getString(11);
app.lastUpdated = (sLastUpdated == null || sLastUpdated
- .length() == 0) ? null : dateFormat
+ .length() == 0) ? null : DATE_FORMAT
.parse(sLastUpdated);
app.compatible = c.getInt(12) == 1;
app.ignoreAllUpdates = c.getInt(13) == 1;
@@ -783,12 +681,16 @@ public class DB {
Log.d("FDroid", "Read app data from database " + " (took "
+ (System.currentTimeMillis() - startTime) + " ms)");
- List repos = getRepos();
- cols = new String[] { "id", "version", "vercode", "sig", "srcname",
- "apkName", "minSdkVersion", "added", "features", "nativecode",
- "compatible", "repo" };
- c = db.query(TABLE_APK, cols, null, null, null, null,
- "vercode desc");
+ 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()
@@ -829,21 +731,19 @@ public class DB {
apk.minSdkVersion = c.getInt(6);
String sApkAdded = c.getString(7);
apk.added = (sApkAdded == null || sApkAdded.length() == 0) ? null
- : dateFormat.parse(sApkAdded);
+ : 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);
if (app.iconUrl == null && app.icon != null) {
- for (DB.Repo repo : repos) {
- if (repo.id != repoid) continue;
- if (repo.version >= 11) {
- app.iconUrl = repo.address + iconsDir + app.icon;
- } else {
- app.iconUrl = repo.address + "/icons/" + app.icon;
- }
- break;
+ int repoVersion = c.getInt(12);
+ String repoAddress = c.getString(13);
+ if (repoVersion >= 11) {
+ app.iconUrl = repoAddress + iconsDir + app.icon;
+ } else {
+ app.iconUrl = repoAddress + "/icons/" + app.icon;
}
}
c.moveToNext();
@@ -1153,10 +1053,10 @@ public class DB {
values.put("dogecoinAddr", upapp.detail_dogecoinAddr);
values.put("flattrID", upapp.detail_flattrID);
values.put("added",
- upapp.added == null ? "" : dateFormat.format(upapp.added));
+ upapp.added == null ? "" : DATE_FORMAT.format(upapp.added));
values.put(
"lastUpdated",
- upapp.added == null ? "" : dateFormat
+ upapp.added == null ? "" : DATE_FORMAT
.format(upapp.lastUpdated));
values.put("curVersion", upapp.curVersion);
values.put("curVercode", upapp.curVercode);
@@ -1201,7 +1101,7 @@ public class DB {
values.put("apkName", upapk.apkName);
values.put("minSdkVersion", upapk.minSdkVersion);
values.put("added",
- upapk.added == null ? "" : dateFormat.format(upapk.added));
+ upapk.added == null ? "" : DATE_FORMAT.format(upapk.added));
values.put("permissions",
CommaSeparatedList.str(upapk.detail_permissions));
values.put("features", CommaSeparatedList.str(upapk.features));
@@ -1216,105 +1116,6 @@ public class DB {
}
}
- // Get details of a repo, given the ID. Returns null if the repo
- // doesn't exist.
- public Repo getRepo(int id) {
- Cursor c = null;
- try {
- c = db.query(TABLE_REPO, new String[] { "address", "name",
- "description", "version", "inuse", "priority", "pubkey",
- "fingerprint", "maxage", "lastetag", "lastUpdated" },
- "id = ?", new String[] { Integer.toString(id) }, null, null, null);
- if (!c.moveToFirst())
- return null;
- Repo repo = new Repo();
- repo.id = id;
- repo.address = c.getString(0);
- repo.name = c.getString(1);
- repo.description = c.getString(2);
- repo.version = c.getInt(3);
- repo.inuse = (c.getInt(4) == 1);
- repo.priority = c.getInt(5);
- repo.pubkey = c.getString(6);
- repo.fingerprint = c.getString(7);
- repo.maxage = c.getInt(8);
- repo.lastetag = c.getString(9);
- try {
- repo.lastUpdated = c.getString(10) != null ?
- dateFormat.parse( c.getString(10)) :
- null;
- } catch (ParseException e) {
- Log.e("FDroid", "Error parsing date " + c.getString(10));
- }
- return repo;
- } finally {
- if (c != null)
- c.close();
- }
- }
-
- // Get a list of the configured repositories.
- public List getRepos() {
- List repos = new ArrayList();
- Cursor c = null;
- try {
- c = db.query(TABLE_REPO, new String[] { "id", "address", "name",
- "description", "version", "inuse", "priority", "pubkey",
- "fingerprint", "maxage", "lastetag" },
- null, null, null, null, "priority");
- c.moveToFirst();
- while (!c.isAfterLast()) {
- Repo repo = new Repo();
- repo.id = c.getInt(0);
- repo.address = c.getString(1);
- repo.name = c.getString(2);
- repo.description = c.getString(3);
- repo.version = c.getInt(4);
- repo.inuse = (c.getInt(5) == 1);
- repo.priority = c.getInt(6);
- repo.pubkey = c.getString(7);
- repo.fingerprint = c.getString(8);
- repo.maxage = c.getInt(9);
- repo.lastetag = c.getString(10);
- repos.add(repo);
- c.moveToNext();
- }
- } catch (Exception e) {
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return repos;
- }
-
- public void enableRepos(List repos) {
- if (repos.isEmpty()) return;
-
- ContentValues values = new ContentValues(1);
- values.put("inuse", 1);
-
- String[] whereArgs = new String[repos.size()];
- StringBuilder where = new StringBuilder("address IN (");
- for (int i = 0; i < repos.size(); i ++) {
- Repo repo = repos.get(i);
- repo.inuse = true;
- whereArgs[i] = repo.address;
- where.append('?');
- if ( i < repos.size() - 1 ) {
- where.append(',');
- }
- }
- where.append(")");
- db.update(TABLE_REPO, values, where.toString(), whereArgs);
- }
-
- public void changeServerStatus(String address) {
- db.execSQL("update " + TABLE_REPO
- + " set inuse=1-inuse, lastetag=null where address = ?",
- new String[] { address });
- }
-
public void setIgnoreUpdates(String appid, boolean All, int This) {
db.execSQL("update " + TABLE_APP + " set"
+ " ignoreAllUpdates=" + (All ? '1' : '0')
@@ -1322,120 +1123,11 @@ public class DB {
+ " where id = ?", new String[] { appid });
}
- public void updateRepoByAddress(Repo repo) {
- updateRepo(repo, "address", repo.address);
- }
-
- public void updateRepo(Repo repo) {
- updateRepo(repo, "id", repo.id + "");
- }
-
- private void updateRepo(Repo repo, String field, String value) {
- ContentValues values = new ContentValues();
- values.put("name", repo.name);
- values.put("address", repo.address);
- values.put("description", repo.description);
- values.put("version", repo.version);
- values.put("inuse", repo.inuse);
- values.put("priority", repo.priority);
- values.put("pubkey", repo.pubkey);
- if (repo.pubkey != null && repo.fingerprint == null) {
- // we got a new pubkey, so calc the fingerprint
- values.put("fingerprint", DB.calcFingerprint(repo.pubkey));
- } else {
- values.put("fingerprint", repo.fingerprint);
- }
- values.put("maxage", repo.maxage);
- values.put("lastetag", (String) null);
- db.update(TABLE_REPO, values, field + " = ?",
- new String[] { value });
- }
-
- /**
- * Updates the lastUpdated time for every enabled repo.
- */
- public void refreshLastUpdates() {
- ContentValues values = new ContentValues();
- values.put("lastUpdated", dateFormat.format(new Date()));
- db.update(TABLE_REPO, values, "inuse = 1",
- new String[] {});
- }
-
- public void writeLastEtag(Repo repo) {
- ContentValues values = new ContentValues();
- values.put("lastetag", repo.lastetag);
- values.put("lastUpdated", dateFormat.format(new Date()));
- db.update(TABLE_REPO, values, "address = ?",
- new String[] { repo.address });
- }
-
- public void addRepo(String address, String name, String description,
- int version, int priority, String pubkey, String fingerprint,
- int maxage, boolean inuse)
- throws SecurityException {
- ContentValues values = new ContentValues();
- values.put("address", address);
- values.put("name", name);
- values.put("description", description);
- values.put("version", version);
- values.put("inuse", inuse ? 1 : 0);
- values.put("priority", priority);
- values.put("pubkey", pubkey);
- String calcedFingerprint = DB.calcFingerprint(pubkey);
- if (fingerprint == null) {
- fingerprint = calcedFingerprint;
- } else if (calcedFingerprint != null) {
- fingerprint = fingerprint.toUpperCase(Locale.ENGLISH);
- if (!fingerprint.equals(calcedFingerprint)) {
- throw new SecurityException("Given fingerprint does not match calculated one! ("
- + fingerprint + " != " + calcedFingerprint);
- }
- }
- values.put("fingerprint", fingerprint);
- values.put("maxage", maxage);
- values.put("lastetag", (String) null);
- db.insert(TABLE_REPO, null, values);
- }
-
- public void doDisableRepos(List repos, boolean remove) {
- if (repos.isEmpty()) return;
+ public void purgeApps(Repo repo, FDroidApp fdroid) {
db.beginTransaction();
- // TODO: Replace with
- // "delete from apk join repo where repo in (?, ?, ...)
- // "update repo set inuse = 0 | delete from repo ] where repo in (?, ?, ...)
try {
- for (Repo repo : repos) {
-
- String address = repo.address;
- // Before removing the repo, remove any apks that are
- // connected to it...
- Cursor c = null;
- try {
- c = db.query(TABLE_REPO, new String[]{"id"},
- "address = ?", new String[]{address},
- null, null, null, null);
- c.moveToFirst();
- if (!c.isAfterLast()) {
- db.delete(TABLE_APK, "repo = ?",
- new String[] { Integer.toString(c.getInt(0)) });
- }
- } finally {
- if (c != null) {
- c.close();
- }
- }
- if (remove)
- db.delete(TABLE_REPO, "address = ?",
- new String[] { address });
- else {
- ContentValues values = new ContentValues(2);
- values.put("inuse", 0);
- values.put("lastetag", (String)null);
- db.update(TABLE_REPO, values, "address = ?",
- new String[] { address });
- }
- }
+ db.delete(TABLE_APK, "repo = ?", new String[] { Long.toString(repo.getId()) });
List apps = getApps(false);
for (App app : apps) {
if (app.apks.isEmpty()) {
@@ -1446,6 +1138,8 @@ public class DB {
} finally {
db.endTransaction();
}
+
+ fdroid.invalidateAllApps();
}
public int getSynchronizationMode() {
diff --git a/src/org/fdroid/fdroid/ManageRepo.java b/src/org/fdroid/fdroid/ManageRepo.java
index 50191ff71..765999a76 100644
--- a/src/org/fdroid/fdroid/ManageRepo.java
+++ b/src/org/fdroid/fdroid/ManageRepo.java
@@ -21,34 +21,39 @@ package org.fdroid.fdroid;
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.ListActivity;
+import android.content.ContentValues;
import android.content.DialogInterface;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.ListFragment;
+import android.support.v4.content.CursorLoader;
import android.content.Intent;
+import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.support.v4.app.LoaderManager;
import android.support.v4.app.NavUtils;
+import android.support.v4.content.Loader;
import android.support.v4.view.MenuItemCompat;
import android.util.Log;
import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.*;
-import org.fdroid.fdroid.DB.Repo;
+
import org.fdroid.fdroid.compat.ActionBarCompat;
import org.fdroid.fdroid.compat.ClipboardCompat;
+import org.fdroid.fdroid.data.Repo;
+import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.views.RepoAdapter;
import org.fdroid.fdroid.views.RepoDetailsActivity;
import org.fdroid.fdroid.views.fragments.RepoDetailsFragment;
import java.net.MalformedURLException;
import java.net.URL;
-import java.util.*;
+import java.util.Locale;
-public class ManageRepo extends ListActivity {
-
- private static final String DEFAULT_NEW_REPO_TEXT = "https://";
- private final int ADD_REPO = 1;
- private final int UPDATE_REPOS = 2;
+public class ManageRepo extends FragmentActivity {
/**
* If we have a new repo added, or the address of a repo has changed, then
@@ -58,6 +63,122 @@ public class ManageRepo extends ListActivity {
*/
public static final String REQUEST_UPDATE = "update";
+ private RepoListFragment listFragment;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ ((FDroidApp) getApplication()).applyTheme(this);
+
+ if (savedInstanceState == null) {
+ listFragment = new RepoListFragment();
+ getSupportFragmentManager()
+ .beginTransaction()
+ .add(android.R.id.content, listFragment)
+ .commit();
+ }
+
+ ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true);
+ }
+
+ public void finish() {
+ Intent ret = new Intent();
+ if (listFragment.hasChanged()) {
+ Log.i("FDroid", "Repo details have changed, prompting for update.");
+ ret.putExtra(REQUEST_UPDATE, true);
+ }
+ setResult(Activity.RESULT_OK, ret);
+ super.finish();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ NavUtils.navigateUpFromSameTask(this);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+}
+
+class RepoListFragment extends ListFragment
+ implements LoaderManager.LoaderCallbacks,RepoAdapter.EnabledListener {
+
+ private static final String DEFAULT_NEW_REPO_TEXT = "https://";
+ private final int ADD_REPO = 1;
+ private final int UPDATE_REPOS = 2;
+
+ public boolean hasChanged() {
+ return changed;
+ }
+
+ @Override
+ public Loader onCreateLoader(int i, Bundle bundle) {
+ Uri uri = RepoProvider.getContentUri();
+ Log.i("FDroid", "Creating repo loader '" + uri + "'.");
+ String[] projection = new String[] {
+ RepoProvider.DataColumns._ID,
+ RepoProvider.DataColumns.NAME,
+ RepoProvider.DataColumns.PUBLIC_KEY,
+ RepoProvider.DataColumns.FINGERPRINT,
+ RepoProvider.DataColumns.IN_USE
+ };
+ return new CursorLoader(getActivity(), uri, projection, null, null, null);
+ }
+
+ @Override
+ public void onLoadFinished(Loader cursorLoader, Cursor cursor) {
+ Log.i("FDroid", "Repo cursor loaded.");
+ repoAdapter.swapCursor(cursor);
+ }
+
+ @Override
+ public void onLoaderReset(Loader cursorLoader) {
+ Log.i("FDroid", "Repo cursor reset.");
+ repoAdapter.swapCursor(null);
+ }
+
+
+ /**
+ * NOTE: If somebody toggles a repo off then on again, it will have removed
+ * all apps from the index when it was toggled off, so when it is toggled on
+ * again, then it will require a refresh.
+ *
+ * Previously, I toyed with the idea of remembering whether they had
+ * toggled on or off, and then only actually performing the function when
+ * the activity stopped, but I think that will be problematic. What about
+ * when they press the home button, or edit a repos details? It will start
+ * to become somewhat-random as to when the actual enabling, disabling is
+ * performed.
+ *
+ * So now, it just does the disable as soon as the user clicks "Off" and
+ * then removes the apps. To compensate for the removal of apps from
+ * index, it notifies the user via a toast that the apps have been removed.
+ * Also, as before, it will still prompt the user to update the repos if
+ * you toggled on on.
+ */
+ @Override
+ public void onSetEnabled(Repo repo, boolean isEnabled) {
+ if (repo.inuse != isEnabled ) {
+ ContentValues values = new ContentValues(1);
+ values.put(RepoProvider.DataColumns.IN_USE, isEnabled ? 1 : 0);
+ RepoProvider.Helper.update(
+ getActivity().getContentResolver(), repo, values);
+
+ if (isEnabled) {
+ changed = true;
+ } else {
+ FDroidApp app = (FDroidApp)getActivity().getApplication();
+ RepoProvider.Helper.purgeApps(repo, app);
+ String notification = getString(R.string.repo_disabled_notification, repo.name);
+ Toast.makeText(getActivity(), notification, Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+
private enum PositiveAction {
ADD_NEW, ENABLE, IGNORE
}
@@ -74,16 +195,14 @@ public class ManageRepo extends ListActivity {
private boolean isImportingRepo = false;
@Override
- protected void onCreate(Bundle savedInstanceState) {
-
- ((FDroidApp) getApplication()).applyTheme(this);
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- ActionBarCompat abCompat = ActionBarCompat.create(this);
- abCompat.setDisplayHomeAsUpEnabled(true);
+ setHasOptionsMenu(true);
- repoAdapter = new RepoAdapter(this);
+ repoAdapter = new RepoAdapter(getActivity(), null);
+ repoAdapter.setEnabledListener(this);
setListAdapter(repoAdapter);
/*
@@ -106,7 +225,7 @@ public class ManageRepo extends ListActivity {
*/
/* let's see if someone is trying to send us a new repo */
- Intent intent = getIntent();
+ Intent intent = getActivity().getIntent();
/* an URL from a click or a QRCode scan */
Uri uri = intent.getData();
if (uri != null) {
@@ -139,27 +258,25 @@ public class ManageRepo extends ListActivity {
}
@Override
- protected void onResume() {
+ public void onResume() {
super.onResume();
- refreshList();
+
+ //Starts a new or restarts an existing Loader in this manager
+ getLoaderManager().restartLoader(0, null, this);
}
@Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
+ public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
- DB.Repo repo = (DB.Repo)getListView().getItemAtPosition(position);
+ Repo repo = new Repo((Cursor)getListView().getItemAtPosition(position));
editRepo(repo);
}
- private void refreshList() {
- repoAdapter.refresh();
- }
-
@Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
MenuItem updateItem = menu.add(Menu.NONE, UPDATE_REPOS, 1,
R.string.menu_update_repo).setIcon(R.drawable.ic_menu_refresh);
@@ -171,102 +288,23 @@ public class ManageRepo extends ListActivity {
android.R.drawable.ic_menu_add);
MenuItemCompat.setShowAsAction(addItem,
MenuItemCompat.SHOW_AS_ACTION_ALWAYS |
- MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT);
-
- return true;
+ MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT);
}
public static final int SHOW_REPO_DETAILS = 1;
- public void editRepo(DB.Repo repo) {
- Log.d("FDroid", "Showing details screen for repo: '" + repo + "'.");
- Intent intent = new Intent(this, RepoDetailsActivity.class);
- intent.putExtra(RepoDetailsFragment.ARG_REPO_ID, repo.id);
+ public void editRepo(Repo repo) {
+ Intent intent = new Intent(getActivity(), RepoDetailsActivity.class);
+ intent.putExtra(RepoDetailsFragment.ARG_REPO_ID, repo.getId());
startActivityForResult(intent, SHOW_REPO_DETAILS);
}
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
-
- if (requestCode == SHOW_REPO_DETAILS && resultCode == RESULT_OK) {
-
- boolean wasDeleted = data.getBooleanExtra(RepoDetailsActivity.ACTION_IS_DELETED, false);
- boolean wasEnabled = data.getBooleanExtra(RepoDetailsActivity.ACTION_IS_ENABLED, false);
- boolean wasDisabled = data.getBooleanExtra(RepoDetailsActivity.ACTION_IS_DISABLED, false);
- boolean wasChanged = data.getBooleanExtra(RepoDetailsActivity.ACTION_IS_CHANGED, false);
-
- if (wasDeleted) {
- int repoId = data.getIntExtra(RepoDetailsActivity.DATA_REPO_ID, 0);
- remove(repoId);
- } else if (wasEnabled || wasDisabled || wasChanged) {
- changed = true;
- }
- }
- }
-
- private DB.Repo getRepoById(int repoId) {
- for (int i = 0; i < getListAdapter().getCount(); i ++) {
- DB.Repo repo = (DB.Repo)getListAdapter().getItem(i);
- if (repo.id == repoId) {
- return repo;
- }
- }
- return null;
- }
-
- private void remove(int repoId) {
- DB.Repo repo = getRepoById(repoId);
- if (repo == null) {
- return;
- }
-
- List reposToRemove = new ArrayList(1);
- reposToRemove.add(repo);
- try {
- DB db = DB.getDB();
- db.doDisableRepos(reposToRemove, true);
- } finally {
- DB.releaseDB();
- }
- refreshList();
- }
-
- protected List getRepos() {
- List repos = null;
- try {
- DB db = DB.getDB();
- repos = db.getRepos();
- } finally {
- DB.releaseDB();
- }
- return repos;
- }
-
- protected Repo getRepoByAddress(String address, List repos) {
- if (address != null)
- for (Repo repo : repos)
- if (address.equals(repo.address))
- return repo;
- return null;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- NavUtils.navigateUpFromSameTask(this);
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
private void updateRepos() {
- UpdateService.updateNow(this).setListener(new ProgressListener() {
+ UpdateService.updateNow(getActivity()).setListener(new ProgressListener() {
@Override
public void onProgress(Event event) {
// No need to prompt to update any more, we just did it!
changed = false;
- refreshList();
}
});
}
@@ -276,14 +314,14 @@ public class ManageRepo extends ListActivity {
}
private void showAddRepo(String newAddress, String newFingerprint) {
-
- View view = getLayoutInflater().inflate(R.layout.addrepo, null);
- final AlertDialog alrt = new AlertDialog.Builder(this).setView(view).create();
+ View view = getLayoutInflater(null).inflate(R.layout.addrepo, null);
+ final AlertDialog alrt = new AlertDialog.Builder(getActivity()).setView(view).create();
final EditText uriEditText = (EditText) view.findViewById(R.id.edit_uri);
final EditText fingerprintEditText = (EditText) view.findViewById(R.id.edit_fingerprint);
- List repos = getRepos();
- final Repo repo = newAddress != null && isImportingRepo ? getRepoByAddress(newAddress, repos) : null;
+ final Repo repo = ( newAddress != null && isImportingRepo )
+ ? RepoProvider.Helper.findByAddress(getActivity().getContentResolver(), newAddress)
+ : null;
alrt.setIcon(android.R.drawable.ic_menu_add);
alrt.setTitle(getString(R.string.repo_add_title));
@@ -312,8 +350,8 @@ public class ManageRepo extends ListActivity {
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- setResult(Activity.RESULT_CANCELED);
- finish();
+ getActivity().setResult(Activity.RESULT_CANCELED);
+ getActivity().finish();
return;
}
});
@@ -338,7 +376,7 @@ public class ManageRepo extends ListActivity {
// this entry already exists and is not enabled, offer to enable it
if (repo.inuse) {
alrt.dismiss();
- Toast.makeText(this, R.string.repo_exists_and_enabled, Toast.LENGTH_LONG).show();
+ Toast.makeText(getActivity(), R.string.repo_exists_and_enabled, Toast.LENGTH_LONG).show();
return;
} else {
overwriteMessage.setText(R.string.repo_exists_enable);
@@ -373,12 +411,10 @@ public class ManageRepo extends ListActivity {
* Adds a new repo to the database.
*/
private void createNewRepo(String address, String fingerprint) {
- try {
- DB db = DB.getDB();
- db.addRepo(address, null, null, 0, 10, null, fingerprint, 0, true);
- } finally {
- DB.releaseDB();
- }
+ ContentValues values = new ContentValues(2);
+ values.put(RepoProvider.DataColumns.ADDRESS, address);
+ values.put(RepoProvider.DataColumns.FINGERPRINT, fingerprint);
+ RepoProvider.Helper.insert(getActivity().getContentResolver(), values);
finishedAddingRepo();
}
@@ -386,13 +422,10 @@ public class ManageRepo extends ListActivity {
* Seeing as this repo already exists, we will force it to be enabled again.
*/
private void createNewRepo(Repo repo) {
+ ContentValues values = new ContentValues(1);
+ values.put(RepoProvider.DataColumns.IN_USE, 1);
+ RepoProvider.Helper.update(getActivity().getContentResolver(), repo, values);
repo.inuse = true;
- try {
- DB db = DB.getDB();
- db.updateRepoByAddress(repo);
- } finally {
- DB.releaseDB();
- }
finishedAddingRepo();
}
@@ -404,17 +437,13 @@ public class ManageRepo extends ListActivity {
private void finishedAddingRepo() {
changed = true;
if (isImportingRepo) {
- setResult(Activity.RESULT_OK);
- finish();
- } else {
- refreshList();
+ getActivity().setResult(Activity.RESULT_OK);
+ getActivity().finish();
}
}
@Override
- public boolean onMenuItemSelected(int featureId, MenuItem item) {
-
- super.onMenuItemSelected(featureId, item);
+ public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == ADD_REPO) {
showAddRepo();
@@ -424,7 +453,7 @@ public class ManageRepo extends ListActivity {
return true;
}
- return false;
+ return super.onOptionsItemSelected(item);
}
/**
@@ -432,7 +461,7 @@ public class ManageRepo extends ListActivity {
* Otherwise return "https://".
*/
private String getNewRepoUri() {
- ClipboardCompat clipboard = ClipboardCompat.create(this);
+ ClipboardCompat clipboard = ClipboardCompat.create(getActivity());
String text = clipboard.getText();
if (text != null) {
try {
@@ -447,46 +476,4 @@ public class ManageRepo extends ListActivity {
}
return text;
}
-
- @Override
- public void finish() {
- Intent ret = new Intent();
- if (changed) {
- Log.i("FDroid", "Repo details have changed, prompting for update.");
- ret.putExtra(REQUEST_UPDATE, true);
- }
- setResult(RESULT_OK, ret);
- super.finish();
- }
-
- /**
- * NOTE: If somebody toggles a repo off then on again, it will have removed
- * all apps from the index when it was toggled off, so when it is toggled on
- * again, then it will require a refresh.
- *
- * Previously, I toyed with the idea of remembering whether they had
- * toggled on or off, and then only actually performing the function when
- * the activity stopped, but I think that will be problematic. What about
- * when they press the home button, or edit a repos details? It will start
- * to become somewhat-random as to when the actual enabling, disabling is
- * performed.
- *
- * So now, it just does the disable as soon as the user clicks "Off" and
- * then removes the apps. To compensate for the removal of apps from
- * index, it notifies the user via a toast that the apps have been removed.
- * Also, as before, it will still prompt the user to update the repos if
- * you toggled on on.
- */
- public void setRepoEnabled(DB.Repo repo, boolean enabled) {
- FDroidApp app = (FDroidApp)getApplication();
- if (enabled) {
- repo.enable(app);
- changed = true;
- } else {
- repo.disable(app);
- String notification = getString(R.string.repo_disabled_notification, repo.toString());
- Toast.makeText(this, notification, Toast.LENGTH_LONG).show();
- }
- }
-
}
diff --git a/src/org/fdroid/fdroid/RepoXMLHandler.java b/src/org/fdroid/fdroid/RepoXMLHandler.java
index a41c867d1..59a109b7a 100644
--- a/src/org/fdroid/fdroid/RepoXMLHandler.java
+++ b/src/org/fdroid/fdroid/RepoXMLHandler.java
@@ -20,6 +20,7 @@
package org.fdroid.fdroid;
import android.os.Bundle;
+import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.updater.RepoUpdater;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
@@ -33,7 +34,7 @@ import java.util.Map;
public class RepoXMLHandler extends DefaultHandler {
// The repo we're processing.
- private DB.Repo repo;
+ private Repo repo;
private Map apps;
private List appsList;
@@ -62,7 +63,7 @@ public class RepoXMLHandler extends DefaultHandler {
private int totalAppCount;
- public RepoXMLHandler(DB.Repo repo, List appsList, ProgressListener listener) {
+ public RepoXMLHandler(Repo repo, List appsList, ProgressListener listener) {
this.repo = repo;
this.apps = new HashMap();
for (DB.App app : appsList) this.apps.put(app.id, app);
@@ -157,7 +158,7 @@ public class RepoXMLHandler extends DefaultHandler {
}
} else if (curel.equals("added")) {
try {
- curapk.added = str.length() == 0 ? null : DB.dateFormat
+ curapk.added = str.length() == 0 ? null : DB.DATE_FORMAT
.parse(str);
} catch (ParseException e) {
curapk.added = null;
@@ -204,7 +205,7 @@ public class RepoXMLHandler extends DefaultHandler {
curapp.detail_trackerURL = str;
} else if (curel.equals("added")) {
try {
- curapp.added = str.length() == 0 ? null : DB.dateFormat
+ curapp.added = str.length() == 0 ? null : DB.DATE_FORMAT
.parse(str);
} catch (ParseException e) {
curapp.added = null;
@@ -212,7 +213,7 @@ public class RepoXMLHandler extends DefaultHandler {
} else if (curel.equals("lastupdated")) {
try {
curapp.lastUpdated = str.length() == 0 ? null
- : DB.dateFormat.parse(str);
+ : DB.DATE_FORMAT.parse(str);
} catch (ParseException e) {
curapp.lastUpdated = null;
}
@@ -281,7 +282,7 @@ public class RepoXMLHandler extends DefaultHandler {
} else if (localName.equals("package") && curapp != null && curapk == null) {
curapk = new DB.Apk();
curapk.id = curapp.id;
- curapk.repo = repo.id;
+ curapk.repo = repo.getId();
hashType = null;
} else if (localName.equals("hash") && curapk != null) {
diff --git a/src/org/fdroid/fdroid/UpdateService.java b/src/org/fdroid/fdroid/UpdateService.java
index f2c13b760..d161cc5ae 100644
--- a/src/org/fdroid/fdroid/UpdateService.java
+++ b/src/org/fdroid/fdroid/UpdateService.java
@@ -41,12 +41,16 @@ import android.os.Parcelable;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.preference.PreferenceManager;
+import android.util.Log;
+
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
+import org.fdroid.fdroid.data.Repo;
+import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.updater.RepoUpdater;
public class UpdateService extends IntentService implements ProgressListener {
@@ -280,22 +284,23 @@ public class UpdateService extends IntentService implements ProgressListener {
// Grab some preliminary information, then we can release the
// database while we do all the downloading, etc...
int updates = 0;
- List repos;
+ List repos;
List apps;
try {
DB db = DB.getDB();
- repos = db.getRepos();
apps = db.getApps(false);
} finally {
DB.releaseDB();
}
+ repos = RepoProvider.Helper.all(getContentResolver());
+
// Process each repo...
List updatingApps = new ArrayList();
- Set keeprepos = new TreeSet();
+ Set keeprepos = new TreeSet();
boolean changes = false;
boolean update;
- for (DB.Repo repo : repos) {
+ for (Repo repo : repos) {
if (!repo.inuse)
continue;
// are we updating all repos, or just one?
@@ -306,7 +311,7 @@ public class UpdateService extends IntentService implements ProgressListener {
if (address.equals(repo.address)) {
update = true;
} else {
- keeprepos.add(repo.id);
+ keeprepos.add(repo.getId());
update = false;
}
}
@@ -321,7 +326,7 @@ public class UpdateService extends IntentService implements ProgressListener {
updatingApps.addAll(updater.getApps());
changes = true;
} else {
- keeprepos.add(repo.id);
+ keeprepos.add(repo.getId());
}
} catch (RepoUpdater.UpdateException e) {
errmsg += (errmsg.length() == 0 ? "" : "\n") + e.getMessage();
@@ -343,7 +348,7 @@ public class UpdateService extends IntentService implements ProgressListener {
// Need to flag things we're keeping despite having received
// no data about during the update. (i.e. stuff from a repo
// that we know is unchanged due to the etag)
- for (int keep : keeprepos) {
+ for (long keep : keeprepos) {
for (DB.App app : apps) {
boolean keepapp = false;
for (DB.Apk apk : app.apks) {
@@ -378,8 +383,6 @@ public class UpdateService extends IntentService implements ProgressListener {
db.updateApplication(app);
}
db.endUpdate();
- for (DB.Repo repo : repos)
- db.writeLastEtag(repo);
} catch (Exception ex) {
db.cancelUpdate();
Log.e("FDroid", "Exception during update processing:\n"
diff --git a/src/org/fdroid/fdroid/Utils.java b/src/org/fdroid/fdroid/Utils.java
index 37d4920fa..c6b397df2 100644
--- a/src/org/fdroid/fdroid/Utils.java
+++ b/src/org/fdroid/fdroid/Utils.java
@@ -36,6 +36,7 @@ import java.util.Locale;
import android.content.Context;
import com.nostra13.universalimageloader.utils.StorageUtils;
+import org.fdroid.fdroid.data.Repo;
public final class Utils {
@@ -159,7 +160,7 @@ public final class Utils {
return count;
}
- public static String formatFingerprint(DB.Repo repo) {
+ public static String formatFingerprint(Repo repo) {
return formatFingerprint(repo.pubkey);
}
diff --git a/src/org/fdroid/fdroid/compat/SwitchCompat.java b/src/org/fdroid/fdroid/compat/SwitchCompat.java
index e683fb625..b5cbb7815 100644
--- a/src/org/fdroid/fdroid/compat/SwitchCompat.java
+++ b/src/org/fdroid/fdroid/compat/SwitchCompat.java
@@ -1,26 +1,27 @@
package org.fdroid.fdroid.compat;
import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.ToggleButton;
-import org.fdroid.fdroid.ManageRepo;
public abstract class SwitchCompat extends Compatibility {
- protected final ManageRepo activity;
+ protected final Context context;
- protected SwitchCompat(ManageRepo activity) {
- this.activity = activity;
+ protected SwitchCompat(Context context) {
+ this.context = context;
}
public abstract CompoundButton createSwitch();
- public static SwitchCompat create(ManageRepo activity) {
+ public static SwitchCompat create(Context context) {
if (hasApi(14)) {
- return new IceCreamSwitch(activity);
+ return new IceCreamSwitch(context);
} else {
- return new OldSwitch(activity);
+ return new OldSwitch(context);
}
}
@@ -29,24 +30,24 @@ public abstract class SwitchCompat extends Compatibility {
@TargetApi(14)
class IceCreamSwitch extends SwitchCompat {
- protected IceCreamSwitch(ManageRepo activity) {
- super(activity);
+ protected IceCreamSwitch(Context context) {
+ super(context);
}
@Override
public CompoundButton createSwitch() {
- return new Switch(activity);
+ return new Switch(context);
}
}
class OldSwitch extends SwitchCompat {
- protected OldSwitch(ManageRepo activity) {
- super(activity);
+ protected OldSwitch(Context context) {
+ super(context);
}
@Override
public CompoundButton createSwitch() {
- return new ToggleButton(activity);
+ return new ToggleButton(context);
}
}
diff --git a/src/org/fdroid/fdroid/data/DBHelper.java b/src/org/fdroid/fdroid/data/DBHelper.java
index 272a29551..26c1b3582 100644
--- a/src/org/fdroid/fdroid/data/DBHelper.java
+++ b/src/org/fdroid/fdroid/data/DBHelper.java
@@ -16,11 +16,15 @@ public class DBHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "fdroid";
+ public static final String TABLE_REPO = "fdroid_repo";
+
private static final String CREATE_TABLE_REPO = "create table "
- + DB.TABLE_REPO + " (id integer primary key, address text not null, "
+ + TABLE_REPO + " (_id integer primary key, "
+ + "address text not null, "
+ "name text, description text, inuse integer not null, "
+ "priority integer not null, pubkey text, fingerprint text, "
- + "maxage integer not null default 0, version integer not null default 0,"
+ + "maxage integer not null default 0, "
+ + "version integer not null default 0, "
+ "lastetag text, lastUpdated string);";
private static final String CREATE_TABLE_APK = "create table " + DB.TABLE_APK
@@ -49,7 +53,7 @@ public class DBHelper extends SQLiteOpenHelper {
+ "ignoreThisUpdate int not null,"
+ "primary key(id));";
- private static final int DB_VERSION = 35;
+ private static final int DB_VERSION = 37;
private Context context;
@@ -58,6 +62,79 @@ public class DBHelper extends SQLiteOpenHelper {
this.context = context;
}
+ private void populateRepoNames(SQLiteDatabase db, int oldVersion) {
+ if (oldVersion < 37) {
+ String[] columns = { "address", "_id" };
+ Cursor cursor = db.query(TABLE_REPO, columns,
+ "name IS NULL OR name = ''", null, null, null, null);
+ cursor.moveToFirst();
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ while (!cursor.isAfterLast()) {
+ String address = cursor.getString(0);
+ long id = cursor.getInt(1);
+ ContentValues values = new ContentValues(1);
+ values.put("name", Repo.addressToName(address));
+ String[] args = { Long.toString( id ) };
+ db.update(TABLE_REPO, values, "_id = ?", args);
+ cursor.moveToNext();
+ }
+ }
+ }
+ }
+
+ private void renameRepoId(SQLiteDatabase db, int oldVersion) {
+ if (oldVersion < 36) {
+
+ Log.d("FDroid", "Renaming " + TABLE_REPO + ".id to _id");
+
+ db.beginTransaction();
+
+ try {
+ // http://stackoverflow.com/questions/805363/how-do-i-rename-a-column-in-a-sqlite-database-table#805508
+ String tempTableName = TABLE_REPO + "__temp__";
+ db.execSQL("ALTER TABLE " + TABLE_REPO + " RENAME TO " + tempTableName + ";" );
+
+ // I realise this is available in the CREATE_TABLE_REPO above,
+ // however I have a feeling that it will need to be the same as the
+ // current structure of the table as of DBVersion 36, or else we may
+ // get into strife. For example, if there was a field that
+ // got removed, then it will break the "insert select"
+ // statement. Therefore, I've put a copy of CREATE_TABLE_REPO
+ // here that is the same as it was at DBVersion 36.
+ String createTableDdl = "create table " + TABLE_REPO + " ("
+ + "_id integer not null primary key, "
+ + "address text not null, "
+ + "name text, "
+ + "description text, "
+ + "inuse integer not null, "
+ + "priority integer not null, "
+ + "pubkey text, "
+ + "fingerprint text, "
+ + "maxage integer not null default 0, "
+ + "version integer not null default 0, "
+ + "lastetag text, "
+ + "lastUpdated string);";
+
+ db.execSQL(createTableDdl);
+
+ String nonIdFields = "address, name, description, inuse, priority, " +
+ "pubkey, fingerprint, maxage, version, lastetag, lastUpdated";
+
+ String insertSql = "INSERT INTO " + TABLE_REPO +
+ "(_id, " + nonIdFields + " ) " +
+ "SELECT id, " + nonIdFields + " FROM " + tempTableName + ";";
+
+ db.execSQL(insertSql);
+ db.execSQL("DROP TABLE " + tempTableName + ";");
+ db.setTransactionSuccessful();
+ } catch (Exception e) {
+ Log.e("FDroid", "Error renaming id to _id: " + e.getMessage());
+ }
+ db.endTransaction();
+ }
+ }
+
@Override
public void onCreate(SQLiteDatabase db) {
@@ -80,7 +157,7 @@ public class DBHelper extends SQLiteOpenHelper {
values.put("inuse", 1);
values.put("priority", 10);
values.put("lastetag", (String) null);
- db.insert(DB.TABLE_REPO, null, values);
+ db.insert(TABLE_REPO, null, values);
values = new ContentValues();
values.put("address",
@@ -97,7 +174,7 @@ public class DBHelper extends SQLiteOpenHelper {
values.put("inuse", 0);
values.put("priority", 20);
values.put("lastetag", (String) null);
- db.insert(DB.TABLE_REPO, null, values);
+ db.insert(TABLE_REPO, null, values);
}
@Override
@@ -118,6 +195,8 @@ public class DBHelper extends SQLiteOpenHelper {
addMaxAgeToRepo(db, oldVersion);
addVersionToRepo(db, oldVersion);
addLastUpdatedToRepo(db, oldVersion);
+ renameRepoId(db, oldVersion);
+ populateRepoNames(db, oldVersion);
}
/**
@@ -126,13 +205,13 @@ public class DBHelper extends SQLiteOpenHelper {
*/
private void migradeRepoTable(SQLiteDatabase db, int oldVersion) {
if (oldVersion < 20) {
- List oldrepos = new ArrayList();
- Cursor c = db.query(DB.TABLE_REPO,
+ List oldrepos = new ArrayList();
+ Cursor c = db.query(TABLE_REPO,
new String[] { "address", "inuse", "pubkey" },
null, null, null, null, null);
c.moveToFirst();
while (!c.isAfterLast()) {
- DB.Repo repo = new DB.Repo();
+ Repo repo = new Repo();
repo.address = c.getString(0);
repo.inuse = (c.getInt(1) == 1);
repo.pubkey = c.getString(2);
@@ -140,16 +219,16 @@ public class DBHelper extends SQLiteOpenHelper {
c.moveToNext();
}
c.close();
- db.execSQL("drop table " + DB.TABLE_REPO);
+ db.execSQL("drop table " + TABLE_REPO);
db.execSQL(CREATE_TABLE_REPO);
- for (DB.Repo repo : oldrepos) {
+ for (Repo repo : oldrepos) {
ContentValues values = new ContentValues();
values.put("address", repo.address);
values.put("inuse", repo.inuse);
values.put("priority", 10);
values.put("pubkey", repo.pubkey);
values.put("lastetag", (String) null);
- db.insert(DB.TABLE_REPO, null, values);
+ db.insert(TABLE_REPO, null, values);
}
}
}
@@ -160,19 +239,19 @@ public class DBHelper extends SQLiteOpenHelper {
*/
private void addNameAndDescriptionToRepo(SQLiteDatabase db, int oldVersion) {
if (oldVersion < 21) {
- if (!columnExists(db, DB.TABLE_REPO, "name"))
- db.execSQL("alter table " + DB.TABLE_REPO + " add column name text");
- if (!columnExists(db, DB.TABLE_REPO, "description"))
- db.execSQL("alter table " + DB.TABLE_REPO + " add column description text");
+ if (!columnExists(db, TABLE_REPO, "name"))
+ db.execSQL("alter table " + TABLE_REPO + " add column name text");
+ if (!columnExists(db, TABLE_REPO, "description"))
+ db.execSQL("alter table " + TABLE_REPO + " add column description text");
ContentValues values = new ContentValues();
values.put("name", context.getString(R.string.default_repo_name));
values.put("description", context.getString(R.string.default_repo_description));
- db.update(DB.TABLE_REPO, values, "address = ?", new String[]{
+ db.update(TABLE_REPO, values, "address = ?", new String[]{
context.getString(R.string.default_repo_address)});
values.clear();
values.put("name", context.getString(R.string.default_repo_name2));
values.put("description", context.getString(R.string.default_repo_description2));
- db.update(DB.TABLE_REPO, values, "address = ?", new String[] {
+ db.update(TABLE_REPO, values, "address = ?", new String[] {
context.getString(R.string.default_repo_address2) });
}
@@ -184,44 +263,44 @@ public class DBHelper extends SQLiteOpenHelper {
*/
private void addFingerprintToRepo(SQLiteDatabase db, int oldVersion) {
if (oldVersion < 29) {
- if (!columnExists(db, DB.TABLE_REPO, "fingerprint"))
- db.execSQL("alter table " + DB.TABLE_REPO + " add column fingerprint text");
- List oldrepos = new ArrayList();
- Cursor c = db.query(DB.TABLE_REPO,
+ if (!columnExists(db, TABLE_REPO, "fingerprint"))
+ db.execSQL("alter table " + TABLE_REPO + " add column fingerprint text");
+ List oldrepos = new ArrayList();
+ Cursor c = db.query(TABLE_REPO,
new String[] { "address", "pubkey" },
null, null, null, null, null);
c.moveToFirst();
while (!c.isAfterLast()) {
- DB.Repo repo = new DB.Repo();
+ Repo repo = new Repo();
repo.address = c.getString(0);
repo.pubkey = c.getString(1);
oldrepos.add(repo);
c.moveToNext();
}
c.close();
- for (DB.Repo repo : oldrepos) {
+ for (Repo repo : oldrepos) {
ContentValues values = new ContentValues();
values.put("fingerprint", DB.calcFingerprint(repo.pubkey));
- db.update(DB.TABLE_REPO, values, "address = ?", new String[] { repo.address });
+ db.update(TABLE_REPO, values, "address = ?", new String[] { repo.address });
}
}
}
private void addMaxAgeToRepo(SQLiteDatabase db, int oldVersion) {
if (oldVersion < 30) {
- db.execSQL("alter table " + DB.TABLE_REPO + " add column maxage integer not null default 0");
+ db.execSQL("alter table " + TABLE_REPO + " add column maxage integer not null default 0");
}
}
private void addVersionToRepo(SQLiteDatabase db, int oldVersion) {
- if (oldVersion < 33 && !columnExists(db, DB.TABLE_REPO, "version")) {
- db.execSQL("alter table " + DB.TABLE_REPO + " add column version integer not null default 0");
+ if (oldVersion < 33 && !columnExists(db, TABLE_REPO, "version")) {
+ db.execSQL("alter table " + TABLE_REPO + " add column version integer not null default 0");
}
}
private void addLastUpdatedToRepo(SQLiteDatabase db, int oldVersion) {
- if (oldVersion < 35 && !columnExists(db, DB.TABLE_REPO, "lastUpdated")) {
- db.execSQL("Alter table " + DB.TABLE_REPO + " add column lastUpdated string");
+ if (oldVersion < 35 && !columnExists(db, TABLE_REPO, "lastUpdated")) {
+ db.execSQL("Alter table " + TABLE_REPO + " add column lastUpdated string");
}
}
@@ -230,7 +309,7 @@ public class DBHelper extends SQLiteOpenHelper {
.putBoolean("triedEmptyUpdate", false).commit();
db.execSQL("drop table " + DB.TABLE_APP);
db.execSQL("drop table " + DB.TABLE_APK);
- db.execSQL("update " + DB.TABLE_REPO + " set lastetag = NULL");
+ db.execSQL("update " + TABLE_REPO + " set lastetag = NULL");
createAppApk(db);
}
diff --git a/src/org/fdroid/fdroid/data/FDroidProvider.java b/src/org/fdroid/fdroid/data/FDroidProvider.java
new file mode 100644
index 000000000..cd8308bd2
--- /dev/null
+++ b/src/org/fdroid/fdroid/data/FDroidProvider.java
@@ -0,0 +1,59 @@
+package org.fdroid.fdroid.data;
+
+import android.content.ContentProvider;
+import android.content.UriMatcher;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+
+abstract class FDroidProvider extends ContentProvider {
+
+ public static final String AUTHORITY = "org.fdroid.fdroid.data";
+
+ protected static final int CODE_LIST = 1;
+ protected static final int CODE_SINGLE = 2;
+
+ private DBHelper dbHelper;
+
+ abstract protected String getTableName();
+
+ abstract protected String getProviderName();
+
+ @Override
+ public boolean onCreate() {
+ dbHelper = new DBHelper(getContext());
+ return true;
+ }
+
+ protected final DBHelper db() {
+ return dbHelper;
+ }
+
+ protected final SQLiteDatabase read() {
+ return db().getReadableDatabase();
+ }
+
+ protected final SQLiteDatabase write() {
+ return db().getWritableDatabase();
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ String type;
+ switch(getMatcher().match(uri)) {
+ case CODE_LIST:
+ type = "dir";
+ break;
+
+ case CODE_SINGLE:
+ default:
+ type = "item";
+ break;
+
+ }
+ return "vnd.android.cursor." + type + "/vnd." + AUTHORITY + "." + getProviderName();
+ }
+
+ abstract protected UriMatcher getMatcher();
+
+}
+
diff --git a/src/org/fdroid/fdroid/data/Repo.java b/src/org/fdroid/fdroid/data/Repo.java
new file mode 100644
index 000000000..ca01bde09
--- /dev/null
+++ b/src/org/fdroid/fdroid/data/Repo.java
@@ -0,0 +1,176 @@
+package org.fdroid.fdroid.data;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.util.Log;
+import org.fdroid.fdroid.DB;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Date;
+
+public class Repo {
+
+ private long id;
+
+ public String address;
+ public String name;
+ public String description;
+ public int version; // index version, i.e. what fdroidserver built it - 0 if not specified
+ public boolean inuse;
+ public int priority;
+ public String pubkey; // null for an unsigned repo
+ public String fingerprint; // always null for an unsigned repo
+ public int maxage; // maximum age of index that will be accepted - 0 for any
+ public String lastetag; // last etag we updated from, null forces update
+ public Date lastUpdated;
+
+ public Repo() {
+
+ }
+
+ public Repo(Cursor cursor) {
+ for(int i = 0; i < cursor.getColumnCount(); i ++ ) {
+ String column = cursor.getColumnName(i);
+ if (column.equals(RepoProvider.DataColumns._ID)) {
+ id = cursor.getInt(i);
+ } else if (column.equals(RepoProvider.DataColumns.LAST_ETAG)) {
+ lastetag = cursor.getString(i);
+ } else if (column.equals(RepoProvider.DataColumns.ADDRESS)) {
+ address = cursor.getString(i);
+ } else if (column.equals(RepoProvider.DataColumns.DESCRIPTION)) {
+ description = cursor.getString(i);
+ } else if (column.equals(RepoProvider.DataColumns.FINGERPRINT)) {
+ fingerprint = cursor.getString(i);
+ } else if (column.equals(RepoProvider.DataColumns.IN_USE)) {
+ inuse = cursor.getInt(i) == 1;
+ } else if (column.equals(RepoProvider.DataColumns.LAST_UPDATED)) {
+ String dateString = 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)) {
+ maxage = cursor.getInt(i);
+ } else if (column.equals(RepoProvider.DataColumns.VERSION)) {
+ version = cursor.getInt(i);
+ } else if (column.equals(RepoProvider.DataColumns.NAME)) {
+ name = cursor.getString(i);
+ } else if (column.equals(RepoProvider.DataColumns.PUBLIC_KEY)) {
+ pubkey = cursor.getString(i);
+ } else if (column.equals(RepoProvider.DataColumns.PRIORITY)) {
+ priority = cursor.getInt(i);
+ }
+ }
+ }
+
+ public long getId() { return id; }
+
+ public String getName() {
+ return name;
+ }
+
+ public String toString() {
+ return address;
+ }
+
+ public int getNumberOfApps() {
+ DB db = DB.getDB();
+ int count = db.countAppsForRepo(id);
+ DB.releaseDB();
+ return count;
+ }
+
+ public boolean isSigned() {
+ return this.pubkey != null && this.pubkey.length() > 0;
+ }
+
+ public boolean hasBeenUpdated() {
+ return this.lastetag != null;
+ }
+ /**
+ * If we haven't run an update for this repo yet, then the name
+ * will be unknown, in which case we will just take a guess at an
+ * appropriate name based on the url (e.g. "fdroid.org/archive")
+ */
+ public static String addressToName(String address) {
+ String tempName;
+ try {
+ URL url = new URL(address);
+ tempName = url.getHost() + url.getPath();
+ } catch (MalformedURLException e) {
+ tempName = address;
+ }
+ return tempName;
+ }
+
+ private static int toInt(Integer value) {
+ if (value == null) {
+ return 0;
+ } else {
+ return value;
+ }
+ }
+
+ public void setValues(ContentValues values) {
+
+ if (values.containsKey(RepoProvider.DataColumns._ID)) {
+ id = toInt(values.getAsInteger(RepoProvider.DataColumns._ID));
+ }
+
+ if (values.containsKey(RepoProvider.DataColumns.LAST_ETAG)) {
+ lastetag = values.getAsString(RepoProvider.DataColumns.LAST_ETAG);
+ }
+
+ if (values.containsKey(RepoProvider.DataColumns.ADDRESS)) {
+ address = values.getAsString(RepoProvider.DataColumns.ADDRESS);
+ }
+
+ if (values.containsKey(RepoProvider.DataColumns.DESCRIPTION)) {
+ description = values.getAsString(RepoProvider.DataColumns.DESCRIPTION);
+ }
+
+ if (values.containsKey(RepoProvider.DataColumns.FINGERPRINT)) {
+ fingerprint = values.getAsString(RepoProvider.DataColumns.FINGERPRINT);
+ }
+
+ if (values.containsKey(RepoProvider.DataColumns.IN_USE)) {
+ inuse = toInt(values.getAsInteger(RepoProvider.DataColumns.FINGERPRINT)) == 1;
+ }
+
+ if (values.containsKey(RepoProvider.DataColumns.LAST_UPDATED)) {
+ String dateString = values.getAsString(RepoProvider.DataColumns.LAST_UPDATED);
+ if (dateString != null) {
+ try {
+ lastUpdated = DB.DATE_FORMAT.parse(dateString);
+ } catch (ParseException e) {
+ Log.e("FDroid", "Error parsing date " + dateString);
+ }
+ }
+ }
+
+ if (values.containsKey(RepoProvider.DataColumns.MAX_AGE)) {
+ maxage = toInt(values.getAsInteger(RepoProvider.DataColumns.MAX_AGE));
+ }
+
+ if (values.containsKey(RepoProvider.DataColumns.VERSION)) {
+ version = toInt(values.getAsInteger(RepoProvider.DataColumns.VERSION));
+ }
+
+ if (values.containsKey(RepoProvider.DataColumns.NAME)) {
+ name = values.getAsString(RepoProvider.DataColumns.NAME);
+ }
+
+ if (values.containsKey(RepoProvider.DataColumns.PUBLIC_KEY)) {
+ pubkey = values.getAsString(RepoProvider.DataColumns.PUBLIC_KEY);
+ }
+
+ if (values.containsKey(RepoProvider.DataColumns.PRIORITY)) {
+ priority = toInt(values.getAsInteger(RepoProvider.DataColumns.PRIORITY));
+ }
+ }
+}
diff --git a/src/org/fdroid/fdroid/data/RepoProvider.java b/src/org/fdroid/fdroid/data/RepoProvider.java
new file mode 100644
index 000000000..8232d7cf9
--- /dev/null
+++ b/src/org/fdroid/fdroid/data/RepoProvider.java
@@ -0,0 +1,294 @@
+package org.fdroid.fdroid.data;
+
+import android.content.*;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.text.TextUtils;
+import android.util.Log;
+import org.fdroid.fdroid.DB;
+import org.fdroid.fdroid.FDroidApp;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RepoProvider extends FDroidProvider {
+
+ public static final class Helper {
+
+ private Helper() {}
+
+ public static Repo findById(ContentResolver resolver, long repoId) {
+ return findById(resolver, repoId, DataColumns.ALL);
+ }
+
+ public static Repo findById(ContentResolver resolver, long repoId,
+ String[] projection) {
+ Uri uri = RepoProvider.getContentUri(repoId);
+ Cursor cursor = resolver.query(uri, projection, null, null, null);
+ Repo repo = null;
+ if (cursor != null) {
+ cursor.moveToFirst();
+ repo = new Repo(cursor);
+ }
+ return repo;
+ }
+
+ public static Repo findByAddress(ContentResolver resolver,
+ String address) {
+ return findByAddress(resolver, address, DataColumns.ALL);
+ }
+
+ public static Repo findByAddress(ContentResolver resolver,
+ String address, String[] projection) {
+ List repos = findBy(
+ resolver, DataColumns.ADDRESS, address, projection);
+ return repos.size() > 0 ? repos.get(0) : null;
+ }
+
+ public static List all(ContentResolver resolver) {
+ return all(resolver, DataColumns.ALL);
+ }
+
+ public static List all(ContentResolver resolver, String[] projection) {
+ Uri uri = RepoProvider.getContentUri();
+ Cursor cursor = resolver.query(uri, projection, null, null, null);
+ return cursorToList(cursor);
+ }
+
+ private static List findBy(ContentResolver resolver,
+ String fieldName,
+ String fieldValue,
+ String[] projection) {
+ Uri uri = RepoProvider.getContentUri();
+ String[] args = { fieldValue };
+ Cursor cursor = resolver.query(
+ uri, projection, fieldName + " = ?", args, null );
+ return cursorToList(cursor);
+ }
+
+ private static List cursorToList(Cursor cursor) {
+ List repos = new ArrayList();
+ if (cursor != null) {
+ cursor.moveToFirst();
+ while (!cursor.isAfterLast()) {
+ repos.add(new Repo(cursor));
+ cursor.moveToNext();
+ }
+ }
+ return repos;
+ }
+
+ public static void update(ContentResolver resolver, Repo repo,
+ ContentValues values) {
+
+ // Change the name to the new address. Next time we update the repo
+ // index file, it will populate the name field with the proper
+ // name, but the best we can do is guess right now.
+ if (values.containsKey(DataColumns.ADDRESS) &&
+ !values.containsKey(DataColumns.NAME)) {
+ String name = Repo.addressToName(values.getAsString(DataColumns.ADDRESS));
+ values.put(DataColumns.NAME, name);
+ }
+
+ // Recalculate the fingerprint if we are saving a public key (it is
+ // probably a new public key. If not, we will get the same
+ // fingerprint anyhow).
+ if (values.containsKey(DataColumns.PUBLIC_KEY) &&
+ values.containsKey(DataColumns.FINGERPRINT)) {
+
+ String publicKey = values.getAsString(DataColumns.PUBLIC_KEY);
+ String fingerprint = values.getAsString(DataColumns.FINGERPRINT);
+ if (publicKey != null && fingerprint == null) {
+ values.put(DataColumns.FINGERPRINT, DB.calcFingerprint(publicKey));
+ }
+ }
+
+ if (values.containsKey(DataColumns.IN_USE)) {
+ Integer inUse = values.getAsInteger(DataColumns.IN_USE);
+ if (inUse != null && inUse == 0) {
+ values.put(DataColumns.LAST_ETAG, (String)null);
+ }
+ }
+
+ Uri uri = getContentUri(repo.getId());
+ String[] args = { Long.toString(repo.getId()) };
+ resolver.update(uri, values, DataColumns._ID + " = ?", args );
+ repo.setValues(values);
+ }
+
+ /**
+ * 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(ContentResolver resolver,
+ ContentValues values) {
+ Uri uri = RepoProvider.getContentUri();
+ resolver.insert(uri, values);
+ }
+
+ public static void remove(ContentResolver resolver, long repoId) {
+ Uri uri = RepoProvider.getContentUri(repoId);
+ resolver.delete(uri, null, null);
+ }
+
+ public static void purgeApps(Repo repo, FDroidApp app) {
+ // TODO: Once we have content providers for apps and apks, use them
+ // to do this...
+ DB db = DB.getDB();
+ try {
+ db.purgeApps(repo, app);
+ } finally {
+ DB.releaseDB();
+ }
+ }
+
+ }
+
+ public interface DataColumns extends BaseColumns {
+ public static String ADDRESS = "address";
+ public static String NAME = "name";
+ public static String DESCRIPTION = "description";
+ public static String IN_USE = "inuse";
+ public static String PRIORITY = "priority";
+ public static String PUBLIC_KEY = "pubkey";
+ public static String FINGERPRINT = "fingerprint";
+ public static String MAX_AGE = "maxage";
+ public static String LAST_ETAG = "lastetag";
+ public static String LAST_UPDATED = "lastUpdated";
+ public static String VERSION = "version";
+
+ public static String[] ALL = {
+ _ID, ADDRESS, NAME, DESCRIPTION, IN_USE, PRIORITY, PUBLIC_KEY,
+ FINGERPRINT, MAX_AGE, LAST_UPDATED, LAST_ETAG, VERSION
+ };
+ }
+
+ private static final String PROVIDER_NAME = "RepoProvider";
+
+ private static final UriMatcher matcher = new UriMatcher(-1);
+
+ static {
+ matcher.addURI(AUTHORITY, PROVIDER_NAME, CODE_LIST);
+ matcher.addURI(AUTHORITY, PROVIDER_NAME + "/#", CODE_SINGLE);
+ }
+
+ public static Uri getContentUri() {
+ return Uri.parse("content://" + AUTHORITY + "/" + PROVIDER_NAME);
+ }
+
+ public static Uri getContentUri(long repoId) {
+ return ContentUris.withAppendedId(getContentUri(), repoId);
+ }
+
+ @Override
+ protected String getTableName() {
+ return DBHelper.TABLE_REPO;
+ }
+
+ @Override
+ protected String getProviderName() {
+ return "RepoProvider";
+ }
+
+ protected UriMatcher getMatcher() {
+ return matcher;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+
+ switch (matcher.match(uri)) {
+ case CODE_LIST:
+ if (TextUtils.isEmpty(sortOrder)) {
+ sortOrder = "_ID ASC";
+ }
+ break;
+
+ case CODE_SINGLE:
+ selection = ( selection == null ? "" : selection ) +
+ "_ID = " + uri.getLastPathSegment();
+ break;
+
+ default:
+ Log.e("FDroid", "Invalid URI for repo content provider: " + uri);
+ throw new UnsupportedOperationException("Invalid URI for repo content provider: " + uri);
+ }
+
+ Cursor cursor = read().query(getTableName(), projection, selection,
+ selectionArgs, null, null, sortOrder);
+ cursor.setNotificationUri(getContext().getContentResolver(), uri);
+ return cursor;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+
+ if (!values.containsKey(DataColumns.ADDRESS)) {
+ throw new UnsupportedOperationException("Cannot add repo without an address.");
+ }
+
+ // The following fields have NOT NULL constraints in the DB, so need
+ // to be present.
+
+ if (!values.containsKey(DataColumns.IN_USE)) {
+ values.put(DataColumns.IN_USE, 1);
+ }
+
+ if (!values.containsKey(DataColumns.PRIORITY)) {
+ values.put(DataColumns.PRIORITY, 10);
+ }
+
+ if (!values.containsKey(DataColumns.MAX_AGE)) {
+ values.put(DataColumns.MAX_AGE, 0);
+ }
+
+ if (!values.containsKey(DataColumns.VERSION)) {
+ values.put(DataColumns.VERSION, 0);
+ }
+
+ if (!values.containsKey(DataColumns.NAME)) {
+ String address = values.getAsString(DataColumns.ADDRESS);
+ values.put(DataColumns.NAME, Repo.addressToName(address));
+ }
+
+ long id = write().insertOrThrow(getTableName(), null, values);
+ Log.i("FDroid", "Inserted repo. Notifying provider change: '" + uri + "'.");
+ getContext().getContentResolver().notifyChange(uri, null);
+ return getContentUri(id);
+ }
+
+ @Override
+ public int delete(Uri uri, String where, String[] whereArgs) {
+
+ switch (matcher.match(uri)) {
+ case CODE_LIST:
+ // Don't support deleting of multiple repos.
+ return 0;
+
+ case CODE_SINGLE:
+ where = ( where == null ? "" : where ) +
+ "_ID = " + uri.getLastPathSegment();
+ break;
+
+ default:
+ Log.e("FDroid", "Invalid URI for repo content provider: " + uri);
+ throw new UnsupportedOperationException("Invalid URI for repo content provider: " + uri);
+ }
+
+ int rowsAffected = write().delete(getTableName(), where, whereArgs);
+ Log.i("FDroid", "Deleted repos. Notifying provider change: '" + uri + "'.");
+ getContext().getContentResolver().notifyChange(uri, null);
+ return rowsAffected;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
+ int numRows = write().update(getTableName(), values, where, whereArgs);
+ Log.i("FDroid", "Updated repo. Notifying provider change: '" + uri + "'.");
+ getContext().getContentResolver().notifyChange(uri, null);
+ return numRows;
+ }
+
+}
diff --git a/src/org/fdroid/fdroid/updater/RepoUpdater.java b/src/org/fdroid/fdroid/updater/RepoUpdater.java
index 2893495b8..f77bbccbc 100644
--- a/src/org/fdroid/fdroid/updater/RepoUpdater.java
+++ b/src/org/fdroid/fdroid/updater/RepoUpdater.java
@@ -1,5 +1,6 @@
package org.fdroid.fdroid.updater;
+import android.content.ContentValues;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
@@ -7,6 +8,8 @@ import org.fdroid.fdroid.DB;
import org.fdroid.fdroid.ProgressListener;
import org.fdroid.fdroid.RepoXMLHandler;
import org.fdroid.fdroid.Utils;
+import org.fdroid.fdroid.data.Repo;
+import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.net.Downloader;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
@@ -18,6 +21,7 @@ import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.*;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
abstract public class RepoUpdater {
@@ -26,7 +30,7 @@ abstract public class RepoUpdater {
public static final int PROGRESS_TYPE_PROCESS_XML = 2;
public static final String PROGRESS_DATA_REPO = "repo";
- public static RepoUpdater createUpdaterFor(Context ctx, DB.Repo repo) {
+ public static RepoUpdater createUpdaterFor(Context ctx, Repo repo) {
if (repo.pubkey != null) {
return new SignedRepoUpdater(ctx, repo);
} else {
@@ -35,12 +39,12 @@ abstract public class RepoUpdater {
}
protected final Context context;
- protected final DB.Repo repo;
+ protected final Repo repo;
protected final List apps = new ArrayList();
protected boolean hasChanged = false;
protected ProgressListener progressListener;
- public RepoUpdater(Context ctx, DB.Repo repo) {
+ public RepoUpdater(Context ctx, Repo repo) {
this.context = ctx;
this.repo = repo;
}
@@ -164,8 +168,6 @@ abstract public class RepoUpdater {
if (hasChanged) {
downloadedFile = downloader.getFile();
- repo.lastetag = downloader.getETag();
-
indexFile = getIndexFromFile(downloadedFile);
// Process the index...
@@ -184,7 +186,7 @@ abstract public class RepoUpdater {
new BufferedReader(new FileReader(indexFile)));
reader.parse(is);
- updateRepo(handler);
+ updateRepo(handler, downloader.getETag());
}
} catch (SAXException e) {
throw new UpdateException(
@@ -210,9 +212,15 @@ abstract public class RepoUpdater {
}
}
- private void updateRepo(RepoXMLHandler handler) {
+ private void updateRepo(RepoXMLHandler handler, String etag) {
- boolean repoChanged = false;
+ ContentValues values = new ContentValues();
+
+ values.put(RepoProvider.DataColumns.LAST_UPDATED, DB.DATE_FORMAT.format(new Date()));
+
+ if (repo.lastetag == null || !repo.lastetag.equals(etag)) {
+ values.put(RepoProvider.DataColumns.LAST_ETAG, etag);
+ }
// We read an unsigned index, but that indicates that
// a signed version is now available...
@@ -224,54 +232,42 @@ abstract public class RepoUpdater {
// information as the unsigned one does not...
Log.d("FDroid",
"Public key found - switching to signed repo for future updates");
- repo.pubkey = handler.getPubKey();
- repoChanged = true;
+ values.put(RepoProvider.DataColumns.PUBLIC_KEY, handler.getPubKey());
}
if (handler.getVersion() != -1 && handler.getVersion() != repo.version) {
Log.d("FDroid", "Repo specified a new version: from "
+ repo.version + " to " + handler.getVersion());
- repo.version = handler.getVersion();
- repoChanged = true;
+ values.put(RepoProvider.DataColumns.VERSION, handler.getVersion());
}
if (handler.getMaxAge() != -1 && handler.getMaxAge() != repo.maxage) {
Log.d("FDroid",
"Repo specified a new maximum age - updated");
- repo.maxage = handler.getMaxAge();
- repoChanged = true;
+ values.put(RepoProvider.DataColumns.MAX_AGE, handler.getMaxAge());
}
if (handler.getDescription() != null && !handler.getDescription().equals(repo.description)) {
- repo.description = handler.getDescription();
- repoChanged = true;
+ values.put(RepoProvider.DataColumns.DESCRIPTION, handler.getDescription());
}
if (handler.getName() != null && !handler.getName().equals(repo.name)) {
- repo.name = handler.getName();
- repoChanged = true;
+ values.put(RepoProvider.DataColumns.NAME, handler.getName());
}
- if (repoChanged) {
- try {
- DB db = DB.getDB();
- db.updateRepoByAddress(repo);
- } finally {
- DB.releaseDB();
- }
- }
+ RepoProvider.Helper.update(context.getContentResolver(), repo, values);
}
public static class UpdateException extends Exception {
- public final DB.Repo repo;
+ public final Repo repo;
- public UpdateException(DB.Repo repo, String message) {
+ public UpdateException(Repo repo, String message) {
super(message);
this.repo = repo;
}
- public UpdateException(DB.Repo repo, String message, Exception cause) {
+ public UpdateException(Repo repo, String message, Exception cause) {
super(message, cause);
this.repo = repo;
}
diff --git a/src/org/fdroid/fdroid/updater/SignedRepoUpdater.java b/src/org/fdroid/fdroid/updater/SignedRepoUpdater.java
index 0097921d4..8f3f75aaa 100644
--- a/src/org/fdroid/fdroid/updater/SignedRepoUpdater.java
+++ b/src/org/fdroid/fdroid/updater/SignedRepoUpdater.java
@@ -6,6 +6,7 @@ import org.fdroid.fdroid.DB;
import org.fdroid.fdroid.Hasher;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils;
+import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.net.Downloader;
import java.io.*;
@@ -16,7 +17,7 @@ import java.util.jar.JarFile;
public class SignedRepoUpdater extends RepoUpdater {
- public SignedRepoUpdater(Context ctx, DB.Repo repo) {
+ public SignedRepoUpdater(Context ctx, Repo repo) {
super(ctx, repo);
}
diff --git a/src/org/fdroid/fdroid/updater/UnsignedRepoUpdater.java b/src/org/fdroid/fdroid/updater/UnsignedRepoUpdater.java
index 6fd6d5699..ec9b0712b 100644
--- a/src/org/fdroid/fdroid/updater/UnsignedRepoUpdater.java
+++ b/src/org/fdroid/fdroid/updater/UnsignedRepoUpdater.java
@@ -3,13 +3,14 @@ package org.fdroid.fdroid.updater;
import android.content.Context;
import android.util.Log;
import org.fdroid.fdroid.DB;
+import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.net.Downloader;
import java.io.File;
public class UnsignedRepoUpdater extends RepoUpdater {
- public UnsignedRepoUpdater(Context ctx, DB.Repo repo) {
+ public UnsignedRepoUpdater(Context ctx, Repo repo) {
super(ctx, repo);
}
diff --git a/src/org/fdroid/fdroid/views/RepoAdapter.java b/src/org/fdroid/fdroid/views/RepoAdapter.java
index 76a5c848e..3fcb3c3aa 100644
--- a/src/org/fdroid/fdroid/views/RepoAdapter.java
+++ b/src/org/fdroid/fdroid/views/RepoAdapter.java
@@ -1,34 +1,48 @@
package org.fdroid.fdroid.views;
+import android.content.Context;
+import android.database.Cursor;
+import android.support.v4.widget.CursorAdapter;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.*;
-import org.fdroid.fdroid.DB;
-import org.fdroid.fdroid.ManageRepo;
+import android.widget.CompoundButton;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.compat.SwitchCompat;
+import org.fdroid.fdroid.data.Repo;
-import java.util.List;
+public class RepoAdapter extends CursorAdapter {
-public class RepoAdapter extends BaseAdapter {
-
- private List repositories;
- private final ManageRepo activity;
-
- public RepoAdapter(ManageRepo activity) {
- this.activity = activity;
- refresh();
+ public interface EnabledListener {
+ public void onSetEnabled(Repo repo, boolean isEnabled);
}
- public void refresh() {
- try {
- DB db = DB.getDB();
- repositories = db.getRepos();
- } finally {
- DB.releaseDB();
- }
- notifyDataSetChanged();
+ private static final int SWITCH_ID = 10000;
+
+ private final LayoutInflater inflater;
+
+ private EnabledListener enabledListener;
+
+ public RepoAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+ inflater = LayoutInflater.from(context);
+ }
+
+ public RepoAdapter(Context context, Cursor c, boolean autoRequery) {
+ super(context, c, autoRequery);
+ inflater = LayoutInflater.from(context);
+ }
+
+ public RepoAdapter(Context context, Cursor c) {
+ super(context, c);
+ inflater = LayoutInflater.from(context);
+ }
+
+ public void setEnabledListener(EnabledListener listener) {
+ enabledListener = listener;
}
public boolean hasStableIds() {
@@ -36,54 +50,45 @@ public class RepoAdapter extends BaseAdapter {
}
@Override
- public int getCount() {
- return repositories.size();
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ View view = inflater.inflate(R.layout.repo_item, null);
+ CompoundButton switchView = addSwitchToView(view, context);
+ setupView(cursor, view, switchView);
+ return view;
}
@Override
- public Object getItem(int position) {
- return repositories.get(position);
+ public void bindView(View view, Context context, Cursor cursor) {
+ CompoundButton switchView = (CompoundButton)view.findViewById(SWITCH_ID);
+
+ // Remove old listener (because we are reusing this view, we don't want
+ // to invoke the listener for the last repo to use it - particularly
+ // because we are potentially about to change the checked status
+ // which would in turn invoke this listener....
+ switchView.setOnCheckedChangeListener(null);
+ setupView(cursor, view, switchView);
}
- @Override
- public long getItemId(int position) {
- return getItem(position).hashCode();
- }
- private static final int SWITCH_ID = 10000;
+ private void setupView(Cursor cursor, View view, CompoundButton switchView) {
- @Override
- public View getView(int position, View view, ViewGroup parent) {
+ final Repo repo = new Repo(cursor);
- final DB.Repo repository = repositories.get(position);
-
- CompoundButton switchView;
- if (view == null) {
- view = activity.getLayoutInflater().inflate(R.layout.repo_item,null);
- switchView = addSwitchToView(view);
- } else {
- switchView = (CompoundButton)view.findViewById(SWITCH_ID);
-
- // Remove old listener (because we are reusing this view, we don't want
- // to invoke the listener for the last repo to use it - particularly
- // because we are potentially about to change the checked status
- // which would in turn invoke this listener....
- switchView.setOnCheckedChangeListener(null);
- }
-
- switchView.setChecked(repository.inuse);
+ switchView.setChecked(repo.inuse);
// Add this listener *after* setting the checked status, so we don't
// invoke the listener while setting up the view...
switchView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- activity.setRepoEnabled(repository, isChecked);
+ if (enabledListener != null) {
+ enabledListener.onSetEnabled(repo, isChecked);
+ }
}
});
TextView nameView = (TextView)view.findViewById(R.id.repo_name);
- nameView.setText(repository.getName());
+ nameView.setText(repo.getName());
RelativeLayout.LayoutParams nameViewLayout =
(RelativeLayout.LayoutParams)nameView.getLayoutParams();
nameViewLayout.addRule(RelativeLayout.LEFT_OF, switchView.getId());
@@ -91,17 +96,15 @@ public class RepoAdapter extends BaseAdapter {
// If we set the signed view to GONE instead of INVISIBLE, then the
// height of each list item varies.
View signedView = view.findViewById(R.id.repo_unsigned);
- if (repository.isSigned()) {
+ if (repo.isSigned()) {
signedView.setVisibility(View.INVISIBLE);
} else {
signedView.setVisibility(View.VISIBLE);
}
-
- return view;
}
- private CompoundButton addSwitchToView(View parent) {
- SwitchCompat switchBuilder = SwitchCompat.create(activity);
+ private CompoundButton addSwitchToView(View parent, Context context) {
+ SwitchCompat switchBuilder = SwitchCompat.create(context);
CompoundButton switchView = switchBuilder.createSwitch();
switchView.setId(SWITCH_ID);
RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(
diff --git a/src/org/fdroid/fdroid/views/RepoDetailsActivity.java b/src/org/fdroid/fdroid/views/RepoDetailsActivity.java
index edf438d53..b0d4ca81f 100644
--- a/src/org/fdroid/fdroid/views/RepoDetailsActivity.java
+++ b/src/org/fdroid/fdroid/views/RepoDetailsActivity.java
@@ -1,32 +1,23 @@
package org.fdroid.fdroid.views;
-import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
-import org.fdroid.fdroid.DB;
-import org.fdroid.fdroid.DB.Repo;
import org.fdroid.fdroid.compat.ActionBarCompat;
+import org.fdroid.fdroid.data.Repo;
+import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.views.fragments.RepoDetailsFragment;
-public class RepoDetailsActivity extends FragmentActivity implements RepoDetailsFragment.OnRepoChangeListener {
-
- public static final String ACTION_IS_DELETED = "isDeleted";
- public static final String ACTION_IS_ENABLED = "isEnabled";
- public static final String ACTION_IS_DISABLED = "isDisabled";
- public static final String ACTION_IS_CHANGED = "isChanged";
-
- public static final String DATA_REPO_ID = "repoId";
-
- private int repoId;
+public class RepoDetailsActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ long repoId = getIntent().getLongExtra(RepoDetailsFragment.ARG_REPO_ID, 0);
+
if (savedInstanceState == null) {
- RepoDetailsFragment fragment = new RepoDetailsFragment();
- fragment.setRepoChangeListener(this);
+ RepoDetailsFragment fragment = new RepoDetailsFragment(repoId);
fragment.setArguments(getIntent().getExtras());
getSupportFragmentManager()
.beginTransaction()
@@ -34,48 +25,11 @@ public class RepoDetailsActivity extends FragmentActivity implements RepoDetails
.commit();
}
- repoId = getIntent().getIntExtra(RepoDetailsFragment.ARG_REPO_ID, -1);
+ String[] projection = new String[] { RepoProvider.DataColumns.NAME };
+ Repo repo = RepoProvider.Helper.findById(getContentResolver(), repoId, projection);
- DB db = DB.getDB();
- Repo repo = db.getRepo(repoId);
- DB.releaseDB();
-
- ActionBarCompat abCompat = ActionBarCompat.create(this);
- abCompat.setDisplayHomeAsUpEnabled(true);
+ ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true);
setTitle(repo.getName());
}
- private void finishWithAction(String actionName) {
- Intent data = new Intent();
- data.putExtra(actionName, true);
- data.putExtra(DATA_REPO_ID, repoId);
- setResult(RESULT_OK, data);
- finish();
- }
-
- @Override
- public void onDeleteRepo(DB.Repo repo) {
- finishWithAction(ACTION_IS_DELETED);
- }
-
- @Override
- public void onRepoDetailsChanged(DB.Repo repo) {
- // Do nothing...
- }
-
- @Override
- public void onEnableRepo(DB.Repo repo) {
- finishWithAction(ACTION_IS_ENABLED);
- }
-
- @Override
- public void onDisableRepo(DB.Repo repo) {
- finishWithAction(ACTION_IS_DISABLED);
- }
-
- @Override
- public void onUpdatePerformed(DB.Repo repo) {
- // do nothing - the actual update is done by the repo fragment...
- }
-
}
diff --git a/src/org/fdroid/fdroid/views/fragments/RepoDetailsFragment.java b/src/org/fdroid/fdroid/views/fragments/RepoDetailsFragment.java
index 96d2b3d45..76995486a 100644
--- a/src/org/fdroid/fdroid/views/fragments/RepoDetailsFragment.java
+++ b/src/org/fdroid/fdroid/views/fragments/RepoDetailsFragment.java
@@ -2,8 +2,12 @@ package org.fdroid.fdroid.views.fragments;
import android.app.Activity;
import android.app.AlertDialog;
+import android.content.ContentValues;
import android.content.DialogInterface;
+import android.database.ContentObserver;
+import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.text.Editable;
@@ -12,8 +16,8 @@ import android.util.Log;
import android.view.*;
import android.widget.*;
import org.fdroid.fdroid.*;
-
-import java.util.List;
+import org.fdroid.fdroid.data.Repo;
+import org.fdroid.fdroid.data.RepoProvider;
public class RepoDetailsFragment extends Fragment {
@@ -49,34 +53,15 @@ public class RepoDetailsFragment extends Fragment {
private static final int DELETE = 0;
private static final int UPDATE = 1;
- public void setRepoChangeListener(OnRepoChangeListener listener) {
- repoChangeListener = listener;
- }
-
- private OnRepoChangeListener repoChangeListener;
-
- public static interface OnRepoChangeListener {
-
- /**
- * This fragment is responsible for getting confirmation from the
- * user, so you should presume that the user has already consented
- * and confirmed to the deletion.
- */
- public void onDeleteRepo(DB.Repo repo);
-
- public void onRepoDetailsChanged(DB.Repo repo);
-
- public void onEnableRepo(DB.Repo repo);
-
- public void onDisableRepo(DB.Repo repo);
-
- public void onUpdatePerformed(DB.Repo repo);
+ private final long repoId;
+ public RepoDetailsFragment(long repoId) {
+ this.repoId = repoId;
}
// TODO: Currently initialised in onCreateView. Not sure if that is the
// best way to go about this...
- private DB.Repo repo;
+ private Repo repo;
public void onAttach(Activity activity) {
super.onAttach(activity);
@@ -88,20 +73,13 @@ public class RepoDetailsFragment extends Fragment {
* have been updated. The safest way to deal with this is to reload the
* repo object directly from the database.
*/
- private void reloadRepoDetails() {
- try {
- DB db = DB.getDB();
- repo = db.getRepo(repo.id);
- } finally {
- DB.releaseDB();
- }
+ private Repo loadRepoDetails() {
+ return RepoProvider.Helper.findById(getActivity().getContentResolver(), repoId);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- int repoId = getArguments().getInt(ARG_REPO_ID);
- DB db = DB.getDB();
- repo = db.getRepo(repoId);
- DB.releaseDB();
+
+ repo = loadRepoDetails();
if (repo == null) {
Log.e("FDroid", "Error showing details for repo '" + repoId + "'");
@@ -186,7 +164,7 @@ public class RepoDetailsFragment extends Fragment {
lastUpdated.setText(lastUpdate);
}
- private void setupDescription(ViewGroup parent, DB.Repo repo) {
+ private void setupDescription(ViewGroup parent, Repo repo) {
TextView descriptionLabel = (TextView)parent.findViewById(R.id.label_description);
TextView description = (TextView)parent.findViewById(R.id.text_description);
@@ -210,20 +188,20 @@ public class RepoDetailsFragment extends Fragment {
* list can be updated. We will perform the update ourselves though.
*/
private void performUpdate() {
- repo.enable((FDroidApp)getActivity().getApplication());
+ // Ensure repo is enabled before updating...
+ ContentValues values = new ContentValues(1);
+ values.put(RepoProvider.DataColumns.IN_USE, 1);
+ RepoProvider.Helper.update(getActivity().getContentResolver(), repo, values);
+
UpdateService.updateRepoNow(repo.address, getActivity()).setListener(new ProgressListener() {
@Override
public void onProgress(Event event) {
- if (event.type == UpdateService.STATUS_COMPLETE_AND_SAME ||
- event.type == UpdateService.STATUS_COMPLETE_WITH_CHANGES) {
- reloadRepoDetails();
+ if (event.type == UpdateService.STATUS_COMPLETE_WITH_CHANGES) {
+ repo = loadRepoDetails();
updateView((ViewGroup)getView());
}
}
});
- if (repoChangeListener != null) {
- repoChangeListener.onUpdatePerformed(repo);
- }
}
/**
@@ -238,18 +216,15 @@ public class RepoDetailsFragment extends Fragment {
public void afterTextChanged(Editable s) {}
@Override
+ // TODO: This is called each character change, resulting in a DB query.
+ // Doesn't exactly cause performance problems,
+ // but seems silly not to go for more of a "focus out" event then
+ // this "text changed" event.
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (!repo.address.equals(s.toString())) {
- repo.address = s.toString();
- try {
- DB db = DB.getDB();
- db.updateRepo(repo);
- } finally {
- DB.releaseDB();
- }
- if (repoChangeListener != null) {
- repoChangeListener.onRepoDetailsChanged(repo);
- }
+ ContentValues values = new ContentValues(1);
+ values.put(RepoProvider.DataColumns.ADDRESS, s.toString());
+ RepoProvider.Helper.update(getActivity().getContentResolver(), repo, values);
}
}
}
@@ -293,24 +268,23 @@ public class RepoDetailsFragment extends Fragment {
.setIcon(android.R.drawable.ic_menu_delete)
.setMessage(R.string.repo_confirm_delete_body)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (repoChangeListener != null) {
- DB.Repo repo = RepoDetailsFragment.this.repo;
- repoChangeListener.onDeleteRepo(repo);
- }
- }
- }).setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- // Do nothing...
+ Repo repo = RepoDetailsFragment.this.repo;
+ RepoProvider.Helper.remove(getActivity().getContentResolver(), repo.getId());
+ getActivity().finish();
+ }
+ }).setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // Do nothing...
+ }
}
- }
).show();
}
- private void setupRepoFingerprint(ViewGroup parent, DB.Repo repo) {
+ private void setupRepoFingerprint(ViewGroup parent, Repo repo) {
TextView repoFingerprintView = (TextView)parent.findViewById(R.id.text_repo_fingerprint);
TextView repoFingerprintDescView = (TextView)parent.findViewById(R.id.text_repo_fingerprint_description);