/* * 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.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.os.*; import android.view.*; import android.widget.AdapterView; import android.widget.ListView; import org.geometerplus.zlibrary.core.network.ZLNetworkManager; import org.geometerplus.zlibrary.core.network.ZLNetworkException; import org.geometerplus.zlibrary.core.language.ZLLanguageUtil; import org.geometerplus.zlibrary.core.resources.ZLResource; import org.geometerplus.zlibrary.ui.android.network.SQLiteCookieDatabase; import org.geometerplus.zlibrary.ui.android.R; import org.geometerplus.android.util.UIUtil; import org.geometerplus.fbreader.tree.FBTree; import org.geometerplus.fbreader.network.*; import org.geometerplus.fbreader.network.tree.*; import org.geometerplus.android.fbreader.tree.BaseActivity; public class NetworkBaseActivity extends BaseActivity implements NetworkView.EventListener { protected static final int BASIC_AUTHENTICATION_CODE = 1; protected static final int CUSTOM_AUTHENTICATION_CODE = 2; protected static final int SIGNUP_CODE = 3; private static final String ACTIVITY_BY_TREE_KEY = "ActivityByTree"; static void setForTree(NetworkTree tree, NetworkBaseActivity activity) { if (tree != null) { tree.setUserData(ACTIVITY_BY_TREE_KEY, activity); } } static NetworkBaseActivity getByTree(NetworkTree tree) { return (NetworkBaseActivity)tree.getUserData(ACTIVITY_BY_TREE_KEY); } public BookDownloaderServiceConnection Connection; private volatile boolean myInProgress; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); OLD_STYLE_FLAG = true; SQLiteCookieDatabase.init(this); Connection = new BookDownloaderServiceConnection(); bindService( new Intent(getApplicationContext(), BookDownloaderService.class), Connection, BIND_AUTO_CREATE ); setListAdapter(new NetworkLibraryAdapter(this)); init(getIntent()); setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); setForTree((NetworkTree)getCurrentTree(), this); setProgressBarIndeterminateVisibility(myInProgress); } @Override protected FBTree getTreeByKey(FBTree.Key key) { final NetworkLibrary library = NetworkLibrary.Instance(); FBTree tree = null; if (key != null) { tree = library.getTreeByKey(key); } return tree != null ? tree : library.getRootTree(); } @Override protected void onStart() { super.onStart(); /* * Set listener in onStart() to give descendants initialize itself in * onCreate methods before onModelChanged() will be called. */ NetworkView.Instance().addEventListener(this); } @Override public void onResume() { super.onResume(); getListView().setOnCreateContextMenuListener(this); onModelChanged(); // do the same update actions as upon onModelChanged ZLNetworkManager.Instance().setCredentialsCreator(myCredentialsCreator); } @Override protected void onStop() { NetworkView.Instance().removeEventListener(this); super.onStop(); } @Override public void onDestroy() { setForTree((NetworkTree)getCurrentTree(), null); if (Connection != null) { unbindService(Connection); Connection = null; } super.onDestroy(); } @Override public boolean isTreeSelected(FBTree tree) { return false; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { final ItemsLoader runnable = ItemsLoadingService.getRunnable((NetworkTree)getCurrentTree()); if (runnable != null) { runnable.interruptLoading(); } } return super.onKeyDown(keyCode, event); } @Override public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { if (menuInfo != null) { final int position = ((AdapterView.AdapterContextMenuInfo)menuInfo).position; final NetworkTree tree = (NetworkTree)getListAdapter().getItem(position); if (tree != null) { final NetworkTreeActions actions = NetworkView.Instance().getActions(tree); if (actions != null) { actions.buildContextMenu(this, menu, tree); return; } } } else if (getCurrentTree() instanceof NetworkCatalogTree) { final INetworkLink link = ((NetworkCatalogTree)getCurrentTree()).Item.Link; if (Util.isTopupSupported(this, link)) { final TopupActions actions = NetworkView.Instance().getTopupActions(); if (actions != null) { actions.buildContextMenu(this, menu, link); return; } } } super.onCreateContextMenu(menu, view, menuInfo); } @Override public boolean onContextItemSelected(MenuItem item) { if (item != null && item.getMenuInfo() != null) { final int position = ((AdapterView.AdapterContextMenuInfo)item.getMenuInfo()).position; final NetworkTree tree = (NetworkTree)getListAdapter().getItem(position); if (tree != null) { final NetworkTreeActions actions = NetworkView.Instance().getActions(tree); if (actions != null && actions.runAction(this, tree, item.getItemId())) { return true; } } } else if (getCurrentTree() instanceof NetworkCatalogTree) { final INetworkLink link = ((NetworkCatalogTree)getCurrentTree()).Item.Link; if (Util.isTopupSupported(this, link)) { final TopupActions actions = NetworkView.Instance().getTopupActions(); if (actions != null && TopupActions.runAction(this, link, item.getItemId())) { return true; } } } return super.onContextItemSelected(item); } @Override public void onListItemClick(ListView listView, View view, int position, long rowId) { final NetworkTree networkTree = (NetworkTree)getListAdapter().getItem(position); final NetworkView networkView = NetworkView.Instance(); final NetworkTreeActions actions = networkView.getActions(networkTree); if (actions == null) { return; } final int actionCode = actions.getDefaultActionCode(this, networkTree); if (actionCode == NetworkTreeActions.TREE_SHOW_CONTEXT_MENU) { listView.showContextMenuForChild(view); return; } if (actionCode < 0) { return; } actions.runAction(this, networkTree, actionCode); } @Override public boolean onSearchRequested() { return false; } private final AuthenticationActivity.CredentialsCreator myCredentialsCreator = new AuthenticationActivity.CredentialsCreator(this, BASIC_AUTHENTICATION_CODE); @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case BASIC_AUTHENTICATION_CODE: myCredentialsCreator.onDataReceived(resultCode, data); break; case CUSTOM_AUTHENTICATION_CODE: Util.processCustomAuthentication( this, ((NetworkCatalogTree)getCurrentTree()).Item.Link, resultCode, data ); break; case SIGNUP_CODE: Util.processSignup(((NetworkCatalogTree)getCurrentTree()).Item.Link, resultCode, data); break; } } private static final int MENU_SEARCH = 1; private static final int MENU_REFRESH = 2; private static final int MENU_ADD_CATALOG = 3; private static final int MENU_LANGUAGE_FILTER = 4; private MenuItem addMenuItem(Menu menu, int index, String resourceKey, int iconId) { final String label = NetworkLibrary.resource().getResource("menu").getResource(resourceKey).getValue(); return menu.add(0, index, Menu.NONE, label).setIcon(iconId); } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); if (getCurrentTree() instanceof RootTree) { addMenuItem(menu, MENU_SEARCH, "networkSearch", R.drawable.ic_menu_search); addMenuItem(menu, MENU_ADD_CATALOG, "addCustomCatalog", R.drawable.ic_menu_add); addMenuItem(menu, MENU_REFRESH, "refreshCatalogsList", R.drawable.ic_menu_refresh); addMenuItem(menu, MENU_LANGUAGE_FILTER, "languages", R.drawable.ic_menu_languages); return true; } else { return NetworkView.Instance().createOptionsMenu(menu, (NetworkTree)getCurrentTree()); } } @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); if (getCurrentTree() instanceof RootTree) { menu.findItem(MENU_SEARCH).setEnabled(!searchIsInProgress()); return true; } else { return NetworkView.Instance().prepareOptionsMenu(this, menu, (NetworkTree)getCurrentTree()); } } protected static boolean searchIsInProgress() { return ItemsLoadingService.getRunnable( NetworkLibrary.Instance().getSearchItemTree() ) != null; } @Override public boolean onOptionsItemSelected(MenuItem item) { if (getCurrentTree() instanceof RootTree) { switch (item.getItemId()) { case MENU_SEARCH: return onSearchRequested(); case MENU_ADD_CATALOG: AddCustomCatalogItemActions.addCustomCatalog(this); return true; case MENU_REFRESH: refreshCatalogsList(); return true; case MENU_LANGUAGE_FILTER: runLanguageFilterDialog(); return true; default: return true; } } else { return NetworkView.Instance().runOptionsMenu(this, item, (NetworkTree)getCurrentTree()); } } private void runLanguageFilterDialog() { final NetworkLibrary library = NetworkLibrary.Instance(); final List<String> allLanguageCodes = library.languageCodes(); Collections.sort(allLanguageCodes, new ZLLanguageUtil.CodeComparator()); final Collection<String> activeLanguageCodes = library.activeLanguageCodes(); final CharSequence[] languageNames = new CharSequence[allLanguageCodes.size()]; final boolean[] checked = new boolean[allLanguageCodes.size()]; for (int i = 0; i < allLanguageCodes.size(); ++i) { final String code = allLanguageCodes.get(i); languageNames[i] = ZLLanguageUtil.languageName(code); checked[i] = activeLanguageCodes.contains(code); } final DialogInterface.OnMultiChoiceClickListener listener = new DialogInterface.OnMultiChoiceClickListener() { public void onClick(DialogInterface dialog, int which, boolean isChecked) { checked[which] = isChecked; } }; final ZLResource dialogResource = ZLResource.resource("dialog"); final AlertDialog dialog = new AlertDialog.Builder(this) .setMultiChoiceItems(languageNames, checked, listener) .setTitle(dialogResource.getResource("languageFilterDialog").getResource("title").getValue()) .setPositiveButton(dialogResource.getResource("button").getResource("ok").getValue(), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { final TreeSet<String> newActiveCodes = new TreeSet<String>(new ZLLanguageUtil.CodeComparator()); for (int i = 0; i < checked.length; ++i) { if (checked[i]) { newActiveCodes.add(allLanguageCodes.get(i)); } } library.setActiveLanguageCodes(newActiveCodes); library.synchronize(); NetworkView.Instance().fireModelChanged(); } }) .create(); dialog.show(); } private void refreshCatalogsList() { final NetworkView view = NetworkView.Instance(); final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.obj == null) { view.finishBackgroundUpdate(); } else { final ZLResource dialogResource = ZLResource.resource("dialog"); final ZLResource boxResource = dialogResource.getResource("networkError"); final ZLResource buttonResource = dialogResource.getResource("button"); new AlertDialog.Builder(NetworkBaseActivity.this) .setTitle(boxResource.getResource("title").getValue()) .setMessage((String) msg.obj) .setIcon(0) .setPositiveButton(buttonResource.getResource("ok").getValue(), null) .create().show(); } } }; UIUtil.wait("updatingCatalogsList", new Runnable() { public void run() { String error = null; try { view.runBackgroundUpdate(true); } catch (ZLNetworkException e) { error = e.getMessage(); } handler.sendMessage(handler.obtainMessage(0, error)); } }, this); } // method from NetworkView.EventListener public void onModelChanged() { runOnUiThread(new Runnable() { public void run() { final NetworkTree tree = getLoadableNetworkTree((NetworkTree)getCurrentTree()); myInProgress = tree != null && ItemsLoadingService.getRunnable(tree) != null; getListView().invalidateViews(); /* * getListAdapter() always returns CatalogAdapter because onModelChanged() * can be called only after Activity's onStart() method (where NetworkView's * addEventListener() is called). Therefore CatalogAdapter will be set as * adapter in onCreate() method before any calls to onModelChanged(). */ ((NetworkLibraryAdapter)getListAdapter()).replaceAll(getCurrentTree().subTrees()); for (FBTree child : getCurrentTree().subTrees()) { if (child instanceof TopUpTree) { child.invalidateChildren(); } } setProgressBarIndeterminateVisibility(myInProgress); } }); } private static NetworkTree getLoadableNetworkTree(NetworkTree tree) { while (tree instanceof NetworkAuthorTree || tree instanceof NetworkSeriesTree) { if (tree.Parent instanceof NetworkTree) { tree = (NetworkTree)tree.Parent; } else { return null; } } return tree; } }