/*
* Copyright (C) 2010-2011 Geometer Plus <contact@geometerplus.com>
*
* 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.*;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Message;
import android.os.Handler;
import android.view.Menu;
import android.view.ContextMenu;
import org.geometerplus.zlibrary.core.util.ZLBoolean3;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.android.util.UIUtil;
import org.geometerplus.android.util.PackageUtil;
import org.geometerplus.fbreader.network.*;
import org.geometerplus.fbreader.network.tree.NetworkTreeFactory;
import org.geometerplus.fbreader.network.tree.NetworkCatalogTree;
import org.geometerplus.fbreader.network.tree.NetworkCatalogRootTree;
import org.geometerplus.fbreader.network.opds.BasketItem;
import org.geometerplus.fbreader.network.authentication.*;
class NetworkCatalogActions extends NetworkTreeActions {
public static final int OPEN_CATALOG_ITEM_ID = 0;
public static final int OPEN_IN_BROWSER_ITEM_ID = 1;
public static final int RELOAD_ITEM_ID = 2;
public static final int SIGNUP_ITEM_ID = 3;
public static final int SIGNIN_ITEM_ID = 4;
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;
public static final int BASKET_CLEAR = 9;
public static final int BASKET_BUY_ALL_BOOKS = 10;
@Override
public boolean canHandleTree(NetworkTree tree) {
return tree instanceof NetworkCatalogTree;
}
@Override
public String getTreeTitle(NetworkTree tree) {
if (tree instanceof NetworkCatalogRootTree) {
return tree.getName();
}
return tree.getName() + " - " + ((NetworkCatalogTree)tree).Item.Link.getSiteName();
}
@Override
public void buildContextMenu(Activity activity, ContextMenu menu, NetworkTree tree) {
final NetworkCatalogItem item = ((NetworkCatalogTree)tree).Item;
final NetworkURLCatalogItem urlItem =
item instanceof NetworkURLCatalogItem ? (NetworkURLCatalogItem)item : null;
menu.setHeaderTitle(tree.getName());
boolean hasItems = false;
final String catalogUrl =
urlItem != null ? urlItem.URLByType.get(NetworkURLCatalogItem.URL_CATALOG) : null;
if (catalogUrl != null &&
(!(item instanceof BasketItem) || item.Link.basket().bookIds().size() > 0)) {
addMenuItem(menu, OPEN_CATALOG_ITEM_ID, "openCatalog");
hasItems = true;
}
if (tree instanceof NetworkCatalogRootTree) {
if (item.getVisibility() == ZLBoolean3.B3_TRUE) {
final NetworkAuthenticationManager mgr = item.Link.authenticationManager();
if (mgr != null) {
if (mgr.mayBeAuthorised(false)) {
addMenuItem(menu, SIGNOUT_ITEM_ID, "signOut", mgr.currentUserName());
if (mgr.refillAccountLink() != null) {
final String account = mgr.currentAccount();
if (account != null) {
addMenuItem(menu, REFILL_ACCOUNT_ITEM_ID, "refillAccount", account);
}
}
} else {
addMenuItem(menu, SIGNIN_ITEM_ID, "signIn");
//if (mgr.passwordRecoverySupported()) {
// registerAction(new PasswordRecoveryAction(mgr), true);
//}
}
}
}
INetworkLink link = item.Link;
if (link instanceof ICustomNetworkLink) {
addMenuItem(menu, CUSTOM_CATALOG_EDIT, "editCustomCatalog");
addMenuItem(menu, CUSTOM_CATALOG_REMOVE, "removeCustomCatalog");
}
} else {
if (urlItem != null && urlItem.URLByType.get(NetworkURLCatalogItem.URL_HTML_PAGE) != null) {
addMenuItem(menu, OPEN_IN_BROWSER_ITEM_ID, "openInBrowser");
hasItems = true;
}
}
if (item.getVisibility() == ZLBoolean3.B3_UNDEFINED &&
!hasItems && item.Link.authenticationManager() != null) {
addMenuItem(menu, SIGNIN_ITEM_ID, "signIn");
}
}
@Override
public int getDefaultActionCode(NetworkBaseActivity activity, NetworkTree tree) {
final NetworkCatalogItem item = ((NetworkCatalogTree)tree).Item;
if (!(item instanceof NetworkURLCatalogItem)) {
return OPEN_CATALOG_ITEM_ID;
}
final NetworkURLCatalogItem urlItem = (NetworkURLCatalogItem)item;
if (urlItem.URLByType.get(NetworkURLCatalogItem.URL_CATALOG) != null) {
return OPEN_CATALOG_ITEM_ID;
}
if (urlItem.URLByType.get(NetworkURLCatalogItem.URL_HTML_PAGE) != null) {
return OPEN_IN_BROWSER_ITEM_ID;
}
if (urlItem.getVisibility() == ZLBoolean3.B3_UNDEFINED &&
urlItem.Link.authenticationManager() != null) {
return SIGNIN_ITEM_ID;
}
return TREE_NO_ACTION;
}
@Override
public String getConfirmText(NetworkTree tree, int actionCode) {
if (actionCode == OPEN_IN_BROWSER_ITEM_ID) {
return getConfirmValue("openInBrowser");
}
return null;
}
@Override
public boolean createOptionsMenu(Menu menu, NetworkTree tree) {
addOptionsItem(menu, RELOAD_ITEM_ID, "reload");
addOptionsItem(menu, SIGNIN_ITEM_ID, "signIn");
addOptionsItem(menu, SIGNUP_ITEM_ID, "signUp");
addOptionsItem(menu, SIGNOUT_ITEM_ID, "signOut", "");
addOptionsItem(menu, REFILL_ACCOUNT_ITEM_ID, "refillAccount");
if (((NetworkCatalogTree)tree).Item instanceof BasketItem) {
addOptionsItem(menu, BASKET_CLEAR, "clearBasket");
addOptionsItem(menu, BASKET_BUY_ALL_BOOKS, "buyAllBooks");
}
return true;
}
@Override
public boolean prepareOptionsMenu(NetworkBaseActivity activity, Menu menu, NetworkTree tree) {
final NetworkCatalogItem item = ((NetworkCatalogTree)tree).Item;
final NetworkURLCatalogItem urlItem =
item instanceof NetworkURLCatalogItem ? (NetworkURLCatalogItem)item : null;
prepareOptionsItem(menu, RELOAD_ITEM_ID,
urlItem != null &&
urlItem.URLByType.get(NetworkURLCatalogItem.URL_CATALOG) != null &&
!NetworkView.Instance().containsItemsLoadingRunnable(tree.getUniqueKey()));
boolean signIn = false;
boolean signOut = false;
boolean refill = false;
String userName = null;
String account = null;
NetworkAuthenticationManager mgr = item.Link.authenticationManager();
if (mgr != null) {
if (mgr.mayBeAuthorised(false)) {
userName = mgr.currentUserName();
signOut = true;
account = mgr.currentAccount();
if (mgr.refillAccountLink() != null && account != null) {
refill = true;
}
} else {
signIn = true;
//if (mgr.passwordRecoverySupported()) {
// registerAction(new PasswordRecoveryAction(mgr), true);
//}
}
}
prepareOptionsItem(menu, SIGNIN_ITEM_ID, signIn);
prepareOptionsItem(menu, SIGNUP_ITEM_ID, signIn & Util.isRegistrationSupported(activity, item.Link));
prepareOptionsItem(menu, SIGNOUT_ITEM_ID, signOut, "signOut", userName);
prepareOptionsItem(menu, REFILL_ACCOUNT_ITEM_ID, refill);
return true;
}
private boolean consumeByVisibility(final NetworkBaseActivity activity, final NetworkTree tree, final int actionCode) {
final NetworkCatalogItem item = ((NetworkCatalogTree)tree).Item;
switch (item.getVisibility()) {
case B3_TRUE:
return false;
case B3_UNDEFINED:
AuthenticationDialog.show(activity, ((NetworkCatalogTree)tree).Item.Link, new Runnable() {
public void run() {
if (item.getVisibility() != ZLBoolean3.B3_TRUE) {
return;
}
if (actionCode != SIGNIN_ITEM_ID) {
runAction(activity, tree, actionCode);
}
}
});
break;
}
return true;
}
@Override
public boolean runAction(NetworkBaseActivity activity, NetworkTree tree, int actionCode) {
if (consumeByVisibility(activity, tree, actionCode)) {
return true;
}
final NetworkCatalogTree catalogTree = (NetworkCatalogTree)tree;
final NetworkCatalogItem item = catalogTree.Item;
switch (actionCode) {
case OPEN_CATALOG_ITEM_ID:
{
if (item instanceof BasketItem && item.Link.basket().bookIds().size() == 0) {
UIUtil.showErrorMessage(activity, "emptyBasket");
} else {
doExpandCatalog(activity, catalogTree);
}
return true;
}
case OPEN_IN_BROWSER_ITEM_ID:
if (item instanceof NetworkURLCatalogItem) {
Util.openInBrowser(
activity,
((NetworkURLCatalogItem)item).URLByType.get(NetworkURLCatalogItem.URL_HTML_PAGE)
);
}
return true;
case RELOAD_ITEM_ID:
doReloadCatalog(activity, catalogTree);
return true;
case SIGNIN_ITEM_ID:
AuthenticationDialog.show(activity, item.Link, null);
return true;
case SIGNUP_ITEM_ID:
Util.runRegistrationDialog(activity, item.Link);
return true;
case SIGNOUT_ITEM_ID:
doSignOut(activity, catalogTree);
return true;
case REFILL_ACCOUNT_ITEM_ID:
new RefillAccountActions().runStandalone(activity, item.Link);
return true;
case CUSTOM_CATALOG_EDIT:
{
final Intent intent = new Intent(activity, AddCustomCatalogActivity.class);
NetworkLibraryActivity.addLinkToIntent(
intent,
(ICustomNetworkLink)item.Link
);
activity.startActivity(intent);
return true;
}
case CUSTOM_CATALOG_REMOVE:
removeCustomLink((ICustomNetworkLink)item.Link);
return true;
case BASKET_CLEAR:
item.Link.basket().clear();
return true;
case BASKET_BUY_ALL_BOOKS:
return true;
}
return false;
}
private static class ExpandCatalogHandler extends ItemsLoadingHandler {
private final NetworkTree.Key myKey;
private final NetworkCatalogTree myTree;
ExpandCatalogHandler(NetworkCatalogTree tree, NetworkTree.Key key) {
myTree = tree;
myKey = key;
}
@Override
public void onUpdateItems(List<NetworkItem> items) {
for (NetworkItem item: items) {
myTree.ChildrenItems.add(item);
NetworkTreeFactory.createNetworkTree(myTree, item);
}
}
@Override
public void afterUpdateItems() {
if (NetworkView.Instance().isInitialized()) {
NetworkView.Instance().fireModelChangedAsync();
}
}
@Override
public void onFinish(String errorMessage, boolean interrupted,
Set<NetworkItem> uncommitedItems) {
if (interrupted &&
(!myTree.Item.supportsResumeLoading() || errorMessage != null)) {
myTree.ChildrenItems.clear();
myTree.clear();
} else {
myTree.removeItems(uncommitedItems);
myTree.updateLoadedTime();
if (!interrupted) {
afterUpdateCatalog(errorMessage, myTree.ChildrenItems.size() == 0);
}
final NetworkLibrary library = NetworkLibrary.Instance();
library.invalidateVisibility();
library.synchronize();
}
if (NetworkView.Instance().isInitialized()) {
NetworkView.Instance().fireModelChangedAsync();
}
}
private void afterUpdateCatalog(String errorMessage, boolean childrenEmpty) {
if (!NetworkView.Instance().isInitialized()) {
return;
}
final NetworkCatalogActivity activity = NetworkView.Instance().getOpenedActivity(myKey);
if (activity == null) {
return;
}
String msg = null;
if (errorMessage != null) {
UIUtil.showErrorMessageText(activity, errorMessage);
} else if (childrenEmpty) {
UIUtil.showErrorMessage(activity, "emptyCatalog");
}
}
}
private static class ExpandCatalogRunnable extends ItemsLoadingRunnable {
private final NetworkCatalogTree myTree;
private final boolean myCheckAuthentication;
private final boolean myResumeNotLoad;
public ExpandCatalogRunnable(ItemsLoadingHandler handler,
NetworkCatalogTree tree, boolean checkAuthentication, boolean resumeNotLoad) {
super(handler);
myTree = tree;
myCheckAuthentication = checkAuthentication;
myResumeNotLoad = resumeNotLoad;
}
public String getResourceKey() {
return "downloadingCatalogs";
}
@Override
public void doBefore() throws ZLNetworkException {
/*if (!NetworkOperationRunnable::tryConnect()) {
return;
}*/
final INetworkLink link = myTree.Item.Link;
if (myCheckAuthentication && link.authenticationManager() != null) {
final NetworkAuthenticationManager mgr = link.authenticationManager();
if (mgr.isAuthorised(true) && mgr.needsInitialization()) {
try {
mgr.initialize();
} catch (ZLNetworkException e) {
mgr.logOut();
}
}
}
}
@Override
public void doLoading(NetworkOperationData.OnNewItemListener doWithListener) throws ZLNetworkException {
if (myResumeNotLoad) {
myTree.Item.resumeLoading(doWithListener);
} else {
myTree.Item.loadChildren(doWithListener);
}
}
}
private void processExtraData(final NetworkBaseActivity activity, Map<String,String> extraData, final Runnable postRunnable) {
if (extraData != null && !extraData.isEmpty()) {
PackageUtil.runInstallPluginDialog(activity, extraData, postRunnable);
} else {
postRunnable.run();
}
}
private void doExpandCatalog(final NetworkBaseActivity activity, final NetworkCatalogTree tree) {
final NetworkTree.Key key = tree.getUniqueKey();
NetworkView.Instance().tryResumeLoading(activity, tree, new Runnable() {
public void run() {
boolean resumeNotLoad = false;
if (tree.hasChildren()) {
if (tree.isContentValid()) {
if (tree.Item.supportsResumeLoading()) {
resumeNotLoad = true;
} else {
Util.openTree(activity, tree);
return;
}
} else {
tree.ChildrenItems.clear();
tree.clear();
NetworkView.Instance().fireModelChangedAsync();
}
}
/* FIXME: if catalog's loading will be very fast
* then it is possible that loading message is lost
* (see ExpandCatalogHandler.afterUpdateCatalog method).
*
* For example, this can be fixed via adding method
* NetworkView.postCatalogLoadingResult, that will do the following:
* 1) If there is activity, then show message
* 2) If there is no activity, then save message, and show when activity is created
* 3) Remove unused messages (say, by timeout)
*/
final ExpandCatalogHandler handler = new ExpandCatalogHandler(tree, key);
NetworkView.Instance().startItemsLoading(
activity,
key,
new ExpandCatalogRunnable(handler, tree, true, resumeNotLoad)
);
processExtraData(activity, tree.Item.extraData(), new Runnable() {
public void run() {
Util.openTree(activity, tree);
}
});
}
});
}
public void doReloadCatalog(NetworkBaseActivity activity, final NetworkCatalogTree tree) {
final NetworkTree.Key key = tree.getUniqueKey();
if (NetworkView.Instance().containsItemsLoadingRunnable(key)) {
return;
}
tree.ChildrenItems.clear();
tree.clear();
NetworkView.Instance().fireModelChangedAsync();
final ExpandCatalogHandler handler = new ExpandCatalogHandler(tree, key);
NetworkView.Instance().startItemsLoading(
activity,
key,
new ExpandCatalogRunnable(handler, tree, false, false)
);
}
private void doSignOut(NetworkBaseActivity activity, NetworkCatalogTree tree) {
final Handler handler = new Handler() {
public void handleMessage(Message message) {
final NetworkLibrary library = NetworkLibrary.Instance();
library.invalidateVisibility();
library.synchronize();
if (NetworkView.Instance().isInitialized()) {
NetworkView.Instance().fireModelChanged();
}
}
};
final NetworkAuthenticationManager mgr = tree.Item.Link.authenticationManager();
final Runnable runnable = new Runnable() {
public void run() {
if (mgr.mayBeAuthorised(false)) {
mgr.logOut();
handler.sendEmptyMessage(0);
}
}
};
UIUtil.wait("signOut", runnable, activity);
}
private void removeCustomLink(ICustomNetworkLink link) {
final NetworkLibrary library = NetworkLibrary.Instance();
library.removeCustomLink(link);
library.synchronize();
NetworkView.Instance().fireModelChangedAsync();
}
}