diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 38a4c6d29..59220288c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -62,6 +62,9 @@
+
+ android:text="https://"/>
+
+
+
+
+
+
diff --git a/res/layout/repodetails.xml b/res/layout/repodetails.xml
new file mode 100644
index 000000000..f888f1142
--- /dev/null
+++ b/res/layout/repodetails.xml
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/repolisticons.xml b/res/layout/repolisticons.xml
index 07f833053..edd4c0147 100644
--- a/res/layout/repolisticons.xml
+++ b/res/layout/repolisticons.xml
@@ -4,11 +4,7 @@
android:id="@+id/vw1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal">
-
-
+ android:orientation="horizontal">
+
+
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
new file mode 100644
index 000000000..7cd1486ca
--- /dev/null
+++ b/res/values/colors.xml
@@ -0,0 +1,5 @@
+
+
+ #ffcccccc
+ #ffCC0000
+
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
new file mode 100644
index 000000000..fd954b7fa
--- /dev/null
+++ b/res/values/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 8dp
+ 5dp
+ 5dp
+
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ea58ded89..a9251e5c6 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -11,6 +11,8 @@
%d version availableNotifyStorage
+ Edit
+ DeleteCache downloaded appsKeep downloaded apk files on SD cardUpdates
@@ -103,7 +105,7 @@
Getting application from:\n Update Repos
- Manage Repos
+ RepositoriesPreferencesAboutSearch
@@ -186,5 +188,34 @@
You don\'t have any app installed that can handle %sCompact LayoutOnly show app names and summaries in list
+ Unsigned
+ URL
+ # of apps
+ Signature
+ Description
+ Last update
+ Name
+ This means that the list of
+ applications could not be verified. You should be careful
+ with applications downloaded from unsigned indexes.
+ This repository has not been used yet.
+ In order to view the apps it provides, you will need to update
+ it.\n\nOnce updated, the description and other details will
+ become available here.
+
+ Do you want to delete the \"{0}\"
+ repository, which has {1} apps in it? Any installed apps will NOT be
+ removed, but you will not be able to update them through F-Droid any
+ more.
+
+ Unknown
+ Delete Repository?
+ Deleting a repository means
+ apps from it will no longer be available from F-Droid.\n\nNote: All
+ previously installed apps will remain on your device.
+
+ Disabled "%1$s".\n\nYou will
+ need to re-enable this repository to install apps from it.
+
diff --git a/src/org/fdroid/fdroid/DB.java b/src/org/fdroid/fdroid/DB.java
index 14592a22f..20f7d180e 100644
--- a/src/org/fdroid/fdroid/DB.java
+++ b/src/org/fdroid/fdroid/DB.java
@@ -20,6 +20,9 @@
package org.fdroid.fdroid;
import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
@@ -62,7 +65,7 @@ public class DB {
// Get access to the database. Must be called before any database activity,
// and releaseDB must be called subsequently. Returns null in the event of
// failure.
- static DB getDB() {
+ public static DB getDB() {
try {
dbSync.acquire();
return dbInstance;
@@ -72,7 +75,7 @@ public class DB {
}
// Release database access lock acquired via getDB().
- static void releaseDB() {
+ public static void releaseDB() {
dbSync.release();
}
@@ -374,7 +377,8 @@ public class DB {
private static final String CREATE_TABLE_REPO = "create table "
+ TABLE_REPO + " (id integer primary key, address text not null, "
+ "name text, description text, inuse integer not null, "
- + "priority integer not null, pubkey text, lastetag text);";
+ + "priority integer not null, pubkey text, lastetag text, "
+ + "lastUpdated string);";
public static class Repo {
public int id;
@@ -385,9 +389,103 @@ public class DB {
public int priority;
public String pubkey; // null for an unsigned repo
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;
+ }
}
- private final int DBVersion = 22;
+ private final int DBVersion = 24;
+
+ private int countAppsForRepo(int id) {
+ String[] selection = { "COUNT(distinct id)" };
+ String[] selectionArgs = { Integer.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;
+ }
+ }
private static void createAppApk(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_APP);
@@ -457,6 +555,9 @@ public class DB {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.i("FDroid", "Upgrading database from v" + oldVersion + " v"
+ + newVersion );
+
// Migrate repo list to new structure. (No way to change primary
// key in sqlite - table must be recreated)
if (oldVersion < 20) {
@@ -500,8 +601,8 @@ public class DB {
ContentValues values = new ContentValues();
values.put("name", mContext.getString(R.string.default_repo_name));
values.put("description", mContext.getString(R.string.default_repo_description));
- db.update(TABLE_REPO, values, "address = ?", new String[] {
- mContext.getString(R.string.default_repo_address) });
+ db.update(TABLE_REPO, values, "address = ?", new String[]{
+ mContext.getString(R.string.default_repo_address)});
values.clear();
values.put("name", mContext.getString(R.string.default_repo_name2));
values.put("description", mContext.getString(R.string.default_repo_description2));
@@ -509,6 +610,11 @@ public class DB {
mContext.getString(R.string.default_repo_address2) });
}
+ if (oldVersion < 23) {
+ if (!columnExists(db, TABLE_REPO, "lastUpdated"))
+ db.execSQL("Alter table " + TABLE_REPO + " add column lastUpdated string");
+ }
+
}
}
@@ -594,7 +700,7 @@ public class DB {
List result = new ArrayList();
Cursor c = null;
try {
- c = db.query(true, TABLE_APP, new String[] { "category" },
+ c = db.query(true, TABLE_APP, new String[]{"category"},
null, null, null, null, "category", null);
c.moveToFirst();
while (!c.isAfterLast()) {
@@ -1267,8 +1373,9 @@ public class DB {
Cursor c = null;
try {
c = db.query(TABLE_REPO, new String[] { "address", "name",
- "description", "inuse", "priority", "pubkey", "lastetag" },
- "id = ?", new String[] { Integer.toString(id) }, null, null, null);
+ "description", "inuse", "priority", "pubkey", "lastetag",
+ "lastUpdated" }, "id = ?", new String[] { Integer.toString(id) },
+ null, null, null);
if (!c.moveToFirst())
return null;
Repo repo = new Repo();
@@ -1280,6 +1387,14 @@ public class DB {
repo.priority = c.getInt(4);
repo.pubkey = c.getString(5);
repo.lastetag = c.getString(6);
+
+ try {
+ repo.lastUpdated = c.getString(7) != null ?
+ mDateFormat.parse( c.getString(7)) :
+ null;
+ } catch (ParseException e) {
+ Log.e("FDroid", "Error parsing date " + c.getString(7));
+ }
return repo;
} finally {
if (c != null)
@@ -1292,8 +1407,8 @@ public class DB {
List repos = new ArrayList();
Cursor c = null;
try {
- c = db.query(TABLE_REPO, new String[] { "id", "address", "name",
- "description", "inuse", "priority", "pubkey", "lastetag" },
+ c = db.query(TABLE_REPO, new String[]{"id", "address", "name",
+ "description", "inuse", "priority", "pubkey", "lastetag"},
null, null, null, null, "priority");
c.moveToFirst();
while (!c.isAfterLast()) {
@@ -1318,6 +1433,27 @@ public class DB {
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 = ?",
@@ -1336,9 +1472,20 @@ public class DB {
new String[] { repo.address });
}
+ /**
+ * Updates the lastUpdated time for every enabled repo.
+ */
+ public void refreshLastUpdates() {
+ ContentValues values = new ContentValues();
+ values.put("lastUpdated", mDateFormat.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", mDateFormat.format(new Date()));
db.update(TABLE_REPO, values, "address = ?",
new String[] { repo.address });
}
@@ -1356,18 +1503,23 @@ public class DB {
db.insert(TABLE_REPO, null, values);
}
- public void doDisableRepos(List addresses, boolean remove) {
- if (addresses.isEmpty()) return;
+ public void doDisableRepos(List repos, boolean remove) {
+ if (repos.isEmpty()) return;
db.beginTransaction();
- try {
- for (String address : addresses) {
+ // 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 },
+ c = db.query(TABLE_REPO, new String[]{"id"},
+ "address = ?", new String[]{address},
null, null, null, null);
c.moveToFirst();
if (!c.isAfterLast()) {
@@ -1382,6 +1534,13 @@ public class DB {
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 });
+ }
}
List apps = getAppsBasic(true);
for (App app : apps) {
diff --git a/src/org/fdroid/fdroid/FDroid.java b/src/org/fdroid/fdroid/FDroid.java
index 1476719b9..da54d3a1c 100644
--- a/src/org/fdroid/fdroid/FDroid.java
+++ b/src/org/fdroid/fdroid/FDroid.java
@@ -26,13 +26,10 @@ import android.support.v4.view.MenuItemCompat;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.NotificationManager;
-import android.app.ProgressDialog;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.ResultReceiver;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.util.Log;
@@ -58,8 +55,6 @@ public class FDroid extends FragmentActivity {
private static final int ABOUT = Menu.FIRST + 3;
private static final int SEARCH = Menu.FIRST + 4;
- private ProgressDialog pd;
-
private ViewPager viewPager;
private AppListManager manager = null;
@@ -212,7 +207,7 @@ public class FDroid extends FragmentActivity {
case REQUEST_APPDETAILS:
break;
case REQUEST_MANAGEREPOS:
- if (data.hasExtra("update")) {
+ if (data.hasExtra(ManageRepo.REQUEST_UPDATE)) {
AlertDialog.Builder ask_alrt = new AlertDialog.Builder(this);
ask_alrt.setTitle(getString(R.string.repo_update_title));
ask_alrt.setIcon(android.R.drawable.ic_menu_rotate);
@@ -221,14 +216,14 @@ public class FDroid extends FragmentActivity {
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
- updateRepos();
+ updateRepos();
}
});
ask_alrt.setNegativeButton(getString(R.string.no),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
- return;
+ // do nothing
}
});
AlertDialog alert = ask_alrt.create();
@@ -261,34 +256,6 @@ public class FDroid extends FragmentActivity {
});
}
- // For receiving results from the UpdateService when we've told it to
- // update in response to a user request.
- private class UpdateReceiver extends ResultReceiver {
- public UpdateReceiver(Handler handler) {
- super(handler);
- }
-
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- String message = resultData.getString(UpdateService.RESULT_MESSAGE);
- boolean finished = false;
- if (resultCode == UpdateService.STATUS_ERROR) {
- Toast.makeText(FDroid.this, message, Toast.LENGTH_LONG).show();
- finished = true;
- } else if (resultCode == UpdateService.STATUS_COMPLETE) {
- repopulateViews();
- finished = true;
- } else if (resultCode == UpdateService.STATUS_INFO) {
- pd.setMessage(message);
- }
-
- if (finished && pd.isShowing())
- pd.dismiss();
- }
- }
-
- private UpdateReceiver mUpdateReceiver;
-
/**
* The first time the app is run, we will have an empty app list.
* If this is the case, we will attempt to update with the default repo.
@@ -314,16 +281,7 @@ public class FDroid extends FragmentActivity {
// is told to do the update, which will result in the database changing. The
// UpdateReceiver class should get told when this is finished.
public void updateRepos() {
-
- pd = ProgressDialog.show(this, getString(R.string.process_wait_title),
- getString(R.string.process_update_msg), true, true);
- pd.setIcon(android.R.drawable.ic_dialog_info);
- pd.setCanceledOnTouchOutside(false);
-
- Intent intent = new Intent(this, UpdateService.class);
- mUpdateReceiver = new UpdateReceiver(new Handler());
- intent.putExtra("receiver", mUpdateReceiver);
- startService(intent);
+ UpdateService.updateNow(this);
}
private TabManager getTabManager() {
diff --git a/src/org/fdroid/fdroid/ManageRepo.java b/src/org/fdroid/fdroid/ManageRepo.java
index f8cd95e17..33bc39eb2 100644
--- a/src/org/fdroid/fdroid/ManageRepo.java
+++ b/src/org/fdroid/fdroid/ManageRepo.java
@@ -19,67 +19,49 @@
package org.fdroid.fdroid;
-import java.security.MessageDigest;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Formatter;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.net.MalformedURLException;
+import java.net.URL;
import android.app.AlertDialog;
-import android.app.AlertDialog.Builder;
import android.app.ListActivity;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.os.Bundle;
-import android.preference.PreferenceManager;
import android.support.v4.view.MenuItemCompat;
-import android.text.format.DateFormat;
import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.EditText;
-import android.widget.ListView;
-import android.widget.SimpleAdapter;
-import android.widget.TextView;
+import android.view.*;
+import android.widget.*;
+import org.fdroid.fdroid.compat.ClipboardCompat;
+import org.fdroid.fdroid.views.RepoAdapter;
+import org.fdroid.fdroid.views.RepoDetailsActivity;
+import org.fdroid.fdroid.views.fragments.RepoDetailsFragment;
public class ManageRepo extends ListActivity {
private final int ADD_REPO = 1;
- private final int REM_REPO = 2;
+
+ /**
+ * If we have a new repo added, or the address of a repo has changed, then
+ * we when we're finished, we'll set this boolean to true in the intent
+ * that we finish with, to signify that we want the main list of apps
+ * updated.
+ */
+ public static final String REQUEST_UPDATE = "update";
private boolean changed = false;
- private List repos;
-
- private static List reposToDisable;
- private static List reposToRemove;
-
- public void disableRepo(String address) {
- if (reposToDisable.contains(address)) return;
- reposToDisable.add(address);
- }
-
- public void removeRepo(String address) {
- if (reposToRemove.contains(address)) return;
- reposToRemove.add(address);
- }
-
- public void removeRepos(List addresses) {
- for (String address : addresses)
- removeRepo(address);
- }
+ private RepoAdapter repoAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.repolist);
+ repoAdapter = new RepoAdapter(this);
+ setListAdapter(repoAdapter);
+
+ /*
+ TODO: Find some other way to display this info, now that we use the ListView widgets...
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(getBaseContext());
@@ -95,69 +77,24 @@ public class ManageRepo extends ListActivity {
}
tv_lastCheck.setText(getString(R.string.last_update_check,s_lastUpdateCheck));
- reposToRemove = new ArrayList();
- reposToDisable = new ArrayList();
+ */
}
@Override
protected void onResume() {
-
super.onResume();
- redraw();
- }
-
- private void redraw() {
- try {
- DB db = DB.getDB();
- repos = db.getRepos();
- } finally {
- DB.releaseDB();
- }
-
- List