From 6174b7fc042f88b85fd7d3efffc30abca81c94ed Mon Sep 17 00:00:00 2001 From: Vasiliy Bout Date: Tue, 29 Jun 2010 20:26:08 +0000 Subject: [PATCH] CustomCatalog UI git-svn-id: https://only.mawhrin.net/repos/FBReaderJ/trunk@1523 6a642e6f-84f6-412e-ac94-c4a38d5a04b0 --- data/resources/application/en.xml | 13 ++ res/layout/network_custom_catalog_dialog.xml | 86 ++++++++ .../network/AuthenticationDialog.java | 22 +- .../fbreader/network/CustomCatalogDialog.java | 205 ++++++++++++++++++ .../network/NetworkCatalogActions.java | 22 +- .../fbreader/network/NetworkDialog.java | 4 + .../network/NetworkLibraryActivity.java | 4 + .../fbreader/network/RegisterUserDialog.java | 16 +- .../network/SQLiteNetworkDatabase.java | 30 +++ .../fbreader/network/NetworkDatabase.java | 1 + .../fbreader/network/NetworkLibrary.java | 34 ++- 11 files changed, 418 insertions(+), 19 deletions(-) create mode 100644 res/layout/network_custom_catalog_dialog.xml create mode 100644 src/org/geometerplus/android/fbreader/network/CustomCatalogDialog.java 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 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 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); + } }