diff --git a/data/resources/application/en.xml b/data/resources/application/en.xml
index 477c50a36..caf56ad0d 100644
--- a/data/resources/application/en.xml
+++ b/data/resources/application/en.xml
@@ -42,6 +42,8 @@
+
+
@@ -58,6 +60,7 @@
+
@@ -302,6 +305,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/network_custom_catalog_dialog.xml b/res/layout/network_custom_catalog_dialog.xml
new file mode 100644
index 000000000..d5cfb0db0
--- /dev/null
+++ b/res/layout/network_custom_catalog_dialog.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/geometerplus/android/fbreader/network/AuthenticationDialog.java b/src/org/geometerplus/android/fbreader/network/AuthenticationDialog.java
index 95715f5d5..d889a01e0 100644
--- a/src/org/geometerplus/android/fbreader/network/AuthenticationDialog.java
+++ b/src/org/geometerplus/android/fbreader/network/AuthenticationDialog.java
@@ -61,8 +61,21 @@ class AuthenticationDialog extends NetworkDialog {
library.synchronize();
NetworkView.Instance().fireModelChanged();
if (message.what < 0) {
- myErrorMessage = (String) message.obj;
- activity.showDialog(NetworkDialog.DIALOG_AUTHENTICATION);
+ if (message.what == -2) {
+ final ZLResource dialogResource = ZLResource.resource("dialog");
+ final ZLResource boxResource = dialogResource.getResource("networkError");
+ final ZLResource buttonResource = dialogResource.getResource("button");
+ new AlertDialog.Builder(activity)
+ .setTitle(boxResource.getResource("title").getValue())
+ .setMessage((String) message.obj)
+ .setIcon(0)
+ .setPositiveButton(buttonResource.getResource("ok").getValue(), null)
+ .create().show();
+ } else {
+ myErrorMessage = (String) message.obj;
+ activity.showDialog(NetworkDialog.DIALOG_AUTHENTICATION);
+ return;
+ }
} else if (message.what > 0) {
if (myOnSuccessRunnable != null) {
myOnSuccessRunnable.run();
@@ -147,6 +160,11 @@ class AuthenticationDialog extends NetworkDialog {
.setTitle(myResource.getResource("title").getValue())
.setPositiveButton(buttonResource.getResource("ok").getValue(), listener)
.setNegativeButton(buttonResource.getResource("cancel").getValue(), listener)
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ listener.onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
+ }
+ })
.create();
}
diff --git a/src/org/geometerplus/android/fbreader/network/CustomCatalogDialog.java b/src/org/geometerplus/android/fbreader/network/CustomCatalogDialog.java
new file mode 100644
index 000000000..e1b11ec0d
--- /dev/null
+++ b/src/org/geometerplus/android/fbreader/network/CustomCatalogDialog.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2010 Geometer Plus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+package org.geometerplus.android.fbreader.network;
+
+import java.util.HashMap;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.AlertDialog;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.widget.TextView;
+import android.content.DialogInterface;
+
+import org.geometerplus.zlibrary.ui.android.R;
+import org.geometerplus.zlibrary.core.resources.ZLResource;
+
+import org.geometerplus.fbreader.network.*;
+import org.geometerplus.fbreader.network.opds.OPDSLinkReader;
+
+
+class CustomCatalogDialog extends NetworkDialog {
+
+ private String myTitle;
+ private String myUrl;
+ private String mySummary;
+
+ public CustomCatalogDialog() {
+ super("CustomCatalogDialog");
+ }
+
+ private void clearData() {
+ myTitle = myUrl = mySummary = null;
+ }
+
+ public Dialog createDialog(final Activity activity) {
+ final View layout = activity.getLayoutInflater().inflate(R.layout.network_custom_catalog_dialog, null);
+
+ setupLabel(layout, R.id.network_catalog_title_text, "catalogTitle", R.id.network_catalog_title);
+ setupLabel(layout, R.id.network_catalog_url_text, "catalogUrl", R.id.network_catalog_url);
+ setupLabel(layout, R.id.network_catalog_summary_text, "catalogSummary", R.id.network_catalog_summary);
+
+ final Handler handler = new Handler() {
+ public void handleMessage(Message message) {
+ if (!NetworkView.Instance().isInitialized()) {
+ return;
+ }
+ final NetworkLibrary library = NetworkLibrary.Instance();
+ library.invalidate();
+ library.invalidateVisibility();
+ library.synchronize();
+ NetworkView.Instance().fireModelChanged();
+ if (message.what < 0) {
+ if (message.what == -2) {
+ final ZLResource dialogResource = ZLResource.resource("dialog");
+ final ZLResource boxResource = dialogResource.getResource("networkError");
+ final ZLResource buttonResource = dialogResource.getResource("button");
+ new AlertDialog.Builder(activity)
+ .setTitle(boxResource.getResource("title").getValue())
+ .setMessage((String) message.obj)
+ .setIcon(0)
+ .setPositiveButton(buttonResource.getResource("ok").getValue(), null)
+ .create().show();
+ } else {
+ myErrorMessage = (String) message.obj;
+ activity.showDialog(NetworkDialog.DIALOG_CUSTOM_CATALOG);
+ return;
+ }
+ } else if (message.what > 0) {
+ if (myOnSuccessRunnable != null) {
+ myOnSuccessRunnable.run();
+ }
+ }
+ clearData();
+ }
+ };
+
+ final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ AlertDialog alert = (AlertDialog) dialog;
+ myTitle = ((TextView) alert.findViewById(R.id.network_catalog_title)).getText().toString().trim();
+ myUrl = ((TextView) alert.findViewById(R.id.network_catalog_url)).getText().toString().trim();
+ mySummary = ((TextView) alert.findViewById(R.id.network_catalog_summary)).getText().toString().trim();
+
+ if (myTitle.length() == 0) {
+ myTitle = null;
+ final String err = myResource.getResource("titleIsEmpty").getValue();
+ handler.sendMessage(handler.obtainMessage(-1, err));
+ return;
+ }
+ if (myUrl.length() == 0) {
+ myUrl = null;
+ final String err = myResource.getResource("urlIsEmpty").getValue();
+ handler.sendMessage(handler.obtainMessage(-1, err));
+ return;
+ }
+ if (mySummary.length() == 0) {
+ mySummary = null;
+ }
+
+ Uri uri = Uri.parse(myUrl);
+ if (uri.getScheme() == null) {
+ myUrl = "http://" + myUrl;
+ uri = Uri.parse(myUrl);
+ }
+
+ String siteName = uri.getHost();
+ if (siteName == null) {
+ final String err = myResource.getResource("invalidUrl").getValue();
+ handler.sendMessage(handler.obtainMessage(-1, err));
+ return;
+ }
+ if (siteName.startsWith("www.")) {
+ siteName = siteName.substring(4);
+ }
+
+ if (myLink == null) {
+ final OPDSLinkReader reader = new OPDSLinkReader();
+ final HashMap links = new HashMap();
+ links.put(INetworkLink.URL_MAIN, myUrl);
+ final ICustomNetworkLink link = reader.createCustomLink(ICustomNetworkLink.INVALID_ID,
+ siteName, myTitle, mySummary, null, links);
+ myLink = link;
+ if (link != null) {
+ if (!NetworkLibrary.Instance().addCustomLink(link)) {
+ final String err = myResource.getResource("alreadyExists").getValue();
+ handler.sendMessage(handler.obtainMessage(-1, err));
+ return;
+ }
+ } else {
+ throw new RuntimeException("Unable to create link!!! Impossible!!!");
+ }
+ } else {
+ final ICustomNetworkLink link = (ICustomNetworkLink) myLink;
+ link.setSiteName(siteName);
+ link.setTitle(myTitle);
+ link.setSummary(mySummary);
+ link.setLink(INetworkLink.URL_MAIN, myUrl);
+ link.saveLink();
+ }
+ handler.sendEmptyMessage(1);
+ } else {
+ handler.sendEmptyMessage(0);
+ }
+ }
+ };
+
+ final ZLResource buttonResource = ZLResource.resource("dialog").getResource("button");
+ return new AlertDialog.Builder(activity)
+ .setView(layout)
+ .setTitle(myResource.getResource("title").getValue())
+ .setPositiveButton(buttonResource.getResource("ok").getValue(), listener)
+ .setNegativeButton(buttonResource.getResource("cancel").getValue(), listener)
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ listener.onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
+ }
+ })
+ .create();
+ }
+
+ public void prepareDialog(Dialog dialog) {
+ if (myLink != null) {
+ if (myTitle == null) myTitle = myLink.getTitle();
+ if (myUrl == null) myUrl = myLink.getLink(INetworkLink.URL_MAIN);
+ if (mySummary == null) mySummary = myLink.getSummary();
+ }
+ ((TextView) dialog.findViewById(R.id.network_catalog_title)).setText((myTitle != null) ? myTitle : "");
+ ((TextView) dialog.findViewById(R.id.network_catalog_url)).setText((myUrl != null) ? myUrl : "");
+ ((TextView) dialog.findViewById(R.id.network_catalog_summary)).setText((mySummary != null) ? mySummary : "");
+
+ final TextView error = (TextView) dialog.findViewById(R.id.network_catalog_error);
+ if (myErrorMessage == null) {
+ error.setVisibility(View.GONE);
+ error.setText("");
+ } else {
+ error.setVisibility(View.VISIBLE);
+ error.setText(myErrorMessage);
+ }
+
+ View dlgView = dialog.findViewById(R.id.network_custom_catalog_dialog);
+ dlgView.invalidate();
+ dlgView.requestLayout();
+ }
+}
diff --git a/src/org/geometerplus/android/fbreader/network/NetworkCatalogActions.java b/src/org/geometerplus/android/fbreader/network/NetworkCatalogActions.java
index 59bbb476f..69a788764 100644
--- a/src/org/geometerplus/android/fbreader/network/NetworkCatalogActions.java
+++ b/src/org/geometerplus/android/fbreader/network/NetworkCatalogActions.java
@@ -48,6 +48,8 @@ class NetworkCatalogActions extends NetworkTreeActions {
public static final int SIGNOUT_ITEM_ID = 5;
public static final int REFILL_ACCOUNT_ITEM_ID = 6;
+ public static final int CUSTOM_CATALOG_EDIT = 7;
+ public static final int CUSTOM_CATALOG_REMOVE = 8;
@Override
public boolean canHandleTree(NetworkTree tree) {
@@ -98,6 +100,11 @@ class NetworkCatalogActions extends NetworkTreeActions {
}
}
}
+ INetworkLink link = catalogTree.Item.Link;
+ if (link instanceof ICustomNetworkLink) {
+ addMenuItem(menu, CUSTOM_CATALOG_EDIT, "editCustomCatalog");
+ addMenuItem(menu, CUSTOM_CATALOG_REMOVE, "removeCustomCatalog");
+ }
} else {
if (item.URLByType.get(NetworkCatalogItem.URL_HTML_PAGE) != null) {
addMenuItem(menu, OPEN_IN_BROWSER_ITEM_ID, "openInBrowser");
@@ -113,7 +120,6 @@ class NetworkCatalogActions extends NetworkTreeActions {
}
break;
}
- return;
}
}
@@ -247,6 +253,12 @@ class NetworkCatalogActions extends NetworkTreeActions {
((NetworkCatalogTree)tree).Item.Link.authenticationManager().refillAccountLink()
);
return true;
+ case CUSTOM_CATALOG_EDIT:
+ NetworkDialog.show(activity, NetworkDialog.DIALOG_CUSTOM_CATALOG, ((NetworkCatalogTree)tree).Item.Link, null);
+ return true;
+ case CUSTOM_CATALOG_REMOVE:
+ removeCustomLink((ICustomNetworkLink)((NetworkCatalogTree)tree).Item.Link);
+ return true;
}
return false;
}
@@ -430,4 +442,12 @@ class NetworkCatalogActions extends NetworkTreeActions {
};
((ZLAndroidDialogManager)ZLAndroidDialogManager.Instance()).wait("signOut", runnable, activity);
}
+
+ private void removeCustomLink(ICustomNetworkLink link) {
+ final NetworkLibrary library = NetworkLibrary.Instance();
+ library.removeCustomLink(link);
+ library.invalidate();
+ library.synchronize();
+ NetworkView.Instance().fireModelChanged();
+ }
}
diff --git a/src/org/geometerplus/android/fbreader/network/NetworkDialog.java b/src/org/geometerplus/android/fbreader/network/NetworkDialog.java
index 1b77351ee..fa9254c25 100644
--- a/src/org/geometerplus/android/fbreader/network/NetworkDialog.java
+++ b/src/org/geometerplus/android/fbreader/network/NetworkDialog.java
@@ -37,6 +37,7 @@ abstract class NetworkDialog {
// dialog identifiers
public static final int DIALOG_AUTHENTICATION = 0;
public static final int DIALOG_REGISTER_USER = 1;
+ public static final int DIALOG_CUSTOM_CATALOG = 2;
private static final TreeMap ourInstances = new TreeMap();
@@ -50,6 +51,9 @@ abstract class NetworkDialog {
case DIALOG_REGISTER_USER:
dlg = new RegisterUserDialog();
break;
+ case DIALOG_CUSTOM_CATALOG:
+ dlg = new CustomCatalogDialog();
+ break;
}
if (dlg != null) {
ourInstances.put(Integer.valueOf(id), dlg);
diff --git a/src/org/geometerplus/android/fbreader/network/NetworkLibraryActivity.java b/src/org/geometerplus/android/fbreader/network/NetworkLibraryActivity.java
index 2efc23458..c2ec9e21d 100644
--- a/src/org/geometerplus/android/fbreader/network/NetworkLibraryActivity.java
+++ b/src/org/geometerplus/android/fbreader/network/NetworkLibraryActivity.java
@@ -116,6 +116,7 @@ public class NetworkLibraryActivity extends NetworkBaseActivity {
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
addMenuItem(menu, 1, "networkSearch", R.drawable.ic_menu_networksearch);
+ addMenuItem(menu, 2, "addCustomCatalog", 0);
return true;
}
@@ -132,6 +133,9 @@ public class NetworkLibraryActivity extends NetworkBaseActivity {
switch (item.getItemId()) {
case 1:
return onSearchRequested();
+ case 2:
+ NetworkDialog.show(this, NetworkDialog.DIALOG_CUSTOM_CATALOG, null, null);
+ return true;
default:
return true;
}
diff --git a/src/org/geometerplus/android/fbreader/network/RegisterUserDialog.java b/src/org/geometerplus/android/fbreader/network/RegisterUserDialog.java
index 94aee7e79..20f46ae05 100644
--- a/src/org/geometerplus/android/fbreader/network/RegisterUserDialog.java
+++ b/src/org/geometerplus/android/fbreader/network/RegisterUserDialog.java
@@ -50,7 +50,7 @@ class RegisterUserDialog extends NetworkDialog {
super("RegisterUserDialog");
}
- private void clearUserInfo() {
+ private void clearData() {
myLogin = myPassword = myEmail = null;
}
@@ -64,12 +64,13 @@ class RegisterUserDialog extends NetworkDialog {
final Handler handler = new Handler() {
public void handleMessage(Message message) {
+ if (!NetworkView.Instance().isInitialized()) {
+ return;
+ }
final NetworkLibrary library = NetworkLibrary.Instance();
library.invalidateVisibility();
library.synchronize();
- if (NetworkView.Instance().isInitialized()) {
- NetworkView.Instance().fireModelChanged();
- }
+ NetworkView.Instance().fireModelChanged();
if (message.what < 0) {
if (message.what == -2) {
final ZLResource dialogResource = ZLResource.resource("dialog");
@@ -91,7 +92,7 @@ class RegisterUserDialog extends NetworkDialog {
myOnSuccessRunnable.run();
}
}
- clearUserInfo();
+ clearData();
}
};
@@ -161,6 +162,11 @@ class RegisterUserDialog extends NetworkDialog {
.setTitle(myResource.getResource("title").getValue())
.setPositiveButton(buttonResource.getResource("ok").getValue(), listener)
.setNegativeButton(buttonResource.getResource("cancel").getValue(), listener)
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ listener.onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
+ }
+ })
.create();
}
diff --git a/src/org/geometerplus/android/fbreader/network/SQLiteNetworkDatabase.java b/src/org/geometerplus/android/fbreader/network/SQLiteNetworkDatabase.java
index ea52460ce..001a03f0e 100644
--- a/src/org/geometerplus/android/fbreader/network/SQLiteNetworkDatabase.java
+++ b/src/org/geometerplus/android/fbreader/network/SQLiteNetworkDatabase.java
@@ -189,6 +189,36 @@ class SQLiteNetworkDatabase extends NetworkDatabase {
});
}
+ private SQLiteStatement myDeleteAllCustomLinksStatement;
+ private SQLiteStatement myDeleteCustomLinkStatement;
+ @Override
+ protected void deleteCustomLink(final ICustomNetworkLink link) {
+ if (link.getId() == ICustomNetworkLink.INVALID_ID) {
+ return;
+ }
+ executeAsATransaction(new Runnable() {
+ public void run() {
+ final long id = link.getId();
+ if (myDeleteAllCustomLinksStatement == null) {
+ myDeleteAllCustomLinksStatement = myDatabase.compileStatement(
+ "DELETE FROM CustomLinkUrls WHERE link_id = ?");
+ }
+ myDeleteAllCustomLinksStatement.bindLong(1, id);
+ myDeleteAllCustomLinksStatement.execute();
+
+ if (myDeleteCustomLinkStatement == null) {
+ myDeleteCustomLinkStatement = myDatabase.compileStatement(
+ "DELETE FROM CustomLinks WHERE link_id = ?"
+ );
+ }
+ myDeleteCustomLinkStatement.bindLong(1, id);
+ myDeleteCustomLinkStatement.execute();
+
+ link.setId(ICustomNetworkLink.INVALID_ID);
+ }
+ });
+ }
+
private void createTables() {
myDatabase.execSQL(
"CREATE TABLE CustomLinks(" +
diff --git a/src/org/geometerplus/fbreader/network/NetworkDatabase.java b/src/org/geometerplus/fbreader/network/NetworkDatabase.java
index 2f7bbf03d..07b78cfd5 100644
--- a/src/org/geometerplus/fbreader/network/NetworkDatabase.java
+++ b/src/org/geometerplus/fbreader/network/NetworkDatabase.java
@@ -41,4 +41,5 @@ public abstract class NetworkDatabase {
protected abstract void loadCustomLinks(List links, ICustomLinksFactory factory);
protected abstract void saveCustomLink(ICustomNetworkLink link);
+ protected abstract void deleteCustomLink(ICustomNetworkLink link);
}
diff --git a/src/org/geometerplus/fbreader/network/NetworkLibrary.java b/src/org/geometerplus/fbreader/network/NetworkLibrary.java
index 2dbc7e9f4..717490fd6 100644
--- a/src/org/geometerplus/fbreader/network/NetworkLibrary.java
+++ b/src/org/geometerplus/fbreader/network/NetworkLibrary.java
@@ -45,17 +45,11 @@ public class NetworkLibrary {
private static class CompositeList extends AbstractSequentialList {
private final ArrayList> myLists;
- private final int mySize;
private Comparator myComparator;
public CompositeList(ArrayList> lists,
Comparator comparator) {
- int size = 0;
- for (ArrayList extends INetworkLink> list: lists) {
- size += list.size();
- }
myLists = lists;
- mySize = size;
myComparator = comparator;
}
@@ -87,7 +81,7 @@ public class NetworkLibrary {
}
public boolean hasNext() {
- return myIndex < mySize;
+ return myIndex < size();
}
public boolean hasPrevious() {
@@ -165,7 +159,7 @@ public class NetworkLibrary {
@Override
public ListIterator listIterator(int location) {
- if (location < 0 || location > mySize) {
+ if (location < 0 || location > size()) {
throw new IndexOutOfBoundsException();
}
Iterator it = new Iterator();
@@ -182,7 +176,11 @@ public class NetworkLibrary {
@Override
public int size() {
- return mySize;
+ int size = 0;
+ for (ArrayList extends INetworkLink> list: myLists) {
+ size += list.size();
+ }
+ return size;
}
}
@@ -332,9 +330,13 @@ public class NetworkLibrary {
}
final INetworkLink nodeLink = ((NetworkCatalogTree) currentNode).Item.Link;
if (nodeLink == link) {
+ if (link instanceof ICustomNetworkLink) {
+ toRemove.add(currentNode);
+ } else {
+ processed = true;
+ }
currentNode = null;
++nodeCount;
- processed = true;
break;
} else {
boolean found = false;
@@ -354,8 +356,8 @@ public class NetworkLibrary {
}
}
}
- final int nextIndex = nodeIterator.nextIndex();
if (!processed) {
+ final int nextIndex = nodeIterator.nextIndex();
new NetworkCatalogRootTree(myRootTree, link, nodeCount++).Item.onDisplayItem();
nodeIterator = myRootTree.subTrees().listIterator(nextIndex + 1);
}
@@ -474,4 +476,14 @@ public class NetworkLibrary {
public ICustomNetworkLink getCustomLink(int index) {
return myCustomLinks.get(index);
}
+
+ public void removeCustomLink(ICustomNetworkLink link) {
+ final int index = Collections.binarySearch(myCustomLinks, link, new LinksComparator());
+ if (index < 0) {
+ return;
+ }
+ myCustomLinks.remove(index);
+ NetworkDatabase.Instance().deleteCustomLink(link);
+ link.setSaveLinkListener(null);
+ }
}