1
0
Fork 0
mirror of https://github.com/geometer/FBReaderJ.git synced 2025-10-06 03:50:19 +02:00

Merge branch 'master' into tts

This commit is contained in:
Nikolay Pultsin 2011-03-06 14:00:53 +00:00
commit a5cc9968d4
94 changed files with 1873 additions and 1312 deletions

View file

@ -135,6 +135,7 @@
<activity android:name="org.geometerplus.android.fbreader.network.NetworkLibraryActivity" android:process=":networkLibrary" android:launchMode="singleTask" android:configChanges="orientation|keyboardHidden">
<meta-data android:name="android.app.default_searchable" android:value="org.geometerplus.android.fbreader.network.NetworkSearchActivity" />
</activity>
<activity android:name="org.geometerplus.android.fbreader.network.AuthenticationActivity" android:process=":networkLibrary" android:configChanges="orientation|keyboardHidden" android:theme="@android:style/Theme.Dialog"/>
<activity android:name="org.geometerplus.android.fbreader.network.AddCustomCatalogActivity" android:process=":networkLibrary" android:configChanges="orientation|keyboardHidden" android:theme="@android:style/Theme.Dialog">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

View file

@ -134,6 +134,7 @@
<activity android:name="org.geometerplus.android.fbreader.network.NetworkLibraryActivity" android:process=":networkLibrary" android:launchMode="singleTask" android:configChanges="orientation|keyboardHidden">
<meta-data android:name="android.app.default_searchable" android:value="org.geometerplus.android.fbreader.network.NetworkSearchActivity" />
</activity>
<activity android:name="org.geometerplus.android.fbreader.network.AuthenticationActivity" android:process=":networkLibrary" android:configChanges="orientation|keyboardHidden" android:theme="@android:style/Theme.Dialog"/>
<activity android:name="org.geometerplus.android.fbreader.network.AddCustomCatalogActivity" android:process=":networkLibrary" android:configChanges="orientation|keyboardHidden" android:theme="@android:style/Theme.Dialog">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

View file

@ -1,7 +1,11 @@
===== 0.99.14 (??? ??, 2011) =====
===== 0.99.14 (Mar ??, 2011) =====
* Thai localization (by Samphan Pojanasophanakul)
* Fixed TTF font style detection
* opds:// URLs support has been implemented
* Unexpected search call has been fixed
* Ignore case comparison for zip entry names
* Fixed custom OPDS link search
* Periodical update of custom links information has been added
===== 0.99.13 (Feb 13, 2011) =====
* Fixed book/position forgetting bug

View file

@ -1,3 +1,27 @@
DONE show 'empty basket' message without opening of catalog
* update basket view if basket content is changed
* basket summary
DONE show recommendation item if basket is not empty
* new actions in main menu: clear; buy all
* update recommendations list after each basket updating/purchasing
* update recommendations list visibility after each basket updating/purchasing
* remove book from basket on purchasing
* search item behaviour like search item in local library
* separate search for each catalog (if search URL is presented)
* menu search button should be available for all catalog levels
* float point numbers as series index
* replace AuthenticationCatalog by AuthenticationActivity
* 'recently visited catalogs' item
* load search URL for catalogs from these catalogs, not from our generic file
DONE show library name in network book activity
* litres: credit card top up
* litres: terminal (?) top up
* litres: similar books link
------------------------------
NP: оповещение об изменениях в namespace'ах происходит после того,
как был обработан тег, объявляющий эти namespace'ы... Нужно сделать:
@ -5,22 +29,8 @@ NP: оповещение об изменениях в namespace'ах проис
2) вызов namespaceMapChangedHandler после вызова endElementHandler
3) вызов namespaceMapChangedHandler для пустых тегов <tagname />
** Записать изменения в ChangeLog
DONE Network library: Объединять книги по сериям
** в сериях книги нужно сортировать по индексу
NP: а где это происходит???
DONE Возможность открывать локальные файлы из всяких файл-менеджеров
DONE На этой странице есть ссылка на epub-файл "для ПК". http://www.zone4iphone.ru/index.php?p_id=7&b_id=18413
Проверить скачивание книги браузером и возможность чтения из папки, куда файл был скачан.
DONE добавлять в библиотеку (сделать таблицу добавленных вручную книг)
------------------------------
** пополнение счета в litres с помощью sms
** сделать как "подкаталог" для элемента "пополнить счет"
** восстановление пароля (use default e-mail)
** сделать отдельную ветку (?) для скачанных samples
** (?) удалять sample при покупке/скачивании полного варианта
@ -57,5 +67,4 @@ DELAYED что делать, когда мало памяти???
-----------------------------
другое:
DONE layout для китайских текстов
* поддержка hufdic в mobipocket

View file

@ -47,6 +47,18 @@
<node name="removeFromFavorites" value="Odebrat z oblíbených"/>
</node>
<node name="networkView">
<node name="byAuthor" value="Podle autora">
<node name="summary" value="Knihy seřazeny podle autora"/>
</node>
<node name="byTitle" value="Podle názvu">
<node name="summary" value="Knihy seřazeny podle názvu"/>
</node>
<node name="byDate" value="By date" toBeTranslated="true">
<node name="summary" value="Books sorted by date of purchasing" toBeTranslated="true"/>
</node>
<node name="bySeries" value="By series" toBeTranslated="true">
<node name="summary" value="Books sorted by series" toBeTranslated="true"/>
</node>
<node name="openCatalog" value="Otevřít katalog"/>
<node name="showResults" value="Zobrazit výsledky"/>
<node name="showBooks" value="Zobrazit knihy"/>
@ -57,6 +69,8 @@
<node name="deleteDemo" value="Odstranit ukázku"/>
<node name="downloadDemo" value="Stáhnout ukázku"/>
<node name="buy" value="Zakoupit (%s)"/>
<node name="addToBasket" value="Add to basket" toBeTranslated="true"/>
<node name="removeFromBasket" value="Remove from basket" toBeTranslated="true"/>
<node name="openInBrowser" value="Otevřít v prohlížeči" />
<node name="stopLoading" value="Zastavit načítání" />
<node name="stopSearching" value="Zastavit vyhledávání" />
@ -109,6 +123,7 @@
<node name="series" value="Série:" />
<node name="indexInSeries" value="Díl v sérii:" />
<node name="tags" value="Štítky:" />
<node name="catalog" value="Catalog:" toBeTranslated="true"/>
</node>
<node name="bookInfo">
<node name="bookInfo" value="Informace o knize" />
@ -559,10 +574,6 @@
<node name="noRequiredInformation" value="Požadovaná informace není uvedena v katalogu" />
<node name="cacheDirectoryError" value="Nelze vytvořit adresář mezipaměti" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Informace" />
<node name="message" value="Katalog je prázdný" />
</node>
<node name="emptySearchResults">
<node name="title" value="Výsledky vyhledávání" />
<node name="message" value="Nebyly nalezeny žádné vhodné knihy" />
@ -610,6 +621,8 @@
<node name="dictionaryIsNotInstalled" value="Slovník není bohužel nainstalován"/>
<node name="permissionDenied" value="Oprávnění bylo bohužel odmítnuto"/>
<node name="noFavorites" value="Seznam oblíbených je bohužel prázdný"/>
<node name="emptyCatalog" value="Katalog je prázdný"/>
<node name="emptyBasket" value="Your basket is empty, sorry" toBeTranslated="true"/>
</node>
<node name="external">
<node name="browser" value="Prohlížeč"/>

View file

@ -48,6 +48,18 @@
<node name="removeFromFavorites" value="Remove from favorites" toBeTranslated="true"/>
</node>
<node name="networkView">
<node name="byAuthor" value="Nach Autor">
<node name="summary" value="Books sorted by author" toBeTranslated="true"/>
</node>
<node name="byTitle" value="By title" toBeTranslated="true">
<node name="summary" value="Books sorted by title" toBeTranslated="true"/>
</node>
<node name="byDate" value="By date" toBeTranslated="true">
<node name="summary" value="Books sorted by date of purchasing" toBeTranslated="true"/>
</node>
<node name="bySeries" value="By series" toBeTranslated="true">
<node name="summary" value="Books sorted by series" toBeTranslated="true"/>
</node>
<node name="openCatalog" value="Katalog öffnen"/>
<node name="showResults" value="Ergebnisse anzeigen"/>
<node name="showBooks" value="Bücher anzeigen"/>
@ -58,6 +70,8 @@
<node name="deleteDemo" value="Leseprobe löschen"/>
<node name="downloadDemo" value="Leseprobe herunterladen"/>
<node name="buy" value="(%s) kaufen"/>
<node name="addToBasket" value="Add to basket" toBeTranslated="true"/>
<node name="removeFromBasket" value="Remove from basket" toBeTranslated="true"/>
<node name="openInBrowser" value="Im Browser öffnen" />
<node name="stopLoading" value="Herunterladen abbrechen" />
<node name="stopSearching" value="Suchen stoppen" />
@ -110,6 +124,7 @@
<node name="series" value="Serie:" />
<node name="indexInSeries" value="Seriennummer:" />
<node name="tags" value="Tags:" />
<node name="catalog" value="Catalog:" toBeTranslated="true"/>
</node>
<node name="bookInfo">
<node name="bookInfo" value="Buchinformation" />
@ -560,10 +575,6 @@
<node name="noRequiredInformation" value="Die benötigte Information ist in dem Katalog nicht spezifiziert." />
<node name="cacheDirectoryError" value="Es kann kein Cache-Verzeichns angelegt werden." />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Information" />
<node name="message" value="Der Katalog ist leer." />
</node>
<node name="emptySearchResults">
<node name="title" value="Suchergebnisse" />
<node name="message" value="Es wurden kein Bücher, die zu ihrer Eingabe passen, gefunden." />
@ -611,6 +622,8 @@
<node name="dictionaryIsNotInstalled" value="Dictionary is not installed, sorry" toBeTranslated="true"/>
<node name="permissionDenied" value="Permission denied, sorry" toBeTranslated="true"/>
<node name="noFavorites" value="Your favorites list is empty, sorry" toBeTranslated="true"/>
<node name="emptyCatalog" value="Der Katalog ist leer."/>
<node name="emptyBasket" value="Your basket is empty, sorry" toBeTranslated="true"/>
</node>
<node name="external">
<node name="browser" value="Browser"/>

View file

@ -46,6 +46,18 @@
<node name="removeFromFavorites" value="Remove from favorites"/>
</node>
<node name="networkView">
<node name="byAuthor" value="By author">
<node name="summary" value="Books sorted by author"/>
</node>
<node name="byTitle" value="By title">
<node name="summary" value="Books sorted by title"/>
</node>
<node name="byDate" value="By date">
<node name="summary" value="Books sorted by date of purchasing"/>
</node>
<node name="bySeries" value="By series">
<node name="summary" value="Books sorted by series"/>
</node>
<node name="openCatalog" value="Open catalog"/>
<node name="showResults" value="Show results"/>
<node name="showBooks" value="Show books"/>
@ -56,6 +68,8 @@
<node name="deleteDemo" value="Delete sample"/>
<node name="downloadDemo" value="Download sample"/>
<node name="buy" value="Buy (%s)"/>
<node name="addToBasket" value="Add to basket"/>
<node name="removeFromBasket" value="Remove from basket"/>
<node name="openInBrowser" value="Open in browser" />
<node name="stopLoading" value="Stop loading" />
<node name="stopSearching" value="Stop searching" />
@ -66,6 +80,8 @@
<node name="refillViaSms" value="Text messages"/>
<node name="refillViaBrowser" value="Open page in browser"/>
<node name="refillSummary" value="Currently: %s"/>
<node name="basket" value="Basket"/>
<node name="basketSummary" value="Books I want to buy"/>
<node name="alreadyDownloading" value="Book is being downloaded" />
<node name="alreadyDownloadingDemo" value="Sample is being downloaded" />
<node name="stoppingCatalogLoading" value="Stopping loading" />
@ -97,6 +113,8 @@
<node name="addCustomCatalog" value="Add catalog"/>
<node name="refreshCatalogsList" value="Refresh catalogs"/>
<node name="languages" value="Language filter"/>
<node name="clearBasket" value="Clear basket"/>
<node name="buyAllBooks" value="Buy all books"/>
</node>
</node>
<node name="networkBookView">
@ -108,6 +126,7 @@
<node name="series" value="Series:" />
<node name="indexInSeries" value="Index in series:" />
<node name="tags" value="Tags:" />
<node name="catalog" value="Catalog:" />
</node>
<node name="bookInfo">
<node name="bookInfo" value="Book Info" />
@ -559,10 +578,6 @@
<node name="noRequiredInformation" value="Required information is not specified in the catalog" />
<node name="cacheDirectoryError" value="Unable to create cache directory" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Information" />
<node name="message" value="Catalog is empty" />
</node>
<node name="emptySearchResults">
<node name="title" value="Search results" />
<node name="message" value="There are no suitable books found" />
@ -610,6 +625,8 @@
<node name="dictionaryIsNotInstalled" value="Dictionary is not installed, sorry"/>
<node name="permissionDenied" value="Permission denied, sorry"/>
<node name="noFavorites" value="Your favorites list is empty, sorry"/>
<node name="emptyCatalog" value="Catalog is empty, sorry" />
<node name="emptyBasket" value="Your basket is empty, sorry" />
</node>
<node name="external">
<node name="browser" value="Browser"/>

View file

@ -50,6 +50,18 @@
<node name="removeFromFavorites" value="Supprimer des favoris"/>
</node>
<node name="networkView">
<node name="byAuthor" value="Par auteur">
<node name="summary" value="Livres triés par auteur"/>
</node>
<node name="byTitle" value="By title" toBeTranslated="true">
<node name="summary" value="Books sorted by title" toBeTranslated="true"/>
</node>
<node name="byDate" value="By date" toBeTranslated="true">
<node name="summary" value="Books sorted by date of purchasing" toBeTranslated="true"/>
</node>
<node name="bySeries" value="By series" toBeTranslated="true">
<node name="summary" value="Books sorted by series" toBeTranslated="true"/>
</node>
<node name="openCatalog" value="Ouvrir le catalogue"/>
<node name="showResults" value="Afficher les résultats"/>
<node name="showBooks" value="Afficher les livres"/>
@ -60,6 +72,8 @@
<node name="deleteDemo" value="Supprimer l'extrait"/>
<node name="downloadDemo" value="Télécharger l'extrait"/>
<node name="buy" value="Acheter (%s)"/>
<node name="addToBasket" value="Add to basket" toBeTranslated="true"/>
<node name="removeFromBasket" value="Remove from basket" toBeTranslated="true"/>
<node name="openInBrowser" value="Ouvrir dans un navigateur" />
<node name="stopLoading" value="Arrêt du chargement" />
<node name="stopSearching" value="Arrêt de la recherche" />
@ -112,6 +126,7 @@
<node name="series" value="Série:" />
<node name="indexInSeries" value="Index dans les séries:" />
<node name="tags" value="Tags:" />
<node name="catalog" value="Catalog:" toBeTranslated="true"/>
</node>
<node name="bookInfo">
<node name="bookInfo" value="Informations sur le livre" />
@ -562,10 +577,6 @@
<node name="noRequiredInformation" value="Certaines informations requises sont absentes du catalogue" />
<node name="cacheDirectoryError" value="Impossible de créer le répertoire contenant le cache" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Information"/>
<node name="message" value="Le catalogue est vide"/>
</node>
<node name="emptySearchResults">
<node name="title" value="Résultats de la recherche" />
<node name="message" value="Aucun livre correspondant trouvé." />
@ -613,6 +624,8 @@
<node name="dictionaryIsNotInstalled" value="Désolé mais le dictionnaire n'est pas installé"/>
<node name="permissionDenied" value="Désolé, accès refusé"/>
<node name="noFavorites" value="Votre liste de favoris est vide"/>
<node name="emptyCatalog" value="Le catalogue est vide"/>
<node name="emptyBasket" value="Your basket is empty, sorry" toBeTranslated="true"/>
</node>
<node name="external">
<node name="browser" value="Navigateur"/>

View file

@ -47,6 +47,18 @@
<node name="removeFromFavorites" value="Retirar de preferidos"/>
</node>
<node name="networkView">
<node name="byAuthor" value="Por autor">
<node name="summary" value="Libros ordenados por autor"/>
</node>
<node name="byTitle" value="Por título">
<node name="summary" value="Libros ordenados por título"/>
</node>
<node name="byDate" value="By date" toBeTranslated="true">
<node name="summary" value="Books sorted by date of purchasing" toBeTranslated="true"/>
</node>
<node name="bySeries" value="By series" toBeTranslated="true">
<node name="summary" value="Books sorted by series" toBeTranslated="true"/>
</node>
<node name="openCatalog" value="Abrir o catalogo"/>
<node name="showResults" value="Mostrar resultados"/>
<node name="showBooks" value="Mostrar libros"/>
@ -57,6 +69,8 @@
<node name="deleteDemo" value="Eliminar mostra"/>
<node name="downloadDemo" value="Descargar mostra"/>
<node name="buy" value="Comprar (%s)"/>
<node name="addToBasket" value="Add to basket" toBeTranslated="true"/>
<node name="removeFromBasket" value="Remove from basket" toBeTranslated="true"/>
<node name="openInBrowser" value="Abrir no navegador" />
<node name="stopLoading" value="Deter a carga" />
<node name="stopSearching" value="Deter a busca" />
@ -109,6 +123,7 @@
<node name="series" value="Series:" />
<node name="indexInSeries" value="Índices das series:" />
<node name="tags" value="Etiquetas:" />
<node name="catalog" value="Catalog:" toBeTranslated="true"/>
</node>
<node name="bookInfo">
<node name="bookInfo" value="Información do libro" />
@ -559,10 +574,6 @@
<node name="noRequiredInformation" value="A información requirida non está indicada no catálogo" />
<node name="cacheDirectoryError" value="Non é posíbel crear o cartafol da caché" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Información" />
<node name="message" value="O catálogo está baleiro" />
</node>
<node name="emptySearchResults">
<node name="title" value="Resultados da busca" />
<node name="message" value="Non se atopa ningún libro coincidente" />
@ -610,6 +621,8 @@
<node name="dictionaryIsNotInstalled" value="O dicionario non está instalado"/>
<node name="permissionDenied" value="Permiso denegado"/>
<node name="noFavorites" value="A súa lista de preferencias está baleira"/>
<node name="emptyCatalog" value="O catálogo está baleiro"/>
<node name="emptyBasket" value="Your basket is empty, sorry" toBeTranslated="true"/>
</node>
<node name="external">
<node name="browser" value="Navegador"/>

View file

@ -47,6 +47,18 @@
<node name="removeFromFavorites" value="Eltávolítás a kedvencek közül"/>
</node>
<node name="networkView">
<node name="byAuthor" value="Szerzők">
<node name="summary" value="Könyvek szerzők szerint"/>
</node>
<node name="byTitle" value="By title" toBeTranslated="true">
<node name="summary" value="Books sorted by title" toBeTranslated="true"/>
</node>
<node name="byDate" value="By date" toBeTranslated="true">
<node name="summary" value="Books sorted by date of purchasing" toBeTranslated="true"/>
</node>
<node name="bySeries" value="By series" toBeTranslated="true">
<node name="summary" value="Books sorted by series" toBeTranslated="true"/>
</node>
<node name="openCatalog" value="Katalógus megnyitása"/>
<node name="showResults" value="Eredmények"/>
<node name="showBooks" value="Könyvek"/>
@ -57,6 +69,8 @@
<node name="deleteDemo" value="Minta törlése"/>
<node name="downloadDemo" value="Minta letöltése"/>
<node name="buy" value="Vásárlás (%s)"/>
<node name="addToBasket" value="Add to basket" toBeTranslated="true"/>
<node name="removeFromBasket" value="Remove from basket" toBeTranslated="true"/>
<node name="openInBrowser" value="Megnyitás böngészőben" />
<node name="stopLoading" value="Letöltés leállítása" />
<node name="stopSearching" value="Keresés leállítása" />
@ -109,6 +123,7 @@
<node name="series" value="Sorozat:" />
<node name="indexInSeries" value="Sorszám:" />
<node name="tags" value="Címkék:" />
<node name="catalog" value="Catalog:" toBeTranslated="true"/>
</node>
<node name="bookInfo">
<node name="bookInfo" value="Könyv adatai" />
@ -559,10 +574,6 @@
<node name="noRequiredInformation" value="Szükséges információ nincs megadva a katalógusban" />
<node name="cacheDirectoryError" value="Gyorsítótár létrehozása sikertelen" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Információ" />
<node name="message" value="A katalógus üres" />
</node>
<node name="emptySearchResults">
<node name="title" value="Eredmények" />
<node name="message" value="Nem található megfelelő könyv" />
@ -610,6 +621,8 @@
<node name="dictionaryIsNotInstalled" value="Sajnos nincs telepítve szótár"/>
<node name="permissionDenied" value="Sanos nem engedélyezett"/>
<node name="noFavorites" value="A Kedvencek listája üres"/>
<node name="emptyCatalog" value="A katalógus üres"/>
<node name="emptyBasket" value="Your basket is empty, sorry" toBeTranslated="true"/>
</node>
<node name="external">
<node name="browser" value="Böngésző"/>

View file

@ -47,6 +47,18 @@
<node name="removeFromFavorites" value="Remove from favorites" toBeTranslated="true"/>
</node>
<node name="networkView">
<node name="byAuthor" value="Per autore">
<node name="summary" value="Books sorted by author" toBeTranslated="true"/>
</node>
<node name="byTitle" value="By title" toBeTranslated="true">
<node name="summary" value="Books sorted by title" toBeTranslated="true"/>
</node>
<node name="byDate" value="By date" toBeTranslated="true">
<node name="summary" value="Books sorted by date of purchasing" toBeTranslated="true"/>
</node>
<node name="bySeries" value="By series" toBeTranslated="true">
<node name="summary" value="Books sorted by series" toBeTranslated="true"/>
</node>
<node name="openCatalog" value="Apri catalogo"/>
<node name="showResults" value="Mostra risultati"/>
<node name="showBooks" value="Mostra libri"/>
@ -57,6 +69,8 @@
<node name="deleteDemo" value="Cancella esempio"/>
<node name="downloadDemo" value="Scarica esempio"/>
<node name="buy" value="Compra (%s)"/>
<node name="addToBasket" value="Add to basket" toBeTranslated="true"/>
<node name="removeFromBasket" value="Remove from basket" toBeTranslated="true"/>
<node name="openInBrowser" value="Apri nel browser" />
<node name="stopLoading" value="Ferma caricamento" />
<node name="stopSearching" value="Ferma ricerca" />
@ -109,6 +123,7 @@
<node name="series" value="Serie:" />
<node name="indexInSeries" value="Indice in serie:" />
<node name="tags" value="Etichette:" />
<node name="catalog" value="Catalog:" toBeTranslated="true"/>
</node>
<node name="bookInfo">
<node name="bookInfo" value="Info Libro" />
@ -559,10 +574,6 @@
<node name="noRequiredInformation" toBeTranslated="true" value="Required information is not specified in the catalog" />
<node name="cacheDirectoryError" toBeTranslated="true" value="Unable to create cache directory" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Informazioni" />
<node name="message" value="Il catalogo è vuoto" />
</node>
<node name="emptySearchResults">
<node name="title" value="Risultati ricerca" />
<node name="message" value="Non sono stati trovati libri adatti" />
@ -610,6 +621,8 @@
<node name="dictionaryIsNotInstalled" value="Dictionary is not installed, sorry" toBeTranslated="true"/>
<node name="permissionDenied" value="Permission denied, sorry" toBeTranslated="true"/>
<node name="noFavorites" value="Your favorites list is empty, sorry" toBeTranslated="true"/>
<node name="emptyCatalog" value="Il catalogo è vuoto"/>
<node name="emptyBasket" value="Your basket is empty, sorry" toBeTranslated="true"/>
</node>
<node name="external">
<node name="browser" value="Browser"/>

View file

@ -47,6 +47,18 @@
<node name="removeFromFavorites" value="Verwijderen van favorieten"/>
</node>
<node name="networkView">
<node name="byAuthor" value="Auteurs">
<node name="summary" value="Boeken gesorteerd op auteur"/>
</node>
<node name="byTitle" value="By title" toBeTranslated="true">
<node name="summary" value="Books sorted by title" toBeTranslated="true"/>
</node>
<node name="byDate" value="By date" toBeTranslated="true">
<node name="summary" value="Books sorted by date of purchasing" toBeTranslated="true"/>
</node>
<node name="bySeries" value="By series" toBeTranslated="true">
<node name="summary" value="Books sorted by series" toBeTranslated="true"/>
</node>
<node name="openCatalog" value="Open catalogus"/>
<node name="showResults" value="Toon resulten"/>
<node name="showBooks" value="Toon boeken"/>
@ -57,6 +69,8 @@
<node name="deleteDemo" value="Verwijder voorbeeld"/>
<node name="downloadDemo" value="Download voorbeeld"/>
<node name="buy" value="Koop (%s)"/>
<node name="addToBasket" value="Add to basket" toBeTranslated="true"/>
<node name="removeFromBasket" value="Remove from basket" toBeTranslated="true"/>
<node name="openInBrowser" value="Openen in de browser" />
<node name="stopLoading" value="Stop loading" toBeTranslated="true" />
<node name="stopSearching" value="Stop searching" toBeTranslated="true" />
@ -109,6 +123,7 @@
<node name="series" value="Serie:" />
<node name="indexInSeries" value="Nummer in serie:" />
<node name="tags" value="Trefwoorden:" />
<node name="catalog" value="Catalog:" toBeTranslated="true"/>
</node>
<node name="bookInfo">
<node name="bookInfo" value="Boekinformatie" />
@ -559,10 +574,6 @@
<node name="noRequiredInformation" value="Required information is not specified in the catalog" toBeTranslated="true" />
<node name="cacheDirectoryError" value="Maken van de cache-directory is mislukt" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Informatie" />
<node name="message" value="Catalogus is leeg" />
</node>
<node name="emptySearchResults">
<node name="title" value="Zoekresultaten" />
<node name="message" value="Er zijn geen geschikte boeken gevonden" />
@ -610,6 +621,8 @@
<node name="dictionaryIsNotInstalled" value="Woordenboek is niet geïnstalleerd"/>
<node name="permissionDenied" value="Toegang geweigerd"/>
<node name="noFavorites" value="Je lijst met favorieten is leeg"/>
<node name="emptyCatalog" value="Catalogus is leeg"/>
<node name="emptyBasket" value="Your basket is empty, sorry" toBeTranslated="true"/>
</node>
<node name="external">
<node name="browser" value="Browser"/>

View file

@ -46,6 +46,18 @@
<node name="removeFromFavorites" value="Убрать из избранного"/>
</node>
<node name="networkView">
<node name="byAuthor" value="Авторы">
<node name="summary" value="Книги, разложенные по авторам"/>
</node>
<node name="byTitle" value="Названия">
<node name="summary" value="Книги, разложенные по названиям"/>
</node>
<node name="byDate" value="Дата">
<node name="summary" value="Книги, разложенные по дате покупки"/>
</node>
<node name="bySeries" value="По сериям">
<node name="summary" value="Книги, разложенные по сериям"/>
</node>
<node name="openCatalog" value="Открыть каталог"/>
<node name="showResults" value="Показать результаты"/>
<node name="showBooks" value="Показать книги"/>
@ -56,6 +68,8 @@
<node name="deleteDemo" value="Удалить фрагмент"/>
<node name="downloadDemo" value="Скачать фрагмент"/>
<node name="buy" value="Купить (%s)"/>
<node name="addToBasket" value="Добавить в корзину"/>
<node name="removeFromBasket" value="Убрать из корзины"/>
<node name="openInBrowser" value="Показать в браузере" />
<node name="stopLoading" value="Остановить загрузку" />
<node name="stopSearching" value="Остановить поиск" />
@ -108,6 +122,7 @@
<node name="series" value="Серия:" />
<node name="indexInSeries" value="Номер в серии:" />
<node name="tags" value="Категории:" />
<node name="catalog" value="Каталог:" />
</node>
<node name="bookInfo">
<node name="bookInfo" value="Информация о книге" />
@ -558,10 +573,6 @@
<node name="noRequiredInformation" value="Информация нет в каталоге" />
<node name="cacheDirectoryError" value="Не удается создать каталог для кэша" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Информация" />
<node name="message" value="Каталог пуст" />
</node>
<node name="emptySearchResults">
<node name="title" value="Результаты поиска" />
<node name="message" value="Подходящих книг не найдено" />
@ -609,6 +620,8 @@
<node name="dictionaryIsNotInstalled" value="Не удалось запустить словарь"/>
<node name="permissionDenied" value="Нет доступа к файлам" />
<node name="noFavorites" value="Вы пока не добавили ни одной книги в «Избранное»"/>
<node name="emptyCatalog" value="Каталог пуст"/>
<node name="emptyBasket" value="В корзине нет книг"/>
</node>
<node name="external">
<node name="browser" value="браузере"/>

View file

@ -47,6 +47,18 @@
<node name="removeFromFavorites" value="ลบจากหนังสือเล่มโปรด"/>
</node>
<node name="networkView">
<node name="byAuthor" value="ชื่อผู้แต่ง ">
<node name="summary" value="เรียงตามชื่อผู้แต่ง"/>
</node>
<node name="byTitle" value="ชื่อหนังสือ">
<node name="summary" value="เรียงตามชื่อหนังสือ"/>
</node>
<node name="byDate" value="By date" toBeTranslated="true">
<node name="summary" value="Books sorted by date of purchasing" toBeTranslated="true"/>
</node>
<node name="bySeries" value="By series" toBeTranslated="true">
<node name="summary" value="Books sorted by series" toBeTranslated="true"/>
</node>
<node name="openCatalog" value="เปิดแคตตาล็อก"/>
<node name="showResults" value="แสดงผลการค้นหา"/>
<node name="showBooks" value="แสดงหนังสือ"/>
@ -57,6 +69,8 @@
<node name="deleteDemo" value="ลบหนังสือตัวอย่าง"/>
<node name="downloadDemo" value="ดาวน์โหลดหนังสือตัวอย่าง"/>
<node name="buy" value="ซื้อ (%s)"/>
<node name="addToBasket" value="Add to basket" toBeTranslated="true"/>
<node name="removeFromBasket" value="Remove from basket" toBeTranslated="true"/>
<node name="openInBrowser" value="เปิดในเว็บบราวเซอร์"/>
<node name="stopLoading" value="หยุดการโหลด"/>
<node name="stopSearching" value="หยุดการค้นหา"/>
@ -109,6 +123,7 @@
<node name="series" value="ชุดหนังสือ:"/>
<node name="indexInSeries" value="ดัชนีชุดหนังสือ:"/>
<node name="tags" value="แท็ก:"/>
<node name="catalog" value="Catalog:" toBeTranslated="true"/>
</node>
<node name="bookInfo">
<node name="bookInfo" value="ข้อมูลหนังสือ"/>
@ -237,12 +252,12 @@
<node name="summaryOff" value="ไม่หมุนหน้าจอเมื่อหมุนตัวเครื่อง"/>
</node>
<node name="showStatusBar" value="แสดงแถบสถานะ">
<node name="summaryOn" value="แสดงแถบสถานะเมื่อแสดงเมนู"/>
<node name="summaryOff" value="ซ่อนแถบสถานะเมื่อแสดง"/>
<node name="summaryOn" value="แสดงแถบสถานะขณะอ่านหนังสือ"/>
<node name="summaryOff" value="ซ่อนแถบสถานะขณะอ่านหนังสือ"/>
</node>
<node name="showStatusBarWhenMenuIsActive" value="Show status bar when menu is active" toBeTranslated="true">
<node name="summaryOn" value="Show status bar when menu becomes active" toBeTranslated="true"/>
<node name="summaryOff" value="Don't show status bar when menu becomes active" toBeTranslated="true"/>
<node name="showStatusBarWhenMenuIsActive" value="แสดงแถบสถานะเมื่อแสดงเมนู">
<node name="summaryOn" value="แสดงแถบสถานะเมื่อแสดงเมนู" />
<node name="summaryOff" value="ไม่แสดงแถบสถานะเมื่อแสดงเมนู" />
</node>
</node>
<node name="text" value="ข้อความ">
@ -559,10 +574,6 @@
<node name="noRequiredInformation" value="ข้อมูลที่ต้องการไม่ได้ระบุในแคตตาล็อก"/>
<node name="cacheDirectoryError" value="ไม่สามารถสร้างแคชสำหรับไดเรคทอรี"/>
</node>
<node name="emptyCatalogBox">
<node name="title" value="ข้อมูล"/>
<node name="message" value="แคตตาล็อกไม่มีข้อมูล"/>
</node>
<node name="emptySearchResults">
<node name="title" value="ผลการค้นหา"/>
<node name="message" value="ไม่พบหนังสือที่ต้องการค้นหา"/>
@ -610,6 +621,8 @@
<node name="dictionaryIsNotInstalled" value="ขออภัย, พจนานุกรมไม่ได้รับการติดตั้ง"/>
<node name="permissionDenied" value="ขออภัย, ไม่ได้รับการอนุญาต"/>
<node name="noFavorites" value="ขออภัย, ไม่มีข้อมูลในรายการโปรดของคุณ"/>
<node name="emptyCatalog" value="แคตตาล็อกไม่มีข้อมูล"/>
<node name="emptyBasket" value="Your basket is empty, sorry" toBeTranslated="true"/>
</node>
<node name="external">
<node name="browser" value="เว็บเบราว์เซอร์"/>

View file

@ -47,6 +47,18 @@
<node name="removeFromFavorites" value="Remove from favorites" toBeTranslated="true"/>
</node>
<node name="networkView">
<node name="byAuthor" value="Автори">
<node name="summary" value="Books sorted by author" toBeTranslated="true"/>
</node>
<node name="byTitle" value="By title" toBeTranslated="true">
<node name="summary" value="Books sorted by title" toBeTranslated="true"/>
</node>
<node name="byDate" value="By date" toBeTranslated="true">
<node name="summary" value="Books sorted by date of purchasing" toBeTranslated="true"/>
</node>
<node name="bySeries" value="By series" toBeTranslated="true">
<node name="summary" value="Books sorted by series" toBeTranslated="true"/>
</node>
<node name="openCatalog" value="Відкрити теку"/>
<node name="showResults" value="Показати результати"/>
<node name="showBooks" value="Показати книжки"/>
@ -57,6 +69,8 @@
<node name="deleteDemo" value="Видалити уривок"/>
<node name="downloadDemo" value="Завантажити уривок"/>
<node name="buy" value="Придбати (%s)"/>
<node name="addToBasket" value="Add to basket" toBeTranslated="true"/>
<node name="removeFromBasket" value="Remove from basket" toBeTranslated="true"/>
<node name="openInBrowser" value="Показати у браузері" />
<node name="stopLoading" value="Припинити завантаження" />
<node name="stopSearching" value="Припинити пошук" />
@ -109,6 +123,7 @@
<node name="series" value="Серія:" />
<node name="indexInSeries" value="Номер у серії:" />
<node name="tags" value="Категорії:" />
<node name="catalog" value="Catalog:" toBeTranslated="true"/>
</node>
<node name="bookInfo">
<node name="bookInfo" value="Інформація про книгу" />
@ -559,10 +574,6 @@
<node name="noRequiredInformation" value="Необхідна інформація не знайдена в каталозі" />
<node name="cacheDirectoryError" value="Не вдається створити каталог для кеша" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Інформація" />
<node name="message" value="Каталог порожній" />
</node>
<node name="emptySearchResults">
<node name="title" value="Результати пошуку" />
<node name="message" value="Відповідних книг не знайдено" />
@ -610,6 +621,8 @@
<node name="dictionaryIsNotInstalled" value="Dictionary is not installed, sorry" toBeTranslated="true"/>
<node name="permissionDenied" value="Permission denied, sorry" toBeTranslated="true"/>
<node name="noFavorites" value="Your favorites list is empty, sorry" toBeTranslated="true"/>
<node name="emptyCatalog" value="Каталог порожній"/>
<node name="emptyBasket" value="Your basket is empty, sorry" toBeTranslated="true"/>
</node>
<node name="external">
<node name="browser" value="браузері"/>

View file

@ -47,6 +47,18 @@
<node name="removeFromFavorites" value="Xóa khỏi yêu thích"/>
</node>
<node name="networkView">
<node name="byAuthor" value="Theo tác giả">
<node name="summary" value="Sách được xếp theo tác giả"/>
</node>
<node name="byTitle" value="By title" toBeTranslated="true">
<node name="summary" value="Books sorted by title" toBeTranslated="true"/>
</node>
<node name="byDate" value="By date" toBeTranslated="true">
<node name="summary" value="Books sorted by date of purchasing" toBeTranslated="true"/>
</node>
<node name="bySeries" value="By series" toBeTranslated="true">
<node name="summary" value="Books sorted by series" toBeTranslated="true"/>
</node>
<node name="openCatalog" value="Mở danh mục"/>
<node name="showResults" value="Hiện kết quả"/>
<node name="showBooks" value="Show books"/>
@ -57,6 +69,8 @@
<node name="deleteDemo" value="Xóa bản thử"/>
<node name="downloadDemo" value="Tải về bản thử"/>
<node name="buy" value="Mua (%s)"/>
<node name="addToBasket" value="Add to basket" toBeTranslated="true"/>
<node name="removeFromBasket" value="Remove from basket" toBeTranslated="true"/>
<node name="openInBrowser" value="Mở trình duyệt" />
<node name="stopLoading" value="Dừng tải" />
<node name="stopSearching" value="Dừng tìm kiếm" />
@ -109,6 +123,7 @@
<node name="series" value="Bộ sách:" />
<node name="indexInSeries" value="Chỉ mục trong bộ:" />
<node name="tags" value="Từ khóa:" />
<node name="catalog" value="Catalog:" toBeTranslated="true"/>
</node>
<node name="bookInfo">
<node name="bookInfo" value="Thông tin sách" />
@ -559,10 +574,6 @@
<node name="noRequiredInformation" value="Thông tin yêu cầu không định vị được trong catalo" />
<node name="cacheDirectoryError" value="Không thể tạo thư mục đệm" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Thông tin" />
<node name="message" value="Danh mục rỗng" />
</node>
<node name="emptySearchResults">
<node name="title" value="Kết quả Tìm kiếm" />
<node name="message" value="Không tìm thấy sách thích hợp" />
@ -610,6 +621,8 @@
<node name="dictionaryIsNotInstalled" value="Xin lỗi, từ điển chưa được cài"/>
<node name="permissionDenied" value="Xin lỗi, từ chối cho phép "/>
<node name="noFavorites" value="Xin lỗi, danh sách yêu thích của bạn đang trống"/>
<node name="emptyCatalog" value="Danh mục rỗng"/>
<node name="emptyBasket" value="Your basket is empty, sorry" toBeTranslated="true"/>
</node>
<node name="external">
<node name="browser" value="Trình duyệt"/>

View file

@ -47,6 +47,18 @@
<node name="removeFromFavorites" value="从收藏中移除"/>
</node>
<node name="networkView">
<node name="byAuthor" value="作者">
<node name="summary" value="按作者排序" />
</node>
<node name="byTitle" value="By title" toBeTranslated="true">
<node name="summary" value="Books sorted by title" toBeTranslated="true"/>
</node>
<node name="byDate" value="By date" toBeTranslated="true">
<node name="summary" value="Books sorted by date of purchasing" toBeTranslated="true"/>
</node>
<node name="bySeries" value="By series" toBeTranslated="true">
<node name="summary" value="Books sorted by series" toBeTranslated="true"/>
</node>
<node name="openCatalog" value="打开书库目录" />
<node name="showResults" value="显示结果"/>
<node name="showBooks" value="显示书籍"/>
@ -57,6 +69,8 @@
<node name="deleteDemo" value="删除试读本" />
<node name="downloadDemo" value="下载试读本" />
<node name="buy" value="购买(%s)" />
<node name="addToBasket" value="Add to basket" toBeTranslated="true"/>
<node name="removeFromBasket" value="Remove from basket" toBeTranslated="true"/>
<node name="openInBrowser" value="在浏览器中打开" />
<node name="stopLoading" value="停止加载" />
<node name="stopSearching" value="停止搜索" />
@ -109,6 +123,7 @@
<node name="series" value="系列:" />
<node name="indexInSeries" value="系列序号:" />
<node name="tags" value="标签:" />
<node name="catalog" value="Catalog:" toBeTranslated="true"/>
</node>
<node name="bookInfo">
<node name="bookInfo" value="书籍信息" />
@ -559,10 +574,6 @@
<node name="noRequiredInformation" value="书库目录中必须的信息没有定义" />
<node name="cacheDirectoryError" value="无法创建缓存目录" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="信息" />
<node name="message" value="空书库目录" />
</node>
<node name="emptySearchResults">
<node name="title" value="搜索结果" />
<node name="message" value="没有找到对应的书籍." />
@ -610,6 +621,8 @@
<node name="dictionaryIsNotInstalled" value="抱歉,你没有安装字典."/>
<node name="permissionDenied" value="对不起,没有权限"/>
<node name="noFavorites" value="对不起,你的收藏列表为空"/>
<node name="emptyCatalog" value="空书库目录"/>
<node name="emptyBasket" value="Your basket is empty, sorry" toBeTranslated="true"/>
</node>
<node name="external">
<node name="browser" value="浏览器" />

View file

@ -75,7 +75,7 @@
<node name="pt" value="โปรตุเกส"/>
<node name="ru" value="รัสเซีย"/>
<node name="sv" value="สวีเดน"/>
<node name="th" value="Thai" toBeTranslated="true"/>
<node name="th" value="ไทย" />
<node name="tr" value="ตุรกี"/>
<node name="uk" value="ยูเครน"/>
<node name="vi" value="เวียดนาม"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -101,116 +101,12 @@
android:paddingRight="10dp"
android:paddingTop="10dp"
>
<LinearLayout
android:id="@+id/network_book_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="10dp"
>
<TextView
android:id="@+id/network_book_title_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"
android:paddingRight="10dp"
/>
<TextView
android:id="@+id/network_book_title_value"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/network_book_authors"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="10dp"
>
<TextView
android:id="@+id/network_book_authors_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"
android:paddingRight="10dp"
/>
<TextView
android:id="@+id/network_book_authors_value"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/network_book_series"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="10dp"
>
<TextView
android:id="@+id/network_book_series_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"
android:paddingRight="10dp"
/>
<TextView
android:id="@+id/network_book_series_value"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/network_book_series_index"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="10dp"
>
<TextView
android:id="@+id/network_book_series_index_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"
android:paddingRight="10dp"
/>
<TextView
android:id="@+id/network_book_series_index_value"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/network_book_tags"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="10dp"
>
<TextView
android:id="@+id/network_book_tags_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"
android:paddingRight="10dp"
/>
<TextView
android:id="@+id/network_book_tags_value"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
</LinearLayout>
<include layout="@layout/book_info_pair" android:id="@+id/network_book_title" />
<include layout="@layout/book_info_pair" android:id="@+id/network_book_authors" />
<include layout="@layout/book_info_pair" android:id="@+id/network_book_series_title" />
<include layout="@layout/book_info_pair" android:id="@+id/network_book_series_index" />
<include layout="@layout/book_info_pair" android:id="@+id/network_book_tags" />
<include layout="@layout/book_info_pair" android:id="@+id/network_book_catalog" />
</LinearLayout>
<TextView
android:id="@+id/network_book_description_title"

View file

@ -21,7 +21,17 @@ public final class ZipFile {
}
private final InputStreamHolder myStreamHolder;
private final LinkedHashMap<String,LocalFileHeader> myFileHeaders = new LinkedHashMap<String,LocalFileHeader>();
private final LinkedHashMap<String,LocalFileHeader> myFileHeaders = new LinkedHashMap<String,LocalFileHeader>() {
@Override
public LocalFileHeader get(Object key) {
return super.get(((String)key).toLowerCase());
}
@Override
public LocalFileHeader put(String key, LocalFileHeader value) {
return super.put(key.toLowerCase(), value);
}
};
private boolean myAllFilesAreRead;
@ -50,7 +60,7 @@ public final class ZipFile {
}
if (header.FileName != null) {
myFileHeaders.put(header.FileName, header);
if (header.FileName.equals(fileToFind)) {
if (header.FileName.equalsIgnoreCase(fileToFind)) {
return true;
}
}

View file

@ -149,7 +149,7 @@ public class BookInfoActivity extends Activity {
}
private void setupInfoPair(int id, String key, CharSequence value) {
LinearLayout layout = (LinearLayout)findViewById(id);
final LinearLayout layout = (LinearLayout)findViewById(id);
if (value == null || value.length() == 0) {
layout.setVisibility(View.GONE);
return;

View file

@ -20,6 +20,7 @@
package org.geometerplus.android.fbreader;
import android.app.SearchManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@ -152,6 +153,7 @@ public final class FBReader extends ZLAndroidActivity {
}
};
UIUtil.wait("search", runnable, this);
startActivity(new Intent(this, getClass()));
} else {
super.onNewIntent(intent);
}

View file

@ -37,6 +37,7 @@ import org.geometerplus.zlibrary.text.view.ZLTextFixedPosition;
import org.geometerplus.fbreader.library.*;
import org.geometerplus.android.util.UIUtil;
import org.geometerplus.android.util.SQLiteUtil;
public final class SQLiteBooksDatabase extends BooksDatabase {
private final String myInstanceId;
@ -107,29 +108,6 @@ public final class SQLiteBooksDatabase extends BooksDatabase {
}, context);
}
private static void bindString(SQLiteStatement statement, int index, String value) {
if (value != null) {
statement.bindString(index, value);
} else {
statement.bindNull(index);
}
}
private static void bindDate(SQLiteStatement statement, int index, Date value) {
if (value != null) {
statement.bindLong(index, value.getTime());
} else {
statement.bindNull(index);
}
}
private static Date getDate(Cursor cursor, int index) {
if (cursor.isNull(index)) {
return null;
}
return new Date(cursor.getLong(index));
}
@Override
protected Book loadBook(long bookId) {
Book book = null;
@ -278,8 +256,8 @@ public final class SQLiteBooksDatabase extends BooksDatabase {
);
}
myUpdateBookInfoStatement.bindLong(1, fileId);
bindString(myUpdateBookInfoStatement, 2, encoding);
bindString(myUpdateBookInfoStatement, 3, language);
SQLiteUtil.bindString(myUpdateBookInfoStatement, 2, encoding);
SQLiteUtil.bindString(myUpdateBookInfoStatement, 3, language);
myUpdateBookInfoStatement.bindString(4, title);
myUpdateBookInfoStatement.bindLong(5, bookId);
myUpdateBookInfoStatement.execute();
@ -292,8 +270,8 @@ public final class SQLiteBooksDatabase extends BooksDatabase {
"INSERT OR IGNORE INTO Books (encoding,language,title,file_id) VALUES (?,?,?,?)"
);
}
bindString(myInsertBookInfoStatement, 1, encoding);
bindString(myInsertBookInfoStatement, 2, language);
SQLiteUtil.bindString(myInsertBookInfoStatement, 1, encoding);
SQLiteUtil.bindString(myInsertBookInfoStatement, 2, language);
myInsertBookInfoStatement.bindString(3, title);
final FileInfoSet infoSet = new FileInfoSet(file);
myInsertBookInfoStatement.bindLong(4, infoSet.getId(file));
@ -709,9 +687,9 @@ public final class SQLiteBooksDatabase extends BooksDatabase {
cursor.getLong(1),
cursor.getString(2),
cursor.getString(3),
getDate(cursor, 4),
getDate(cursor, 5),
getDate(cursor, 6),
SQLiteUtil.getDate(cursor, 4),
SQLiteUtil.getDate(cursor, 5),
SQLiteUtil.getDate(cursor, 6),
(int)cursor.getLong(7),
cursor.getString(8),
(int)cursor.getLong(9),
@ -737,9 +715,9 @@ public final class SQLiteBooksDatabase extends BooksDatabase {
cursor.getLong(1),
cursor.getString(2),
cursor.getString(3),
getDate(cursor, 4),
getDate(cursor, 5),
getDate(cursor, 6),
SQLiteUtil.getDate(cursor, 4),
SQLiteUtil.getDate(cursor, 5),
SQLiteUtil.getDate(cursor, 6),
(int)cursor.getLong(7),
cursor.getString(8),
(int)cursor.getLong(9),
@ -775,11 +753,11 @@ public final class SQLiteBooksDatabase extends BooksDatabase {
statement.bindLong(1, bookmark.getBookId());
statement.bindString(2, bookmark.getText());
bindDate(statement, 3, bookmark.getTime(Bookmark.CREATION));
bindDate(statement, 4, bookmark.getTime(Bookmark.MODIFICATION));
bindDate(statement, 5, bookmark.getTime(Bookmark.ACCESS));
SQLiteUtil.bindDate(statement, 3, bookmark.getTime(Bookmark.CREATION));
SQLiteUtil.bindDate(statement, 4, bookmark.getTime(Bookmark.MODIFICATION));
SQLiteUtil.bindDate(statement, 5, bookmark.getTime(Bookmark.ACCESS));
statement.bindLong(6, bookmark.getAccessCount());
bindString(statement, 7, bookmark.ModelId);
SQLiteUtil.bindString(statement, 7, bookmark.ModelId);
statement.bindLong(8, bookmark.ParagraphIndex);
statement.bindLong(9, bookmark.ElementIndex);
statement.bindLong(10, bookmark.CharIndex);

View file

@ -53,7 +53,7 @@ public class ImageViewActivity extends Activity {
setContentView(new ImageView());
final Uri uri = getIntent().getData();
if ("imagefile".equals(uri.getScheme())) {
if (ZLFileImage.SCHEME.equals(uri.getScheme())) {
try {
final String[] data = uri.getPath().split("\000");
final ZLFileImage image = new ZLFileImage(

View file

@ -19,6 +19,8 @@
package org.geometerplus.android.fbreader.network;
import java.util.HashMap;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
@ -35,15 +37,14 @@ import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.fbreader.network.ICustomNetworkLink;
import org.geometerplus.fbreader.network.opds.OPDSLinkReader;
import org.geometerplus.fbreader.network.*;
import org.geometerplus.fbreader.network.opds.OPDSCustomLink;
import org.geometerplus.android.util.UIUtil;
public class AddCustomCatalogActivity extends Activity {
private ZLResource myResource;
private Integer myCatalogId;
private String myIcon;
private volatile ICustomNetworkLink myLink;
@Override
public void onCreate(Bundle icicle) {
@ -83,21 +84,16 @@ public class AddCustomCatalogActivity extends Activity {
);
final Intent intent = getIntent();
myLink = NetworkLibraryActivity.getLinkFromIntent(intent);
final Uri uri = intent.getData();
myCatalogId = ICustomNetworkLink.INVALID_ID;
if (uri != null) {
myCatalogId = intent.getIntExtra(
NetworkLibraryActivity.ADD_CATALOG_ID_KEY, myCatalogId
);
if (myCatalogId != ICustomNetworkLink.INVALID_ID) {
setTextById(R.id.add_custom_catalog_url, uri.toString());
setTextById(R.id.add_custom_catalog_title, intent.getStringExtra(NetworkLibraryActivity.ADD_CATALOG_TITLE_KEY));
setTextById(R.id.add_custom_catalog_summary, intent.getStringExtra(NetworkLibraryActivity.ADD_CATALOG_SUMMARY_KEY));
myIcon = intent.getStringExtra(NetworkLibraryActivity.ADD_CATALOG_ICON_KEY);
} else {
loadInfoByUri(uri);
}
if (myLink != null) {
setTextById(R.id.add_custom_catalog_url, myLink.getUrlInfo(INetworkLink.URL_MAIN).URL);
setTextById(R.id.add_custom_catalog_title, myLink.getTitle());
setTextById(R.id.add_custom_catalog_summary, myLink.getSummary());
setExtraFieldsVisibility(true);
} else if (uri != null) {
loadInfoByUri(uri);
} else {
setExtraFieldsVisibility(false);
}
@ -126,24 +122,22 @@ public class AddCustomCatalogActivity extends Activity {
setErrorByKey("invalidUrl");
return;
}
if (!getExtraFieldsVisibility()) {
if (myLink == null) {
loadInfoByUri(uri);
} else if (isEmptyString(title)) {
setErrorByKey("titleIsEmpty");
} else {
startActivity(
new Intent(
NetworkLibraryActivity.ADD_CATALOG,
uri,
AddCustomCatalogActivity.this,
NetworkLibraryActivity.class
)
.putExtra(NetworkLibraryActivity.ADD_CATALOG_TITLE_KEY, title)
.putExtra(NetworkLibraryActivity.ADD_CATALOG_SUMMARY_KEY, summary)
.putExtra(NetworkLibraryActivity.ADD_CATALOG_ICON_KEY, myIcon)
.putExtra(NetworkLibraryActivity.ADD_CATALOG_ID_KEY, myCatalogId)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP)
);
myLink.setTitle(title);
myLink.setSummary(summary);
Intent intent = new Intent(
NetworkLibraryActivity.ADD_CATALOG,
uri,
AddCustomCatalogActivity.this,
NetworkLibraryActivity.class
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
NetworkLibraryActivity.addLinkToIntent(intent, myLink);
startActivity(intent);
finish();
}
}
@ -152,10 +146,6 @@ public class AddCustomCatalogActivity extends Activity {
return s == null || s.length() == 0;
}
private boolean getExtraFieldsVisibility() {
return findViewById(R.id.add_custom_catalog_title_group).getVisibility() == View.VISIBLE;
}
private void setExtraFieldsVisibility(boolean show) {
final int visibility = show ? View.VISIBLE : View.GONE;
runOnUiThread(new Runnable() {
@ -244,17 +234,16 @@ public class AddCustomCatalogActivity extends Activity {
}
setTextById(R.id.add_custom_catalog_url, textUrl);
String siteName = uri.getHost();
final String siteName = uri.getHost();
if (isEmptyString(siteName)) {
setErrorByKey("invalidUrl");
return;
}
if (siteName.startsWith("www.")) {
siteName = siteName.substring(4);
}
final ICustomNetworkLink link =
OPDSLinkReader.createCustomLink(siteName, null, null, null, textUrl);
final HashMap<String,UrlInfo> infos = new HashMap<String,UrlInfo>();
infos.put(INetworkLink.URL_MAIN, new UrlInfo(textUrl));
myLink = new OPDSCustomLink(
ICustomNetworkLink.INVALID_ID, siteName, null, null, infos
);
final Runnable loadInfoRunnable = new Runnable() {
private String myError;
@ -262,20 +251,19 @@ public class AddCustomCatalogActivity extends Activity {
public void run() {
try {
myError = null;
link.reloadInfo();
myLink.reloadInfo(false);
} catch (ZLNetworkException e) {
myError = e.getMessage();
}
runOnUiThread(new Runnable() {
public void run() {
if (myError == null) {
setTextById(R.id.add_custom_catalog_title, link.getTitle());
setTextById(R.id.add_custom_catalog_summary, link.getSummary());
myIcon = link.getIcon();
setTextById(R.id.add_custom_catalog_title, myLink.getTitle());
setTextById(R.id.add_custom_catalog_summary, myLink.getSummary());
setExtraFieldsVisibility(true);
} else {
runErrorDialog(myError);
myIcon = null;
myLink = null;
}
}
});

View file

@ -25,10 +25,9 @@ import android.view.Menu;
import android.view.ContextMenu;
import org.geometerplus.fbreader.network.NetworkTree;
import org.geometerplus.fbreader.network.tree.AddCustomCatalogItemTree;
class AddCustomCatalogItemActions extends NetworkTreeActions {
public static final int RUN_ITEM_ID = 0;
@Override

View file

@ -19,44 +19,103 @@
package org.geometerplus.android.fbreader.network;
import android.app.Dialog;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.TextView;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.fbreader.network.INetworkLink;
import org.geometerplus.fbreader.network.NetworkLibrary;
import org.geometerplus.fbreader.network.authentication.NetworkAuthenticationManager;
import org.geometerplus.android.util.UIUtil;
class AuthenticationDialog {
private static AuthenticationDialog ourDialog;
class AuthenticationDialog extends NetworkDialog {
public AuthenticationDialog() {
super("AuthenticationDialog");
public static AuthenticationDialog getDialog() {
if (ourDialog == null) {
ourDialog = new AuthenticationDialog();
}
return ourDialog;
}
@Override
protected void clearData() {
private class DialogHandler extends Handler {
@Override
public void handleMessage(Message message) {
if (!NetworkView.Instance().isInitialized()) {
return;
}
final NetworkLibrary library = NetworkLibrary.Instance();
library.invalidateVisibility();
library.synchronize();
NetworkView.Instance().fireModelChanged();
if (message.what == -1) {
myErrorMessage = (String)message.obj;
myActivity.showDialog(0);
} else if (message.what == 1) {
if (myOnSuccessRunnable != null) {
myOnSuccessRunnable.run();
}
}
}
};
private final ZLResource myResource =
ZLResource.resource("dialog").getResource("AuthenticationDialog");
private INetworkLink myLink;
private String myErrorMessage;
private Runnable myOnSuccessRunnable;
private Activity myActivity;
private final DialogHandler myHandler = new DialogHandler();
public static void show(Activity activity, INetworkLink link, Runnable onSuccessRunnable) {
getDialog().showInternal(activity, link, onSuccessRunnable);
}
@Override
public View createLayout() {
private void showInternal(Activity activity, INetworkLink link, Runnable onSuccessRunnable) {
myLink = link;
myErrorMessage = null;
myOnSuccessRunnable = onSuccessRunnable;
activity.showDialog(0);
}
private void sendSuccess() {
myHandler.sendMessage(myHandler.obtainMessage(1, null));
}
private void sendCancel() {
myHandler.sendMessage(myHandler.obtainMessage(0, null));
}
private void sendError(String message) {
myHandler.sendMessage(myHandler.obtainMessage(-1, message));
}
private View createLayout() {
final View layout = myActivity.getLayoutInflater().inflate(R.layout.network_authentication_dialog, null);
((TextView) layout.findViewById(R.id.network_authentication_login_text)).setText(myResource.getResource("login").getValue());
((TextView) layout.findViewById(R.id.network_authentication_password_text)).setText(myResource.getResource("password").getValue());
((TextView)layout.findViewById(R.id.network_authentication_login_text)).setText(myResource.getResource("login").getValue());
((TextView)layout.findViewById(R.id.network_authentication_password_text)).setText(myResource.getResource("password").getValue());
final TextView registerText = (TextView) layout.findViewById(R.id.network_authentication_register);
final TextView registerText = (TextView)layout.findViewById(R.id.network_authentication_register);
registerText.setText(myResource.getResource("register").getValue());
registerText.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
//final NetworkAuthenticationManager mgr = myLink.authenticationManager();
if (Util.isRegistrationSupported(myActivity, myLink)) {
myActivity.dismissDialog(NetworkDialog.DIALOG_AUTHENTICATION);
myActivity.dismissDialog(0);
Util.runRegistrationDialog(myActivity, myLink);
}
}
@ -64,15 +123,14 @@ class AuthenticationDialog extends NetworkDialog {
return layout;
}
@Override
protected void onPositive(DialogInterface dialog) {
AlertDialog alert = (AlertDialog) dialog;
final String login = ((TextView) alert.findViewById(R.id.network_authentication_login)).getText().toString().trim();
final String password = ((TextView) alert.findViewById(R.id.network_authentication_password)).getText().toString();
private void onPositive(DialogInterface dialog) {
AlertDialog alert = (AlertDialog)dialog;
final String login = ((TextView)alert.findViewById(R.id.network_authentication_login)).getText().toString().trim();
final String password = ((TextView)alert.findViewById(R.id.network_authentication_password)).getText().toString();
if (login.length() == 0) {
final String err = myResource.getResource("loginIsEmpty").getValue();
sendError(true, false, err);
sendError(err);
return;
}
@ -87,37 +145,64 @@ class AuthenticationDialog extends NetworkDialog {
}
} catch (ZLNetworkException e) {
mgr.logOut();
sendError(true, false, e.getMessage());
sendError(e.getMessage());
return;
}
sendSuccess(false);
sendSuccess();
}
};
UIUtil.wait("authentication", runnable, myActivity);
}
@Override
protected void onNegative(DialogInterface dialog) {
private void onNegative(DialogInterface dialog) {
final NetworkAuthenticationManager mgr = myLink.authenticationManager();
final Runnable runnable = new Runnable() {
public void run() {
if (mgr.mayBeAuthorised(false)) {
mgr.logOut();
sendCancel(false);
sendCancel();
}
}
};
UIUtil.wait("signOut", runnable, myActivity);
}
@Override
public void prepareDialogInternal(Dialog dialog) {
public final Dialog createDialog(final Activity activity) {
myActivity = activity;
final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
onPositive(dialog);
} else {
onNegative(dialog);
}
}
};
final View layout = createLayout();
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) {
onNegative(dialog);
}
})
.create();
}
public final void prepareDialog(final Activity activity, Dialog dialog) {
myActivity = activity;
final NetworkAuthenticationManager mgr = myLink.authenticationManager();
((TextView) dialog.findViewById(R.id.network_authentication_login)).setText(mgr.UserNameOption.getValue());
((TextView) dialog.findViewById(R.id.network_authentication_password)).setText("");
((TextView)dialog.findViewById(R.id.network_authentication_login)).setText(mgr.UserNameOption.getValue());
((TextView)dialog.findViewById(R.id.network_authentication_password)).setText("");
final TextView error = (TextView) dialog.findViewById(R.id.network_authentication_error);
final TextView error = (TextView)dialog.findViewById(R.id.network_authentication_error);
if (myErrorMessage == null) {
error.setVisibility(View.GONE);
error.setText("");

View file

@ -29,7 +29,7 @@ import android.os.Message;
import android.os.Handler;
import org.geometerplus.fbreader.network.INetworkLink;
import org.geometerplus.fbreader.network.NetworkLibraryItem;
import org.geometerplus.fbreader.network.NetworkItem;
abstract class ItemsLoadingHandler extends Handler {
@ -37,20 +37,20 @@ abstract class ItemsLoadingHandler extends Handler {
private static final int WHAT_UPDATE_ITEMS = 0;
private static final int WHAT_FINISHED = 1;
private final LinkedList<NetworkLibraryItem> myItems = new LinkedList<NetworkLibraryItem>();
private final HashMap<INetworkLink, LinkedList<NetworkLibraryItem>> myUncommitedItems = new HashMap<INetworkLink, LinkedList<NetworkLibraryItem>>();
private final LinkedList<NetworkItem> myItems = new LinkedList<NetworkItem>();
private final HashMap<INetworkLink, LinkedList<NetworkItem>> myUncommitedItems = new HashMap<INetworkLink, LinkedList<NetworkItem>>();
private final Object myItemsMonitor = new Object();
private volatile boolean myFinishProcessed;
private final Object myFinishMonitor = new Object();
public final void addItem(INetworkLink link, NetworkLibraryItem item) {
public final void addItem(INetworkLink link, NetworkItem item) {
synchronized (myItemsMonitor) {
myItems.add(item);
LinkedList<NetworkLibraryItem> uncommited = myUncommitedItems.get(link);
LinkedList<NetworkItem> uncommited = myUncommitedItems.get(link);
if (uncommited == null) {
uncommited = new LinkedList<NetworkLibraryItem>();
uncommited = new LinkedList<NetworkItem>();
myUncommitedItems.put(link, uncommited);
}
uncommited.add(item);
@ -59,7 +59,7 @@ abstract class ItemsLoadingHandler extends Handler {
public final void commitItems(INetworkLink link) {
synchronized (myItemsMonitor) {
LinkedList<NetworkLibraryItem> uncommited = myUncommitedItems.get(link);
LinkedList<NetworkItem> uncommited = myUncommitedItems.get(link);
if (uncommited != null) {
uncommited.clear();
}
@ -98,9 +98,9 @@ abstract class ItemsLoadingHandler extends Handler {
}
private final void doProcessFinish(String errorMessage, boolean interrupted) {
HashSet<NetworkLibraryItem> uncommitedItems = new HashSet<NetworkLibraryItem>();
HashSet<NetworkItem> uncommitedItems = new HashSet<NetworkItem>();
synchronized (myUncommitedItems) {
for (LinkedList<NetworkLibraryItem> items: myUncommitedItems.values()) {
for (LinkedList<NetworkItem> items: myUncommitedItems.values()) {
uncommitedItems.addAll(items);
}
}
@ -124,9 +124,9 @@ abstract class ItemsLoadingHandler extends Handler {
// callbacks
public abstract void onUpdateItems(List<NetworkLibraryItem> items);
public abstract void onUpdateItems(List<NetworkItem> items);
public abstract void afterUpdateItems();
public abstract void onFinish(String errorMessage, boolean interrupted, Set<NetworkLibraryItem> uncommitedItems);
public abstract void onFinish(String errorMessage, boolean interrupted, Set<NetworkItem> uncommitedItems);
@Override

View file

@ -26,7 +26,7 @@ import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.fbreader.network.INetworkLink;
import org.geometerplus.fbreader.network.NetworkOperationData;
import org.geometerplus.fbreader.network.NetworkLibraryItem;
import org.geometerplus.fbreader.network.NetworkItem;
abstract class ItemsLoadingRunnable implements Runnable {
private final ItemsLoadingHandler myHandler;
@ -99,7 +99,7 @@ abstract class ItemsLoadingRunnable implements Runnable {
doLoading(new NetworkOperationData.OnNewItemListener() {
private long myUpdateTime;
private int myItemsNumber;
public void onNewItem(INetworkLink link, NetworkLibraryItem item) {
public void onNewItem(INetworkLink link, NetworkItem item) {
myHandler.addItem(link, item);
++myItemsNumber;
final long now = System.currentTimeMillis();

View file

@ -25,6 +25,7 @@ import android.os.Message;
import android.app.Service;
import android.content.Intent;
import org.geometerplus.fbreader.network.NetworkTree;
public class ItemsLoadingService extends Service {
@ -53,7 +54,7 @@ public class ItemsLoadingService extends Service {
super.onStart(intent, startId);
doStart();
final String key = intent.getStringExtra(ITEMS_LOADING_RUNNABLE_KEY);
final NetworkTree.Key key = (NetworkTree.Key)intent.getSerializableExtra(ITEMS_LOADING_RUNNABLE_KEY);
if (key == null) {
doStop();
return;

View file

@ -38,11 +38,12 @@ import org.geometerplus.zlibrary.ui.android.image.ZLAndroidImageData;
import org.geometerplus.fbreader.network.NetworkTree;
import org.geometerplus.fbreader.network.tree.NetworkBookTree;
import org.geometerplus.fbreader.network.tree.AddCustomCatalogItemTree;
import org.geometerplus.fbreader.network.tree.SearchItemTree;
import org.geometerplus.android.fbreader.tree.ZLAndroidTree;
abstract class NetworkBaseActivity extends ListActivity implements NetworkView.EventListener {
protected final ZLResource myResource = ZLResource.resource("networkView");
public BookDownloaderServiceConnection Connection;
@ -105,6 +106,14 @@ abstract class NetworkBaseActivity extends ListActivity implements NetworkView.E
};
private void setupCover(final ImageView coverView, NetworkTree tree, int width, int height) {
if (tree instanceof AddCustomCatalogItemTree) {
coverView.setImageResource(R.drawable.ic_list_plus);
return;
}
if (tree instanceof SearchItemTree) {
coverView.setImageResource(R.drawable.ic_list_searchresult);
return;
}
if (tree instanceof ZLAndroidTree) {
coverView.setImageResource(((ZLAndroidTree)tree).getCoverResourceId());
return;
@ -178,42 +187,31 @@ abstract class NetworkBaseActivity extends ListActivity implements NetworkView.E
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
NetworkTree tree = null;
System.err.println("onCreateContextMenu -1");
if (menuInfo != null) {
final int position = ((AdapterView.AdapterContextMenuInfo)menuInfo).position;
tree = (NetworkTree)getListAdapter().getItem(position);
} else {
tree = getDefaultTree();
}
if (tree != null) {
final NetworkTreeActions actions = NetworkView.Instance().getActions(tree);
if (actions != null) {
actions.buildContextMenu(this, menu, tree);
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;
}
}
}
}
private NetworkTree myDefaultTree;
protected NetworkTree getDefaultTree() {
return myDefaultTree;
}
protected void setDefaultTree(NetworkTree tree) {
myDefaultTree = tree;
super.onCreateContextMenu(menu, view, menuInfo);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
NetworkTree tree = null;
if (item != null && item.getMenuInfo() != null) {
final int position = ((AdapterView.AdapterContextMenuInfo)item.getMenuInfo()).position;
tree = (NetworkTree)getListAdapter().getItem(position);
} else {
tree = getDefaultTree();
}
if (tree != null) {
final NetworkTreeActions actions = NetworkView.Instance().getActions(tree);
if (actions != null && actions.runAction(this, tree, item.getItemId())) {
return true;
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;
}
}
}
return super.onContextItemSelected(item);
@ -260,7 +258,7 @@ abstract class NetworkBaseActivity extends ListActivity implements NetworkView.E
if (!NetworkView.Instance().isInitialized()) {
return null;
}
final NetworkDialog dlg = NetworkDialog.getDialog(id);
final AuthenticationDialog dlg = AuthenticationDialog.getDialog();
if (dlg != null) {
return dlg.createDialog(this);
}
@ -271,10 +269,10 @@ abstract class NetworkBaseActivity extends ListActivity implements NetworkView.E
protected void onPrepareDialog(int id, Dialog dialog) {
super.onPrepareDialog(id, dialog);
final NetworkDialog dlg = NetworkDialog.getDialog(id);
final AuthenticationDialog dlg = AuthenticationDialog.getDialog();
if (dlg != null) {
dlg.prepareDialog(this, dialog);
}
}
}
@Override

View file

@ -49,8 +49,6 @@ import org.geometerplus.fbreader.network.authentication.NetworkAuthenticationMan
class NetworkBookActions extends NetworkTreeActions {
private static final String PACKAGE = "org.geometerplus.android.fbreader.network";
public static final int DOWNLOAD_BOOK_ITEM_ID = 0;
public static final int DOWNLOAD_DEMO_ITEM_ID = 1;
public static final int READ_BOOK_ITEM_ID = 2;
@ -60,8 +58,11 @@ class NetworkBookActions extends NetworkTreeActions {
public static final int BUY_DIRECTLY_ITEM_ID = 6;
public static final int BUY_IN_BROWSER_ITEM_ID = 7;
public static final int SHOW_BOOK_ACTIVITY_ITEM_ID = 8;
public static final int SHOW_BOOKS_ITEM_ID = 9;
public static final int ADD_BOOK_TO_BASKET = 10;
public static final int REMOVE_BOOK_FROM_BASKET = 11;
public static final int ADD_CATALOG_TO_FAVORITES = 12;
public static final int REMOVE_CATALOG_FROM_FAVORITES = 13;
private static boolean useFullReferences(NetworkBookItem book) {
return book.reference(BookReference.Type.DOWNLOAD_FULL) != null ||
@ -184,6 +185,14 @@ class NetworkBookActions extends NetworkTreeActions {
final String price = ((BuyBookReference) reference).Price;
actions.add(new Action(id, "buy", price));
}
final Basket basket = book.Link.basket();
if (basket != null) {
if (basket.contains(book)) {
actions.add(new Action(REMOVE_BOOK_FROM_BASKET, "removeFromBasket"));
} else {
actions.add(new Action(ADD_BOOK_TO_BASKET, "addToBasket"));
}
}
}
return actions;
}
@ -215,28 +224,15 @@ class NetworkBookActions extends NetworkTreeActions {
public boolean runAction(NetworkBaseActivity activity, NetworkTree tree, int actionCode) {
if (tree instanceof NetworkAuthorTree || tree instanceof NetworkSeriesTree) {
switch (actionCode) {
case SHOW_BOOKS_ITEM_ID:
showBooks(activity, tree);
return true;
case SHOW_BOOKS_ITEM_ID:
NetworkView.Instance().openTree(activity, tree);
return true;
}
return false;
}
return runAction(activity, ((NetworkBookTree) tree).Book, actionCode);
}
private void showBooks(NetworkBaseActivity activity, NetworkTree tree) {
String key = null;
if (tree instanceof NetworkAuthorTree) {
key = PACKAGE + ".Authors:" + ((NetworkAuthorTree) tree).Author.DisplayName;
} else if (tree instanceof NetworkSeriesTree) {
key = PACKAGE + ".Series:" + ((NetworkSeriesTree) tree).SeriesTitle;
}
if (key != null) {
NetworkView.Instance().openTree(activity, tree, key);
}
}
static boolean runAction(Activity activity, NetworkBookItem book, int actionCode) {
switch (actionCode) {
case DOWNLOAD_BOOK_ITEM_ID:
@ -266,6 +262,12 @@ class NetworkBookActions extends NetworkTreeActions {
case SHOW_BOOK_ACTIVITY_ITEM_ID:
NetworkView.Instance().showBookInfoActivity(activity, book);
return true;
case ADD_BOOK_TO_BASKET:
book.Link.basket().add(book);
return true;
case REMOVE_BOOK_FROM_BASKET:
book.Link.basket().remove(book);
return true;
}
return false;
}
@ -450,7 +452,7 @@ class NetworkBookActions extends NetworkTreeActions {
}
} catch (ZLNetworkException e) {
}
NetworkDialog.show(activity, NetworkDialog.DIALOG_AUTHENTICATION, book.Link, buyRunnable);
AuthenticationDialog.show(activity, book.Link, buyRunnable);
}
private static void doBuyInBrowser(Activity activity, final NetworkBookItem book) {

View file

@ -29,6 +29,7 @@ import android.view.View;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.content.Intent;
import android.graphics.Bitmap;
@ -88,6 +89,14 @@ public class NetworkBookInfoActivity extends Activity implements NetworkView.Eve
return myMainView;
}
private void setTextById(int id, CharSequence text) {
((TextView)findViewById(id)).setText(text);
}
private void setTextFromResource(int id, String resourceKey) {
setTextById(id, myResource.getResource(resourceKey).getValue());
}
@Override
public void onDestroy() {
if (myConnection != null) {
@ -99,7 +108,7 @@ public class NetworkBookInfoActivity extends Activity implements NetworkView.Eve
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
new RefillAccountActions().buildContextMenu(this, menu, new RefillAccountTree(myBook.Link));
new RefillAccountActions().buildContextMenu(this, menu, myBook.Link);
}
@Override
@ -109,29 +118,39 @@ public class NetworkBookInfoActivity extends Activity implements NetworkView.Eve
}
private final void setupDescription() {
((TextView) findViewById(R.id.network_book_description_title)).setText(myResource.getResource("description").getValue());
setTextFromResource(R.id.network_book_description_title, "description");
final TextView descriptionView = (TextView) findViewById(R.id.network_book_description);
final String description;
if (myBook.Summary != null) {
description = myBook.Summary;
} else {
description = myResource.getResource("noDescription").getValue();
}
descriptionView.setText(description);
setTextById(R.id.network_book_description, description);
}
private void setPairLabelTextFromResource(int id, String resourceKey) {
final LinearLayout layout = (LinearLayout)findViewById(id);
((TextView)layout.findViewById(R.id.book_info_key))
.setText(myResource.getResource(resourceKey).getValue());
}
private void setPairValueText(int id, CharSequence text) {
final LinearLayout layout = (LinearLayout)findViewById(id);
((TextView)layout.findViewById(R.id.book_info_value)).setText(text);
}
private void setupInfo() {
((TextView) findViewById(R.id.network_book_info_title)).setText(myResource.getResource("bookInfo").getValue());
setTextFromResource(R.id.network_book_info_title, "bookInfo");
((TextView) findViewById(R.id.network_book_title_key)).setText(myResource.getResource("title").getValue());
((TextView) findViewById(R.id.network_book_authors_key)).setText(myResource.getResource("authors").getValue());
((TextView) findViewById(R.id.network_book_series_key)).setText(myResource.getResource("series").getValue());
((TextView) findViewById(R.id.network_book_series_index_key)).setText(myResource.getResource("indexInSeries").getValue());
((TextView) findViewById(R.id.network_book_tags_key)).setText(myResource.getResource("tags").getValue());
setPairLabelTextFromResource(R.id.network_book_title, "title");
setPairLabelTextFromResource(R.id.network_book_authors, "authors");
setPairLabelTextFromResource(R.id.network_book_series_title, "series");
setPairLabelTextFromResource(R.id.network_book_series_index, "indexInSeries");
setPairLabelTextFromResource(R.id.network_book_tags, "tags");
setPairLabelTextFromResource(R.id.network_book_catalog, "catalog");
((TextView) findViewById(R.id.network_book_title_value)).setText(myBook.Title);
setPairValueText(R.id.network_book_title, myBook.Title);
if (myBook.Authors.size() > 0) {
findViewById(R.id.network_book_authors).setVisibility(View.VISIBLE);
@ -142,22 +161,22 @@ public class NetworkBookInfoActivity extends Activity implements NetworkView.Eve
}
authorsText.append(author.DisplayName);
}
((TextView) findViewById(R.id.network_book_authors_value)).setText(authorsText);
setPairValueText(R.id.network_book_authors, authorsText);
} else {
findViewById(R.id.network_book_authors).setVisibility(View.GONE);
}
if (myBook.SeriesTitle != null) {
findViewById(R.id.network_book_series).setVisibility(View.VISIBLE);
((TextView) findViewById(R.id.network_book_series_value)).setText(myBook.SeriesTitle);
findViewById(R.id.network_book_series_title).setVisibility(View.VISIBLE);
setPairValueText(R.id.network_book_series_title, myBook.SeriesTitle);
if (myBook.IndexInSeries > 0) {
((TextView) findViewById(R.id.network_book_series_index_value)).setText(String.valueOf(myBook.IndexInSeries));
setPairValueText(R.id.network_book_series_index, String.valueOf(myBook.IndexInSeries));
findViewById(R.id.network_book_series_index).setVisibility(View.VISIBLE);
} else {
findViewById(R.id.network_book_series_index).setVisibility(View.GONE);
}
} else {
findViewById(R.id.network_book_series).setVisibility(View.GONE);
findViewById(R.id.network_book_series_title).setVisibility(View.GONE);
findViewById(R.id.network_book_series_index).setVisibility(View.GONE);
}
@ -170,15 +189,17 @@ public class NetworkBookInfoActivity extends Activity implements NetworkView.Eve
}
tagsText.append(tag);
}
((TextView) findViewById(R.id.network_book_tags_value)).setText(tagsText);
setPairValueText(R.id.network_book_tags, tagsText);
} else {
findViewById(R.id.network_book_tags).setVisibility(View.GONE);
}
setPairValueText(R.id.network_book_catalog, myBook.Link.getTitle());
}
private final void setupCover() {
final View rootView = findViewById(R.id.network_book_root);
final ImageView coverView = (ImageView) findViewById(R.id.network_book_cover);
final ImageView coverView = (ImageView)findViewById(R.id.network_book_cover);
final DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
@ -232,9 +253,11 @@ public class NetworkBookInfoActivity extends Activity implements NetworkView.Eve
};
final Set<NetworkBookActions.Action> actions = NetworkBookActions.getContextMenuActions(myBook, myConnection);
final boolean skipSecondButton = actions.size() < buttons.length && (actions.size() % 2) == 1;
final boolean skipSecondButton =
actions.size() < buttons.length &&
actions.size() % 2 == 1;
int buttonNumber = 0;
for (final NetworkBookActions.Action a: actions) {
for (final NetworkBookActions.Action a : actions) {
if (skipSecondButton && buttonNumber == 1) {
++buttonNumber;
}
@ -250,7 +273,7 @@ public class NetworkBookInfoActivity extends Activity implements NetworkView.Eve
}
final int buttonId = buttons[buttonNumber++];
TextView button = (TextView) findViewById(buttonId);
TextView button = (TextView)findViewById(buttonId);
button.setText(text);
button.setVisibility(View.VISIBLE);
button.setOnClickListener(new View.OnClickListener() {
@ -305,7 +328,7 @@ public class NetworkBookInfoActivity extends Activity implements NetworkView.Eve
if (!NetworkView.Instance().isInitialized()) {
return null;
}
final NetworkDialog dlg = NetworkDialog.getDialog(id);
final AuthenticationDialog dlg = AuthenticationDialog.getDialog();
if (dlg != null) {
return dlg.createDialog(this);
}
@ -316,9 +339,9 @@ public class NetworkBookInfoActivity extends Activity implements NetworkView.Eve
protected void onPrepareDialog(int id, Dialog dialog) {
super.onPrepareDialog(id, dialog);
final NetworkDialog dlg = NetworkDialog.getDialog(id);
final AuthenticationDialog dlg = AuthenticationDialog.getDialog();
if (dlg != null) {
dlg.prepareDialog(this, dialog);
}
}
}
}

View file

@ -30,7 +30,6 @@ import android.os.Handler;
import android.view.Menu;
import android.view.ContextMenu;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.core.util.ZLBoolean3;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
@ -41,11 +40,11 @@ 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;
@ -57,6 +56,9 @@ class NetworkCatalogActions extends NetworkTreeActions {
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;
@ -72,14 +74,15 @@ class NetworkCatalogActions extends NetworkTreeActions {
@Override
public void buildContextMenu(Activity activity, ContextMenu menu, NetworkTree tree) {
final NetworkCatalogTree catalogTree = (NetworkCatalogTree) tree;
final NetworkCatalogTree catalogTree = (NetworkCatalogTree)tree;
final NetworkCatalogItem item = catalogTree.Item;
menu.setHeaderTitle(tree.getName());
boolean hasItems = false;
final String catalogUrl = item.URLByType.get(NetworkCatalogItem.URL_CATALOG);
if (catalogUrl != null) {
if (catalogUrl != null &&
(!(item instanceof BasketItem) || item.Link.basket().bookIds().size() > 0)) {
addMenuItem(menu, OPEN_CATALOG_ITEM_ID, "openCatalog");
hasItems = true;
}
@ -154,6 +157,10 @@ class NetworkCatalogActions extends NetworkTreeActions {
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;
}
@ -162,11 +169,11 @@ class NetworkCatalogActions extends NetworkTreeActions {
final NetworkCatalogTree catalogTree = (NetworkCatalogTree) tree;
final NetworkCatalogItem item = catalogTree.Item;
final String catalogUrl = item.URLByType.get(NetworkCatalogItem.URL_CATALOG);
final boolean isLoading = (catalogUrl != null) ?
NetworkView.Instance().containsItemsLoadingRunnable(catalogUrl) : false;
final boolean isLoading =
NetworkView.Instance().containsItemsLoadingRunnable(tree.getUniqueKey());
prepareOptionsItem(menu, RELOAD_ITEM_ID, catalogUrl != null && !isLoading);
prepareOptionsItem(menu, RELOAD_ITEM_ID,
item.URLByType.get(NetworkCatalogItem.URL_CATALOG) != null && !isLoading);
boolean signIn = false;
boolean signOut = false;
@ -202,7 +209,7 @@ class NetworkCatalogActions extends NetworkTreeActions {
case B3_TRUE:
return false;
case B3_UNDEFINED:
NetworkDialog.show(activity, NetworkDialog.DIALOG_AUTHENTICATION, ((NetworkCatalogTree)tree).Item.Link, new Runnable() {
AuthenticationDialog.show(activity, ((NetworkCatalogTree)tree).Item.Link, new Runnable() {
public void run() {
if (catalogTree.Item.getVisibility() != ZLBoolean3.B3_TRUE) {
return;
@ -222,50 +229,56 @@ class NetworkCatalogActions extends NetworkTreeActions {
if (consumeByVisibility(activity, tree, actionCode)) {
return true;
}
final NetworkCatalogTree catalogTree = (NetworkCatalogTree)tree;
switch (actionCode) {
case OPEN_CATALOG_ITEM_ID:
doExpandCatalog(activity, (NetworkCatalogTree)tree);
return true;
case OPEN_IN_BROWSER_ITEM_ID:
Util.openInBrowser(
activity,
((NetworkCatalogTree)tree).Item.URLByType.get(NetworkCatalogItem.URL_HTML_PAGE)
);
return true;
case RELOAD_ITEM_ID:
doReloadCatalog(activity, (NetworkCatalogTree)tree);
return true;
case SIGNIN_ITEM_ID:
NetworkDialog.show(activity, NetworkDialog.DIALOG_AUTHENTICATION, ((NetworkCatalogTree)tree).Item.Link, null);
return true;
case SIGNUP_ITEM_ID:
Util.runRegistrationDialog(activity, ((NetworkCatalogTree)tree).Item.Link);
return true;
case SIGNOUT_ITEM_ID:
doSignOut(activity, (NetworkCatalogTree)tree);
return true;
case REFILL_ACCOUNT_ITEM_ID:
new RefillAccountActions().runStandalone(activity, ((RefillAccountTree)activity.getDefaultTree()).Link);
return true;
case CUSTOM_CATALOG_EDIT:
{
final ICustomNetworkLink link =
(ICustomNetworkLink)((NetworkCatalogTree)tree).Item.Link;
final String textUrl = link.getLink(INetworkLink.URL_MAIN);
if (textUrl != null) {
activity.startActivity(
new Intent(activity, AddCustomCatalogActivity.class)
.setData(Uri.parse(textUrl))
.putExtra(NetworkLibraryActivity.ADD_CATALOG_TITLE_KEY, link.getTitle())
.putExtra(NetworkLibraryActivity.ADD_CATALOG_SUMMARY_KEY, link.getSummary())
.putExtra(NetworkLibraryActivity.ADD_CATALOG_ICON_KEY, link.getIcon())
.putExtra(NetworkLibraryActivity.ADD_CATALOG_ID_KEY, link.getId())
);
final NetworkCatalogItem item = catalogTree.Item;
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:
Util.openInBrowser(
activity,
catalogTree.Item.URLByType.get(NetworkCatalogItem.URL_HTML_PAGE)
);
return true;
case RELOAD_ITEM_ID:
doReloadCatalog(activity, catalogTree);
return true;
case SIGNIN_ITEM_ID:
AuthenticationDialog.show(activity, catalogTree.Item.Link, null);
return true;
case SIGNUP_ITEM_ID:
Util.runRegistrationDialog(activity, catalogTree.Item.Link);
return true;
case SIGNOUT_ITEM_ID:
doSignOut(activity, catalogTree);
return true;
case REFILL_ACCOUNT_ITEM_ID:
new RefillAccountActions().runStandalone(activity, catalogTree.Item.Link);
return true;
case CUSTOM_CATALOG_EDIT:
{
final Intent intent = new Intent(activity, AddCustomCatalogActivity.class);
NetworkLibraryActivity.addLinkToIntent(
intent,
(ICustomNetworkLink)catalogTree.Item.Link
);
activity.startActivity(intent);
return true;
}
case CUSTOM_CATALOG_REMOVE:
removeCustomLink((ICustomNetworkLink)((NetworkCatalogTree)tree).Item.Link);
removeCustomLink((ICustomNetworkLink)catalogTree.Item.Link);
return true;
case BASKET_CLEAR:
catalogTree.Item.Link.basket().clear();
return true;
case BASKET_BUY_ALL_BOOKS:
return true;
}
return false;
@ -273,18 +286,17 @@ class NetworkCatalogActions extends NetworkTreeActions {
private static class ExpandCatalogHandler extends ItemsLoadingHandler {
private final String myKey;
private final NetworkTree.Key myKey;
private final NetworkCatalogTree myTree;
ExpandCatalogHandler(NetworkCatalogTree tree, String key) {
ExpandCatalogHandler(NetworkCatalogTree tree, NetworkTree.Key key) {
myTree = tree;
myKey = key;
}
@Override
public void onUpdateItems(List<NetworkLibraryItem> items) {
for (NetworkLibraryItem item: items) {
public void onUpdateItems(List<NetworkItem> items) {
for (NetworkItem item: items) {
myTree.ChildrenItems.add(item);
NetworkTreeFactory.createNetworkTree(myTree, item);
}
@ -299,7 +311,7 @@ class NetworkCatalogActions extends NetworkTreeActions {
@Override
public void onFinish(String errorMessage, boolean interrupted,
Set<NetworkLibraryItem> uncommitedItems) {
Set<NetworkItem> uncommitedItems) {
if (interrupted &&
(!myTree.Item.supportsResumeLoading() || errorMessage != null)) {
myTree.ChildrenItems.clear();
@ -320,30 +332,18 @@ class NetworkCatalogActions extends NetworkTreeActions {
}
private void afterUpdateCatalog(String errorMessage, boolean childrenEmpty) {
final ZLResource dialogResource = ZLResource.resource("dialog");
ZLResource boxResource = null;
if (!NetworkView.Instance().isInitialized()) {
return;
}
final NetworkCatalogActivity activity = NetworkView.Instance().getOpenedActivity(myKey);
if (activity == null) {
return;
}
String msg = null;
if (errorMessage != null) {
boxResource = dialogResource.getResource("networkError");
msg = errorMessage;
UIUtil.showErrorMessageText(activity, errorMessage);
} else if (childrenEmpty) {
// TODO: make ListView's empty view instead
boxResource = dialogResource.getResource("emptyCatalogBox");
msg = boxResource.getResource("message").getValue();
}
if (msg != null) {
if (NetworkView.Instance().isInitialized()) {
final NetworkCatalogActivity activity = NetworkView.Instance().getOpenedActivity(myKey);
if (activity != null) {
final ZLResource buttonResource = dialogResource.getResource("button");
new AlertDialog.Builder(activity)
.setTitle(boxResource.getResource("title").getValue())
.setMessage(msg)
.setIcon(0)
.setPositiveButton(buttonResource.getResource("ok").getValue(), null)
.create().show();
}
}
UIUtil.showErrorMessage(activity, "emptyCatalog");
}
}
}
@ -401,12 +401,9 @@ class NetworkCatalogActions extends NetworkTreeActions {
}
}
public void doExpandCatalog(final NetworkBaseActivity activity, final NetworkCatalogTree tree) {
final String url = tree.Item.URLByType.get(NetworkCatalogItem.URL_CATALOG);
if (url == null) {
throw new RuntimeException("That's impossible!!!");
}
NetworkView.Instance().tryResumeLoading(activity, tree, url, new Runnable() {
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()) {
@ -414,7 +411,7 @@ class NetworkCatalogActions extends NetworkTreeActions {
if (tree.Item.supportsResumeLoading()) {
resumeNotLoad = true;
} else {
NetworkView.Instance().openTree(activity, tree, url);
NetworkView.Instance().openTree(activity, tree);
return;
}
} else {
@ -424,15 +421,25 @@ class NetworkCatalogActions extends NetworkTreeActions {
}
}
final ExpandCatalogHandler handler = new ExpandCatalogHandler(tree, url);
/* 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,
url,
key,
new ExpandCatalogRunnable(handler, tree, true, resumeNotLoad)
);
processExtraData(activity, tree.Item.extraData(), new Runnable() {
public void run() {
NetworkView.Instance().openTree(activity, tree, url);
NetworkView.Instance().openTree(activity, tree);
}
});
}
@ -440,20 +447,17 @@ class NetworkCatalogActions extends NetworkTreeActions {
}
public void doReloadCatalog(NetworkBaseActivity activity, final NetworkCatalogTree tree) {
final String url = tree.Item.URLByType.get(NetworkCatalogItem.URL_CATALOG);
if (url == null) {
throw new RuntimeException("That's impossible!!!");
}
if (NetworkView.Instance().containsItemsLoadingRunnable(url)) {
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, url);
final ExpandCatalogHandler handler = new ExpandCatalogHandler(tree, key);
NetworkView.Instance().startItemsLoading(
activity,
url,
key,
new ExpandCatalogRunnable(handler, tree, false, false)
);
}
@ -484,7 +488,6 @@ class NetworkCatalogActions extends NetworkTreeActions {
private void removeCustomLink(ICustomNetworkLink link) {
final NetworkLibrary library = NetworkLibrary.Instance();
library.removeCustomLink(link);
library.updateChildren();
library.synchronize();
NetworkView.Instance().fireModelChangedAsync();
}

View file

@ -28,17 +28,15 @@ import android.content.Intent;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.fbreader.network.NetworkTree;
import org.geometerplus.fbreader.network.NetworkCatalogItem;
import org.geometerplus.fbreader.network.*;
import org.geometerplus.fbreader.network.tree.*;
import org.geometerplus.fbreader.tree.FBTree;
public class NetworkCatalogActivity extends NetworkBaseActivity implements UserRegistrationConstants {
public static final String CATALOG_LEVEL_KEY = "org.geometerplus.android.fbreader.network.CatalogLevel";
public static final String CATALOG_KEY_KEY = "org.geometerplus.android.fbreader.network.CatalogKey";
private NetworkTree myTree;
private String myCatalogKey;
private boolean myInProgress;
private volatile boolean myInProgress;
@Override
public void onCreate(Bundle icicle) {
@ -53,27 +51,49 @@ public class NetworkCatalogActivity extends NetworkBaseActivity implements UserR
}
final Intent intent = getIntent();
final int level = intent.getIntExtra(CATALOG_LEVEL_KEY, -1);
if (level == -1) {
throw new RuntimeException("Catalog's Level was not specified!!!");
final NetworkLibrary library = NetworkLibrary.Instance();
final NetworkTree.Key key = (NetworkTree.Key)intent.getSerializableExtra(CATALOG_KEY_KEY);
myTree = library.getTreeByKey(key);
if (myTree == null) {
throw new RuntimeException("Tree not found for key " + key);
}
myCatalogKey = intent.getStringExtra(CATALOG_KEY_KEY);
if (myCatalogKey == null) {
throw new RuntimeException("Catalog's Key was not specified!!!");
}
myTree = networkView.getOpenedTree(level);
networkView.setOpenedActivity(myCatalogKey, this);
networkView.setOpenedActivity(key, this);
setListAdapter(new CatalogAdapter());
getListView().invalidateViews();
setupTitle();
if (myTree instanceof NetworkCatalogTree &&
Util.isAccountRefillingSupported(this, ((NetworkCatalogTree)myTree).Item.Link)) {
setDefaultTree(new RefillAccountTree((NetworkCatalogTree)myTree));
}
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
if (menuInfo == null && myTree instanceof NetworkCatalogTree) {
final INetworkLink link = ((NetworkCatalogTree)myTree).Item.Link;
if (Util.isAccountRefillingSupported(this, link)) {
final RefillAccountActions 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) && myTree instanceof NetworkCatalogTree) {
final INetworkLink link = ((NetworkCatalogTree)myTree).Item.Link;
if (Util.isAccountRefillingSupported(this, link)) {
final RefillAccountActions actions = NetworkView.Instance().getTopUpActions();
if (actions != null && actions.runAction(this, link, item.getItemId())) {
return true;
}
}
}
return super.onContextItemSelected(item);
}
@Override
@ -112,25 +132,10 @@ public class NetworkCatalogActivity extends NetworkBaseActivity implements UserR
setProgressBarIndeterminateVisibility(myInProgress);
}
private static String getNetworkTreeKey(NetworkTree tree, boolean recursive) {
if (tree instanceof NetworkCatalogTree) {
return ((NetworkCatalogTree) tree).Item.URLByType.get(NetworkCatalogItem.URL_CATALOG);
} else if (tree instanceof SearchItemTree) {
return NetworkSearchActivity.SEARCH_RUNNABLE_KEY;
} else if (recursive && tree.Parent instanceof NetworkTree) {
if (tree instanceof NetworkAuthorTree
|| tree instanceof NetworkSeriesTree) {
return getNetworkTreeKey((NetworkTree) tree.Parent, true);
}
}
return null;
}
@Override
public void onDestroy() {
if (myTree != null && myCatalogKey != null && NetworkView.Instance().isInitialized()) {
NetworkView.Instance().setOpenedActivity(myCatalogKey, null);
if (myTree != null && NetworkView.Instance().isInitialized()) {
NetworkView.Instance().setOpenedActivity(myTree.getUniqueKey(), null);
}
super.onDestroy();
}
@ -141,44 +146,15 @@ public class NetworkCatalogActivity extends NetworkBaseActivity implements UserR
}
private final class CatalogAdapter extends BaseAdapter {
private ArrayList<NetworkTree> mySpecialItems;
public CatalogAdapter() {
if (myTree instanceof NetworkCatalogRootTree) {
final NetworkCatalogRootTree rootTree = (NetworkCatalogRootTree)myTree;
mySpecialItems = new ArrayList<NetworkTree>();
if (Util.isAccountRefillingSupported(NetworkCatalogActivity.this, rootTree.Item.Link)) {
mySpecialItems.add(new RefillAccountTree(rootTree));
}
if (mySpecialItems.size() > 0) {
mySpecialItems.trimToSize();
} else {
mySpecialItems = null;
}
}
}
public final int getCount() {
return myTree.subTrees().size() +
((mySpecialItems != null && !myInProgress) ? mySpecialItems.size() : 0);
return myTree.subTrees().size();
}
public final NetworkTree getItem(int position) {
if (position < 0) {
if (position < 0 || position >= myTree.subTrees().size()) {
return null;
}
if (position < myTree.subTrees().size()) {
return (NetworkTree) myTree.subTrees().get(position);
}
if (myInProgress) {
return null;
}
position -= myTree.subTrees().size();
if (mySpecialItems != null && position < mySpecialItems.size()) {
return mySpecialItems.get(position);
}
return null;
return (NetworkTree)myTree.subTrees().get(position);
}
public final long getItemId(int position) {
@ -192,18 +168,26 @@ public class NetworkCatalogActivity extends NetworkBaseActivity implements UserR
void onModelChanged() {
notifyDataSetChanged();
if (mySpecialItems != null) {
for (NetworkTree tree: mySpecialItems) {
tree.invalidateChildren(); // call to update secondString
for (FBTree child : myTree.subTrees()) {
if (child instanceof TopUpTree) {
child.invalidateChildren();
}
}
}
}
private static NetworkTree.Key getLoadableNetworkTreeKey(NetworkTree tree) {
if ((tree instanceof NetworkAuthorTree || tree instanceof NetworkSeriesTree)
&& tree.Parent instanceof NetworkTree) {
return getLoadableNetworkTreeKey((NetworkTree)tree.Parent);
}
return tree.getUniqueKey();
}
@Override
public void onModelChanged() {
final NetworkView networkView = NetworkView.Instance();
final String key = getNetworkTreeKey(myTree, true);
final NetworkTree.Key key = getLoadableNetworkTreeKey(myTree);
myInProgress = key != null && networkView.isInitialized() && networkView.containsItemsLoadingRunnable(key);
getListView().invalidateViews();
@ -227,9 +211,9 @@ public class NetworkCatalogActivity extends NetworkBaseActivity implements UserR
}
private void doStopLoading() {
final String key = getNetworkTreeKey(myTree, false);
if (key != null && NetworkView.Instance().isInitialized()) {
final ItemsLoadingRunnable runnable = NetworkView.Instance().getItemsLoadingRunnable(key);
if (NetworkView.Instance().isInitialized()) {
final ItemsLoadingRunnable runnable =
NetworkView.Instance().getItemsLoadingRunnable(myTree.getUniqueKey());
if (runnable != null) {
runnable.interruptLoading();
}

View file

@ -1,184 +0,0 @@
/*
* 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.TreeMap;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.fbreader.network.INetworkLink;
import org.geometerplus.fbreader.network.NetworkLibrary;
abstract class NetworkDialog {
// dialog identifiers
public static final int DIALOG_AUTHENTICATION = 0;
private static final TreeMap<Integer, NetworkDialog> ourInstances = new TreeMap<Integer, NetworkDialog>();
public static NetworkDialog getDialog(int id) {
NetworkDialog dlg = ourInstances.get(Integer.valueOf(id));
if (dlg == null) {
switch (id) {
case DIALOG_AUTHENTICATION:
dlg = new AuthenticationDialog();
break;
}
if (dlg != null) {
dlg.myId = id;
ourInstances.put(Integer.valueOf(id), dlg);
}
}
return dlg;
}
private class DialogHandler extends Handler {
public Message obtainMessage(int code, boolean invalidateLibrary, String message) {
return obtainMessage(code, invalidateLibrary ? 1 : 0, 0, message);
}
@Override
public void handleMessage(Message message) {
if (!NetworkView.Instance().isInitialized()) {
return;
}
final NetworkLibrary library = NetworkLibrary.Instance();
if (message.arg1 != 0) {
library.invalidateChildren();
}
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(myActivity)
.setTitle(boxResource.getResource("title").getValue())
.setMessage((String) message.obj)
.setIcon(0)
.setPositiveButton(buttonResource.getResource("ok").getValue(), null)
.create().show();
} else {
myErrorMessage = (String) message.obj;
myActivity.showDialog(myId);
return;
}
} else if (message.what > 0) {
if (myOnSuccessRunnable != null) {
myOnSuccessRunnable.run();
}
}
clearData();
}
};
protected final ZLResource myResource;
protected int myId;
protected INetworkLink myLink;
protected String myErrorMessage;
protected Runnable myOnSuccessRunnable;
protected Activity myActivity;
protected final DialogHandler myHandler = new DialogHandler();
public NetworkDialog(String key) {
myResource = ZLResource.resource("dialog").getResource(key);
}
public static void show(Activity activity, int id, INetworkLink link, Runnable onSuccessRunnable) {
getDialog(id).showInternal(activity, link, onSuccessRunnable);
}
private void showInternal(Activity activity, INetworkLink link, Runnable onSuccessRunnable) {
myLink = link;
myErrorMessage = null;
myOnSuccessRunnable = onSuccessRunnable;
activity.showDialog(myId);
}
protected void sendSuccess(boolean invalidateLibrary) {
myHandler.sendMessage(myHandler.obtainMessage(1, invalidateLibrary, null));
}
protected void sendCancel(boolean invalidateLibrary) {
myHandler.sendMessage(myHandler.obtainMessage(0, invalidateLibrary, null));
}
protected void sendError(boolean restart, boolean invalidateLibrary, String message) {
myHandler.sendMessage(myHandler.obtainMessage(restart ? -1 : -2, invalidateLibrary, message));
}
protected abstract View createLayout();
protected abstract void clearData();
protected abstract void onPositive(DialogInterface dialog);
protected abstract void onNegative(DialogInterface dialog);
public final Dialog createDialog(final Activity activity) {
myActivity = activity;
final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
onPositive(dialog);
} else {
onNegative(dialog);
}
}
};
final View layout = createLayout();
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) {
onNegative(dialog);
}
})
.create();
}
public final void prepareDialog(final Activity activity, Dialog dialog) {
myActivity = activity;
prepareDialogInternal(dialog);
}
protected abstract void prepareDialogInternal(Dialog dialog);
}

View file

@ -39,18 +39,41 @@ import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.android.util.UIUtil;
import org.geometerplus.fbreader.network.NetworkTree;
import org.geometerplus.fbreader.network.NetworkLibrary;
import org.geometerplus.fbreader.network.ICustomNetworkLink;
import org.geometerplus.fbreader.network.opds.OPDSLinkReader;
import org.geometerplus.fbreader.network.*;
import org.geometerplus.fbreader.network.opds.OPDSCustomLink;
public class NetworkLibraryActivity extends NetworkBaseActivity {
final static String ADD_CATALOG = "android.fbreader.action.ADD_CATALOG";
static final String ADD_CATALOG = "android.fbreader.action.ADD_CATALOG";
final static String ADD_CATALOG_TITLE_KEY = "title";
final static String ADD_CATALOG_SUMMARY_KEY = "summary";
final static String ADD_CATALOG_ICON_KEY = "icon";
final static String ADD_CATALOG_ID_KEY = "id";
private static final String ADD_CATALOG_TITLE_KEY = "title";
private static final String ADD_CATALOG_SUMMARY_KEY = "summary";
private static final String ADD_CATALOG_ID_KEY = "id";
private static final String ADD_CATALOG_URLS_MAP_KEY = "urls";
static void addLinkToIntent(Intent intent, ICustomNetworkLink link) {
final String textUrl = link.getUrlInfo(INetworkLink.URL_MAIN).URL;
intent.setData(Uri.parse(textUrl));
intent
.putExtra(ADD_CATALOG_TITLE_KEY, link.getTitle())
.putExtra(ADD_CATALOG_SUMMARY_KEY, link.getSummary())
.putExtra(ADD_CATALOG_ID_KEY, link.getId())
.putExtra(ADD_CATALOG_URLS_MAP_KEY, link.urlInfoMap());
}
static ICustomNetworkLink getLinkFromIntent(Intent intent) {
final Uri uri = intent.getData();
if (uri == null || !intent.hasExtra(ADD_CATALOG_ID_KEY)) {
return null;
}
return new OPDSCustomLink(
intent.getIntExtra(ADD_CATALOG_ID_KEY, ICustomNetworkLink.INVALID_ID),
uri.getHost(),
intent.getStringExtra(ADD_CATALOG_TITLE_KEY),
intent.getStringExtra(ADD_CATALOG_SUMMARY_KEY),
(HashMap<String,UrlInfo>)intent.getSerializableExtra(ADD_CATALOG_URLS_MAP_KEY)
);
}
private NetworkTree myTree;
private volatile Intent myIntent;
@ -73,25 +96,14 @@ public class NetworkLibraryActivity extends NetworkBaseActivity {
}
private void processIntent(Intent intent) {
final String action = intent.getAction();
if (ADD_CATALOG.equals(action)) {
final Uri uri = intent.getData();
final String title = intent.getStringExtra(ADD_CATALOG_TITLE_KEY);
final String summary = intent.getStringExtra(ADD_CATALOG_SUMMARY_KEY);
final String icon = intent.getStringExtra(ADD_CATALOG_ICON_KEY);
final int id = intent.getIntExtra(ADD_CATALOG_ID_KEY, ICustomNetworkLink.INVALID_ID);
if (uri == null || title == null) {
return;
}
final ICustomNetworkLink link = OPDSLinkReader.createCustomLink(
id, uri.getHost(), title, summary, icon, uri.toString()
);
if (ADD_CATALOG.equals(intent.getAction())) {
final ICustomNetworkLink link = getLinkFromIntent(intent);
System.err.println("LINK = " + link);
if (link != null) {
runOnUiThread(new Runnable() {
public void run() {
final NetworkLibrary library = NetworkLibrary.Instance();
library.addCustomLink(link);
library.updateChildren();
library.synchronize();
NetworkView.Instance().fireModelChangedAsync();
getListView().invalidateViews();
@ -103,13 +115,13 @@ public class NetworkLibraryActivity extends NetworkBaseActivity {
private void prepareView() {
if (myTree == null) {
myTree = NetworkLibrary.Instance().getTree();
myTree = NetworkLibrary.Instance().getRootTree();
setListAdapter(new LibraryAdapter());
getListView().invalidateViews();
}
}
private static Initializator myInitializator;
private static Initializator myInitializator;
@Override
public void onResume() {
@ -227,19 +239,11 @@ public class NetworkLibraryActivity extends NetworkBaseActivity {
if (!NetworkView.Instance().isInitialized()) {
return 0;
}
return myTree.subTrees().size() + 2; // subtrees + <search item>
return myTree.subTrees().size();
}
public final NetworkTree getItem(int position) {
final int size = myTree.subTrees().size();
if (position == 0) {
return NetworkView.Instance().getSearchItemTree();
} else if (position > 0 && position <= size) {
return (NetworkTree)myTree.subTrees().get(position - 1);
} else if (position == size + 1) {
return NetworkView.Instance().getAddCustomCatalogItemTree();
}
return null;
return (NetworkTree)myTree.subTrees().get(position);
}
public final long getItemId(int position) {
@ -274,11 +278,16 @@ public class NetworkLibraryActivity extends NetworkBaseActivity {
return true;
}
private static boolean searchIsInProgress() {
return NetworkView.Instance().containsItemsLoadingRunnable(
NetworkLibrary.Instance().getSearchItemTree().getUniqueKey()
);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
final boolean searchInProgress = NetworkView.Instance().containsItemsLoadingRunnable(NetworkSearchActivity.SEARCH_RUNNABLE_KEY);
menu.findItem(MENU_SEARCH).setEnabled(!searchInProgress);
menu.findItem(MENU_SEARCH).setEnabled(!searchIsInProgress());
return true;
}
@ -316,7 +325,6 @@ public class NetworkLibraryActivity extends NetworkBaseActivity {
}
}
library.setActiveLanguageCodes(newActiveCodes);
library.invalidateChildren();
library.synchronize();
NetworkView.Instance().fireModelChanged();
}
@ -346,7 +354,7 @@ public class NetworkLibraryActivity extends NetworkBaseActivity {
@Override
public boolean onSearchRequested() {
if (NetworkView.Instance().containsItemsLoadingRunnable(NetworkSearchActivity.SEARCH_RUNNABLE_KEY)) {
if (searchIsInProgress()) {
return false;
}
final NetworkLibrary library = NetworkLibrary.Instance();

View file

@ -32,12 +32,10 @@ import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.fbreader.network.*;
import org.geometerplus.fbreader.network.tree.SearchItemTree;
public class NetworkSearchActivity extends Activity {
public static final String SEARCH_RUNNABLE_KEY = "org.geometerplus.android.fbreader.network.NetworkSearchActivity";
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
@ -57,7 +55,6 @@ public class NetworkSearchActivity extends Activity {
}
private class SearchHandler extends ItemsLoadingHandler {
private final SearchItemTree myTree;
public SearchHandler(SearchItemTree tree) {
@ -65,11 +62,11 @@ public class NetworkSearchActivity extends Activity {
}
@Override
public void onUpdateItems(List<NetworkLibraryItem> items) {
public void onUpdateItems(List<NetworkItem> items) {
SearchResult result = myTree.getSearchResult();
for (NetworkLibraryItem item: items) {
for (NetworkItem item: items) {
if (item instanceof NetworkBookItem) {
result.addBook((NetworkBookItem) item);
result.addBook((NetworkBookItem)item);
}
}
}
@ -84,12 +81,12 @@ public class NetworkSearchActivity extends Activity {
@Override
public void onFinish(String errorMessage, boolean interrupted,
Set<NetworkLibraryItem> uncommitedItems) {
Set<NetworkItem> uncommitedItems) {
if (interrupted) {
myTree.setSearchResult(null);
} else {
myTree.updateSubTrees();
afterUpdateCatalog(errorMessage, myTree.getSearchResult().empty());
afterUpdateCatalog(errorMessage, myTree.getSearchResult().isEmpty());
}
if (NetworkView.Instance().isInitialized()) {
NetworkView.Instance().fireModelChangedAsync();
@ -99,27 +96,36 @@ public class NetworkSearchActivity extends Activity {
private void afterUpdateCatalog(String errorMessage, boolean childrenEmpty) {
final ZLResource dialogResource = ZLResource.resource("dialog");
ZLResource boxResource = null;
String msg = null;
String msg;
if (errorMessage != null) {
boxResource = dialogResource.getResource("networkError");
msg = errorMessage;
} else if (childrenEmpty) {
boxResource = dialogResource.getResource("emptySearchResults");
msg = boxResource.getResource("message").getValue();
} else {
return;
}
if (msg != null) {
if (NetworkView.Instance().isInitialized()) {
final NetworkCatalogActivity activity = NetworkView.Instance().getOpenedActivity(SEARCH_RUNNABLE_KEY);
if (activity != null) {
final ZLResource buttonResource = dialogResource.getResource("button");
new AlertDialog.Builder(activity)
.setTitle(boxResource.getResource("title").getValue())
.setMessage(msg)
.setIcon(0)
.setPositiveButton(buttonResource.getResource("ok").getValue(), null)
.create().show();
}
}
if (!NetworkView.Instance().isInitialized()) {
return;
}
final SearchItemTree tree = NetworkLibrary.Instance().getSearchItemTree();
if (tree == null) {
return;
}
final NetworkCatalogActivity activity =
NetworkView.Instance().getOpenedActivity(tree.getUniqueKey());
if (activity != null) {
final ZLResource buttonResource = dialogResource.getResource("button");
new AlertDialog.Builder(activity)
.setTitle(boxResource.getResource("title").getValue())
.setMessage(msg)
.setIcon(0)
.setPositiveButton(buttonResource.getResource("ok").getValue(), null)
.create().show();
}
}
}
@ -148,24 +154,24 @@ public class NetworkSearchActivity extends Activity {
final NetworkLibrary library = NetworkLibrary.Instance();
library.NetworkSearchPatternOption.setValue(pattern);
if (NetworkView.Instance().containsItemsLoadingRunnable(SEARCH_RUNNABLE_KEY)) {
final SearchItemTree tree = library.getSearchItemTree();
if (tree == null ||
NetworkView.Instance().containsItemsLoadingRunnable(tree.getUniqueKey())) {
return;
}
final String summary = ZLResource.resource("networkView").getResource("searchResults").getValue().replace("%s", pattern);
final SearchResult result = new SearchResult(summary);
final SearchItemTree tree = NetworkView.Instance().getSearchItemTree();
tree.setSearchResult(result);
NetworkView.Instance().fireModelChangedAsync();
final SearchHandler handler = new SearchHandler(tree);
NetworkView.Instance().startItemsLoading(
this,
SEARCH_RUNNABLE_KEY,
tree.getUniqueKey(),
new SearchRunnable(handler, pattern)
);
NetworkView.Instance().openTree(this, tree, SEARCH_RUNNABLE_KEY);
NetworkView.Instance().openTree(this, tree);
}
}

View file

@ -33,6 +33,7 @@ import android.view.Menu;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.fbreader.network.*;
import org.geometerplus.fbreader.network.tree.NetworkCatalogTree;
class NetworkView {
private static NetworkView ourInstance;
@ -44,12 +45,13 @@ class NetworkView {
return ourInstance;
}
private volatile boolean myInitialized;
private final ArrayList<NetworkTreeActions> myActions = new ArrayList<NetworkTreeActions>();
private RefillAccountActions myTopUpActions;
private NetworkView() {
}
private volatile boolean myInitialized;
public boolean isInitialized() {
return myInitialized;
}
@ -64,7 +66,8 @@ class NetworkView {
myActions.add(new NetworkBookActions());
myActions.add(new NetworkCatalogActions());
myActions.add(new SearchItemActions());
myActions.add(new RefillAccountActions());
myTopUpActions = new RefillAccountActions();
myActions.add(myTopUpActions);
myActions.add(new AddCustomCatalogItemActions());
myActions.trimToSize();
@ -85,10 +88,12 @@ class NetworkView {
}
/*
* NetworkLibraryItem's actions
* NetworkItem's actions
*/
private final ArrayList<NetworkTreeActions> myActions = new ArrayList<NetworkTreeActions>();
public RefillAccountActions getTopUpActions() {
return myTopUpActions;
}
public NetworkTreeActions getActions(NetworkTree tree) {
for (NetworkTreeActions actions: myActions) {
@ -140,9 +145,10 @@ class NetworkView {
* Code for loading network items (running items-loading service and managing items-loading runnables).
*/
private final HashMap<String, ItemsLoadingRunnable> myItemsLoadingRunnables = new HashMap<String, ItemsLoadingRunnable>();
private final HashMap<NetworkTree.Key,ItemsLoadingRunnable> myItemsLoadingRunnables =
new HashMap<NetworkTree.Key,ItemsLoadingRunnable>();
public void startItemsLoading(Context context, String key, ItemsLoadingRunnable runnable) {
public void startItemsLoading(Context context, NetworkTree.Key key, ItemsLoadingRunnable runnable) {
boolean doDownload = false;
synchronized (myItemsLoadingRunnables) {
if (!myItemsLoadingRunnables.containsKey(key)) {
@ -158,13 +164,13 @@ class NetworkView {
}
}
ItemsLoadingRunnable getItemsLoadingRunnable(String key) {
ItemsLoadingRunnable getItemsLoadingRunnable(NetworkTree.Key key) {
synchronized (myItemsLoadingRunnables) {
return myItemsLoadingRunnables.get(key);
}
}
void removeItemsLoadingRunnable(String key) {
void removeItemsLoadingRunnable(NetworkTree.Key key) {
synchronized (myItemsLoadingRunnables) {
ItemsLoadingRunnable runnable = myItemsLoadingRunnables.remove(key);
if (runnable != null) {
@ -173,14 +179,14 @@ class NetworkView {
}
}
public final boolean containsItemsLoadingRunnable(String key) {
public final boolean containsItemsLoadingRunnable(NetworkTree.Key key) {
return getItemsLoadingRunnable(key) != null;
}
public void tryResumeLoading(NetworkBaseActivity activity, NetworkTree tree, String key, Runnable expandRunnable) {
final ItemsLoadingRunnable runnable = getItemsLoadingRunnable(key);
public void tryResumeLoading(NetworkBaseActivity activity, NetworkCatalogTree tree, Runnable expandRunnable) {
final ItemsLoadingRunnable runnable = getItemsLoadingRunnable(tree.getUniqueKey());
if (runnable != null && runnable.tryResumeLoading()) {
openTree(activity, tree, key);
openTree(activity, tree);
return;
}
if (runnable == null) {
@ -264,30 +270,17 @@ class NetworkView {
* Opening Catalogs & managing opened catalogs stack
*/
private final LinkedList<NetworkTree> myOpenedStack = new LinkedList<NetworkTree>();
private final HashMap<String, NetworkCatalogActivity> myOpenedActivities = new HashMap<String, NetworkCatalogActivity>();
public void openTree(Context context, NetworkTree tree, String key) {
final int level = tree.Level - 1; // tree.Level == 1 for catalog's root element
if (level > myOpenedStack.size()) {
throw new RuntimeException("Unable to open catalog with Level greater than the number of opened catalogs.\n"
+ "Catalog: " + tree.getName() + "\n"
+ "Level: " + level + "\n"
+ "Opened catalogs: " + myOpenedStack.size());
}
while (level < myOpenedStack.size()) {
myOpenedStack.removeLast();
}
myOpenedStack.add(tree);
private final HashMap<NetworkTree.Key,NetworkCatalogActivity> myOpenedActivities =
new HashMap<NetworkTree.Key,NetworkCatalogActivity>();
public void openTree(Context context, NetworkTree tree) {
context.startActivity(
new Intent(context.getApplicationContext(), NetworkCatalogActivity.class)
.putExtra(NetworkCatalogActivity.CATALOG_LEVEL_KEY, level)
.putExtra(NetworkCatalogActivity.CATALOG_KEY_KEY, key)
.putExtra(NetworkCatalogActivity.CATALOG_KEY_KEY, tree.getUniqueKey())
);
}
void setOpenedActivity(String key, NetworkCatalogActivity activity) {
void setOpenedActivity(NetworkTree.Key key, NetworkCatalogActivity activity) {
if (activity == null) {
myOpenedActivities.remove(key);
} else {
@ -295,29 +288,7 @@ class NetworkView {
}
}
public NetworkCatalogActivity getOpenedActivity(String key) {
public NetworkCatalogActivity getOpenedActivity(NetworkTree.Key key) {
return myOpenedActivities.get(key);
}
public NetworkTree getOpenedTree(int level) {
if (level < 0 || level >= myOpenedStack.size()) {
return null;
}
return myOpenedStack.get(level);
}
/*
* Special view items item
*/
private final SearchItemTree mySearchItem = new SearchItemTree();
private final AddCustomCatalogItemTree myAddCustomCatalogItem = new AddCustomCatalogItemTree();
public SearchItemTree getSearchItemTree() {
return mySearchItem;
}
public AddCustomCatalogItemTree getAddCustomCatalogItemTree() {
return myAddCustomCatalogItem;
}
}

View file

@ -26,9 +26,9 @@ import android.view.ContextMenu;
import org.geometerplus.fbreader.network.INetworkLink;
import org.geometerplus.fbreader.network.NetworkTree;
import org.geometerplus.fbreader.network.tree.TopUpTree;
import org.geometerplus.fbreader.network.authentication.NetworkAuthenticationManager;
class RefillAccountActions extends NetworkTreeActions {
public static final int REFILL_VIA_SMS_ITEM_ID = 0;
public static final int REFILL_VIA_BROWSER_ITEM_ID = 1;
@ -36,14 +36,17 @@ class RefillAccountActions extends NetworkTreeActions {
@Override
public boolean canHandleTree(NetworkTree tree) {
return tree instanceof RefillAccountTree;
return tree instanceof TopUpTree;
}
@Override
public void buildContextMenu(Activity activity, ContextMenu menu, NetworkTree tree) {
buildContextMenu(activity, menu, ((TopUpTree)tree).Item.Link);
}
public void buildContextMenu(Activity activity, ContextMenu menu, INetworkLink link) {
menu.setHeaderTitle(getTitleValue("refillTitle"));
final INetworkLink link = ((RefillAccountTree)tree).Link;
if (Util.isSmsAccountRefillingSupported(activity, link)) {
addMenuItem(menu, REFILL_VIA_SMS_ITEM_ID, "refillViaSms");
}
@ -54,7 +57,7 @@ class RefillAccountActions extends NetworkTreeActions {
@Override
public int getDefaultActionCode(NetworkBaseActivity activity, NetworkTree tree) {
return getDefaultActionCode(activity, ((RefillAccountTree)tree).Link);
return getDefaultActionCode(activity, ((TopUpTree)tree).Item.Link);
}
private int getDefaultActionCode(Activity activity, INetworkLink link) {
final boolean sms = Util.isSmsAccountRefillingSupported(activity, link);
@ -86,9 +89,10 @@ class RefillAccountActions extends NetworkTreeActions {
@Override
public boolean runAction(NetworkBaseActivity activity, NetworkTree tree, int actionCode) {
final INetworkLink link = ((RefillAccountTree)tree).Link;
final INetworkLink link = ((TopUpTree)tree).Item.Link;
return runAction(activity, link, actionCode);
}
public boolean runAction(Activity activity, INetworkLink link, int actionCode) {
Runnable refillRunnable = null;
switch (actionCode) {
@ -131,7 +135,7 @@ class RefillAccountActions extends NetworkTreeActions {
if (mgr.mayBeAuthorised(false)) {
refiller.run();
} else {
NetworkDialog.show(activity, NetworkDialog.DIALOG_AUTHENTICATION, link, new Runnable() {
AuthenticationDialog.show(activity, link, new Runnable() {
public void run() {
if (mgr.mayBeAuthorised(false)) {
refiller.run();

View file

@ -30,6 +30,9 @@ import org.geometerplus.zlibrary.ui.android.library.ZLAndroidApplication;
import org.geometerplus.fbreader.network.ICustomNetworkLink;
import org.geometerplus.fbreader.network.NetworkDatabase;
import org.geometerplus.fbreader.network.UrlInfo;
import org.geometerplus.android.util.SQLiteUtil;
class SQLiteNetworkDatabase extends NetworkDatabase {
private final SQLiteDatabase myDatabase;
@ -41,7 +44,7 @@ class SQLiteNetworkDatabase extends NetworkDatabase {
private void migrate() {
final int version = myDatabase.getVersion();
final int currentCodeVersion = 2;
final int currentCodeVersion = 3;
if (version >= currentCodeVersion) {
return;
}
@ -51,6 +54,8 @@ class SQLiteNetworkDatabase extends NetworkDatabase {
createTables();
case 1:
updateTables1();
case 2:
updateTables2();
}
myDatabase.setTransactionSuccessful();
myDatabase.endTransaction();
@ -69,33 +74,30 @@ class SQLiteNetworkDatabase extends NetworkDatabase {
}
}
private static void bindString(SQLiteStatement statement, int index, String value) {
if (value != null) {
statement.bindString(index, value);
} else {
statement.bindNull(index);
}
}
@Override
protected void loadCustomLinks(ICustomLinksHandler handler) {
final Cursor cursor = myDatabase.rawQuery("SELECT link_id,title,site_name,summary,icon FROM CustomLinks", null);
final HashMap<String,String> linksMap = new HashMap<String,String>();
final Cursor cursor = myDatabase.rawQuery("SELECT link_id,title,site_name,summary FROM Links", null);
final HashMap<String,UrlInfo> linksMap = new HashMap<String,UrlInfo>();
while (cursor.moveToNext()) {
final int id = cursor.getInt(0);
final String title = cursor.getString(1);
final String siteName = cursor.getString(2);
final String summary = cursor.getString(3);
final String icon = cursor.getString(4);
linksMap.clear();
final Cursor linksCursor = myDatabase.rawQuery("SELECT key,url FROM CustomLinkUrls WHERE link_id = " + id, null);
final Cursor linksCursor = myDatabase.rawQuery("SELECT key,url,update_time FROM LinkUrls WHERE link_id = " + id, null);
while (linksCursor.moveToNext()) {
linksMap.put(linksCursor.getString(0), linksCursor.getString(1));
linksMap.put(
linksCursor.getString(0),
new UrlInfo(
linksCursor.getString(1),
SQLiteUtil.getDate(linksCursor, 2)
)
);
}
linksCursor.close();
handler.handleCustomLinkData(id, siteName, title, summary, icon, linksMap);
handler.handleCustomLinkData(id, siteName, title, summary, linksMap);
}
cursor.close();
}
@ -113,14 +115,14 @@ class SQLiteNetworkDatabase extends NetworkDatabase {
if (link.getId() == ICustomNetworkLink.INVALID_ID) {
if (myInsertCustomLinkStatement == null) {
myInsertCustomLinkStatement = myDatabase.compileStatement(
"INSERT INTO CustomLinks (title,site_name,summary,icon) VALUES (?,?,?,?)"
"INSERT INTO Links (title,site_name,summary) VALUES (?,?,?)"
);
}
statement = myInsertCustomLinkStatement;
} else {
if (myUpdateCustomLinkStatement == null) {
myUpdateCustomLinkStatement = myDatabase.compileStatement(
"UPDATE CustomLinks SET title = ?, site_name = ?, summary =?, icon = ? "
"UPDATE Links SET title = ?, site_name = ?, summary =? "
+ "WHERE link_id = ?"
);
}
@ -129,55 +131,61 @@ class SQLiteNetworkDatabase extends NetworkDatabase {
statement.bindString(1, link.getTitle());
statement.bindString(2, link.getSiteName());
bindString(statement, 3, link.getSummary());
bindString(statement, 4, link.getIcon());
SQLiteUtil.bindString(statement, 3, link.getSummary());
final long id;
final HashMap<String,String> linksMap = new HashMap<String,String>();
final HashMap<String,UrlInfo> linksMap = new HashMap<String,UrlInfo>();
if (statement == myInsertCustomLinkStatement) {
id = statement.executeInsert();
link.setId((int) id);
} else {
id = link.getId();
statement.bindLong(5, id);
statement.bindLong(4, id);
statement.execute();
final Cursor linksCursor = myDatabase.rawQuery("SELECT key,url FROM CustomLinkUrls WHERE link_id = " + link.getId(), null);
final Cursor linksCursor = myDatabase.rawQuery("SELECT key,url,update_time FROM LinkUrls WHERE link_id = " + link.getId(), null);
while (linksCursor.moveToNext()) {
linksMap.put(linksCursor.getString(0), linksCursor.getString(1));
linksMap.put(
linksCursor.getString(0),
new UrlInfo(
linksCursor.getString(1),
SQLiteUtil.getDate(linksCursor, 2)
)
);
}
linksCursor.close();
}
for (String key: link.getLinkKeys()) {
final String value = link.getLink(key);
final String dbValue = linksMap.remove(key);
for (String key : link.getUrlKeys()) {
final UrlInfo info = link.getUrlInfo(key);
final UrlInfo dbInfo = linksMap.remove(key);
final SQLiteStatement urlStatement;
if (dbValue == null) {
if (dbInfo == null) {
if (myInsertCustomLinkUrlStatement == null) {
myInsertCustomLinkUrlStatement = myDatabase.compileStatement(
"INSERT OR REPLACE INTO CustomLinkUrls(url,link_id,key) VALUES (?,?,?)");
"INSERT OR REPLACE INTO LinkUrls(url,update_time,link_id,key) VALUES (?,?,?,?)");
}
urlStatement = myInsertCustomLinkUrlStatement;
} else if (!value.equals(dbValue)) {
} else if (!info.equals(dbInfo)) {
if (myUpdateCustomLinkUrlStatement == null) {
myUpdateCustomLinkUrlStatement = myDatabase.compileStatement(
"UPDATE CustomLinkUrls SET url = ? WHERE link_id = ? AND key = ?");
"UPDATE LinkUrls SET url = ?, update_time = ? WHERE link_id = ? AND key = ?");
}
urlStatement = myUpdateCustomLinkUrlStatement;
} else {
continue;
}
urlStatement.bindString(1, value);
urlStatement.bindLong(2, id);
urlStatement.bindString(3, key);
SQLiteUtil.bindString(urlStatement, 1, info.URL);
SQLiteUtil.bindDate(urlStatement, 2, info.Updated);
urlStatement.bindLong(3, id);
urlStatement.bindString(4, key);
urlStatement.execute();
}
for (String key: linksMap.keySet()) {
if (myDeleteCustomLinkUrlStatement == null) {
myDeleteCustomLinkUrlStatement = myDatabase.compileStatement(
"DELETE FROM CustomLinkUrls WHERE link_id = ? AND key = ?");
"DELETE FROM LinkUrls WHERE link_id = ? AND key = ?");
}
myDeleteCustomLinkUrlStatement.bindLong(1, id);
myDeleteCustomLinkUrlStatement.bindString(2, key);
@ -199,14 +207,14 @@ class SQLiteNetworkDatabase extends NetworkDatabase {
final long id = link.getId();
if (myDeleteAllCustomLinksStatement == null) {
myDeleteAllCustomLinksStatement = myDatabase.compileStatement(
"DELETE FROM CustomLinkUrls WHERE link_id = ?");
"DELETE FROM LinkUrls WHERE link_id = ?");
}
myDeleteAllCustomLinksStatement.bindLong(1, id);
myDeleteAllCustomLinksStatement.execute();
if (myDeleteCustomLinkStatement == null) {
myDeleteCustomLinkStatement = myDatabase.compileStatement(
"DELETE FROM CustomLinks WHERE link_id = ?"
"DELETE FROM Links WHERE link_id = ?"
);
}
myDeleteCustomLinkStatement.bindLong(1, id);
@ -244,5 +252,34 @@ class SQLiteNetworkDatabase extends NetworkDatabase {
"icon TEXT)");
myDatabase.execSQL("INSERT INTO CustomLinks (link_id,title,site_name,summary,icon) SELECT link_id,title,site_name,summary,icon FROM CustomLinks_Obsolete");
myDatabase.execSQL("DROP TABLE CustomLinks_Obsolete");
myDatabase.execSQL(
"CREATE TABLE LinkUrls(" +
"key TEXT NOT NULL," +
"link_id INTEGER NOT NULL REFERENCES CustomLinks(link_id)," +
"url TEXT," +
"update_time INTEGER," +
"CONSTRAINT LinkUrls_PK PRIMARY KEY (key, link_id))");
myDatabase.execSQL("INSERT INTO LinkUrls (key,link_id,url) SELECT key,link_id,url FROM CustomLinkUrls");
myDatabase.execSQL("DROP TABLE CustomLinkUrls");
}
private void updateTables2() {
myDatabase.execSQL(
"CREATE TABLE Links(" +
"link_id INTEGER PRIMARY KEY," +
"title TEXT NOT NULL," +
"site_name TEXT NOT NULL," +
"summary TEXT)");
myDatabase.execSQL("INSERT INTO Links (link_id,title,site_name,summary) SELECT link_id,title,site_name,summary FROM CustomLinks");
final Cursor cursor = myDatabase.rawQuery("SELECT link_id,icon FROM CustomLinks", null);
while (cursor.moveToNext()) {
final int id = cursor.getInt(0);
final String url = cursor.getString(1);
myDatabase.execSQL("INSERT INTO LinkUrls (key,link_id,url) VALUES " +
"('icon'," + id + ",'" + url + "')");
}
cursor.close();
myDatabase.execSQL("DROP TABLE CustomLinks");
}
}

View file

@ -25,7 +25,7 @@ import android.view.ContextMenu;
import org.geometerplus.fbreader.network.NetworkTree;
import org.geometerplus.fbreader.network.SearchResult;
import org.geometerplus.fbreader.network.tree.SearchItemTree;
class SearchItemActions extends NetworkTreeActions {
@ -39,7 +39,7 @@ class SearchItemActions extends NetworkTreeActions {
@Override
public String getTreeTitle(NetworkTree tree) {
final SearchResult result = ((SearchItemTree) tree).getSearchResult();
final SearchResult result = ((SearchItemTree)tree).getSearchResult();
if (result != null) {
return result.Summary;
}
@ -50,7 +50,7 @@ class SearchItemActions extends NetworkTreeActions {
public void buildContextMenu(Activity activity, ContextMenu menu, NetworkTree tree) {
menu.setHeaderTitle(tree.getName());
final boolean isLoading = NetworkView.Instance().containsItemsLoadingRunnable(NetworkSearchActivity.SEARCH_RUNNABLE_KEY);
final boolean isLoading = NetworkView.Instance().containsItemsLoadingRunnable(tree.getUniqueKey());
if (!isLoading) {
addMenuItem(menu, RUN_SEARCH_ITEM_ID, "search");
@ -61,7 +61,7 @@ class SearchItemActions extends NetworkTreeActions {
@Override
public int getDefaultActionCode(NetworkBaseActivity activity, NetworkTree tree) {
final boolean isLoading = NetworkView.Instance().containsItemsLoadingRunnable(NetworkSearchActivity.SEARCH_RUNNABLE_KEY);
final boolean isLoading = NetworkView.Instance().containsItemsLoadingRunnable(tree.getUniqueKey());
if (!isLoading) {
return RUN_SEARCH_ITEM_ID;
}

View file

@ -49,7 +49,7 @@ abstract class Util implements UserRegistrationConstants {
return testService(
activity,
REGISTRATION_ACTION,
link.getLink(INetworkLink.URL_SIGN_UP)
link.getUrlInfo(INetworkLink.URL_SIGN_UP).URL
);
}
@ -57,12 +57,12 @@ abstract class Util implements UserRegistrationConstants {
try {
final Intent intent = new Intent(
REGISTRATION_ACTION,
Uri.parse(link.getLink(INetworkLink.URL_SIGN_UP))
Uri.parse(link.getUrlInfo(INetworkLink.URL_SIGN_UP).URL)
);
if (PackageUtil.canBeStarted(activity, intent)) {
activity.startActivityForResult(new Intent(
REGISTRATION_ACTION,
Uri.parse(link.getLink(INetworkLink.URL_SIGN_UP))
Uri.parse(link.getUrlInfo(INetworkLink.URL_SIGN_UP).URL)
), USER_REGISTRATION_REQUEST_CODE);
}
} catch (ActivityNotFoundException e) {
@ -93,7 +93,7 @@ abstract class Util implements UserRegistrationConstants {
return testService(
activity,
SMS_REFILLING_ACTION,
link.getLink(INetworkLink.URL_MAIN)
link.getUrlInfo(INetworkLink.URL_MAIN).URL
);
}
@ -101,7 +101,7 @@ abstract class Util implements UserRegistrationConstants {
try {
final Intent intent = new Intent(
SMS_REFILLING_ACTION,
Uri.parse(link.getLink(INetworkLink.URL_MAIN))
Uri.parse(link.getUrlInfo(INetworkLink.URL_MAIN).URL)
);
final NetworkAuthenticationManager mgr = link.authenticationManager();
if (mgr != null) {
@ -117,7 +117,7 @@ abstract class Util implements UserRegistrationConstants {
}
static boolean isBrowserAccountRefillingSupported(Activity activity, INetworkLink link) {
return link.getLink(INetworkLink.URL_REFILL_ACCOUNT) != null;
return link.getUrlInfo(INetworkLink.URL_REFILL_ACCOUNT).URL != null;
}
static void openInBrowser(Context context, String url) {

View file

@ -0,0 +1,50 @@
/*
* Copyright (C) 2009-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.util;
import java.util.Date;
import android.database.sqlite.SQLiteStatement;
import android.database.Cursor;
public abstract class SQLiteUtil {
public static void bindString(SQLiteStatement statement, int index, String value) {
if (value != null) {
statement.bindString(index, value);
} else {
statement.bindNull(index);
}
}
public static void bindDate(SQLiteStatement statement, int index, Date value) {
if (value != null) {
statement.bindLong(index, value.getTime());
} else {
statement.bindNull(index);
}
}
public static Date getDate(Cursor cursor, int index) {
if (cursor.isNull(index)) {
return null;
}
return new Date(cursor.getLong(index));
}
}

View file

@ -110,19 +110,21 @@ public abstract class UIUtil {
runner.start();
}
public static void showErrorMessageText(Context context, String text) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
}
public static void showErrorMessage(Context context, String resourceKey) {
Toast.makeText(
showErrorMessageText(
context,
ZLResource.resource("errorMessage").getResource(resourceKey).getValue(),
Toast.LENGTH_SHORT
).show();
ZLResource.resource("errorMessage").getResource(resourceKey).getValue()
);
}
public static void showErrorMessage(Context context, String resourceKey, String parameter) {
Toast.makeText(
showErrorMessageText(
context,
ZLResource.resource("errorMessage").getResource(resourceKey).getValue().replace("%s", parameter),
Toast.LENGTH_SHORT
).show();
ZLResource.resource("errorMessage").getResource(resourceKey).getValue().replace("%s", parameter)
);
}
}

View file

@ -32,7 +32,7 @@ class OEBAnnotationReader extends ZLXMLReaderAdapter implements XMLNamespaces {
private static final int READ_DESCRIPTION = 1;
private int myReadState;
private final StringBuffer myBuffer = new StringBuffer();
private final StringBuilder myBuffer = new StringBuilder();
String readAnnotation(ZLFile file) {
myReadState = READ_NONE;

View file

@ -83,7 +83,7 @@ class OEBMetaInfoReader extends ZLXMLReaderAdapter implements XMLNamespaces {
private int myReadState;
private boolean myReadMetaData;
private final StringBuffer myBuffer = new StringBuffer();
private final StringBuilder myBuffer = new StringBuilder();
@Override
public boolean processNamespaces() {

View file

@ -19,19 +19,19 @@
package org.geometerplus.fbreader.network;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.*;
import org.geometerplus.zlibrary.core.util.ZLMiscUtil;
import org.geometerplus.zlibrary.core.options.ZLStringListOption;
public abstract class AbstractNetworkLink implements INetworkLink {
public abstract class AbstractNetworkLink implements INetworkLink, Basket {
protected String mySiteName;
protected String myTitle;
protected String mySummary;
protected String myIcon;
protected final String myLanguage;
protected final TreeMap<String, String> myLinks;
protected final TreeMap<String,UrlInfo> myInfos;
private ZLStringListOption myBooksInBasketOption;
/**
* Creates new NetworkLink instance.
@ -39,65 +39,114 @@ public abstract class AbstractNetworkLink implements INetworkLink {
* @param siteName name of the corresponding website. Must be not <code>null</code>.
* @param title title of the corresponding library item. Must be not <code>null</code>.
* @param summary description of the corresponding library item. Can be <code>null</code>.
* @param icon string contains link's icon data/url. Can be <code>null</code>.
* @param language language of the catalog. If <code>null</code> we assume this catalog is multilanguage.
* @param links map contains URLs with their identifiers; must always contain one URL with <code>URL_MAIN</code> identifier
* @param infos map contains URL infos with their identifiers; must always contain one URL with <code>URL_MAIN</code> identifier
*/
public AbstractNetworkLink(String siteName, String title, String summary, String icon, String language, Map<String, String> links) {
public AbstractNetworkLink(String siteName, String title, String summary, String language, Map<String,UrlInfo> infos) {
mySiteName = siteName;
myTitle = title;
mySummary = summary;
myIcon = icon;
myLanguage = language != null ? language : "multi";
myLinks = new TreeMap<String, String>(links);
myInfos = new TreeMap<String,UrlInfo>(infos);
}
public String getSiteName() {
public final String getSiteName() {
return mySiteName;
}
public String getTitle() {
public final String getTitle() {
return myTitle;
}
public String getSummary() {
public final String getSummary() {
return mySummary;
}
public String getIcon() {
return myIcon;
}
public String getLanguage() {
public final String getLanguage() {
return myLanguage;
}
public String getLink(String urlKey) {
return myLinks.get(urlKey);
public final HashMap<String,UrlInfo> urlInfoMap() {
return new HashMap(myInfos);
}
public Set<String> getLinkKeys() {
return myLinks.keySet();
public final UrlInfo getUrlInfo(String urlKey) {
final UrlInfo info = myInfos.get(urlKey);
return info != null ? info : UrlInfo.NULL;
}
public NetworkOperationData createOperationData(INetworkLink link,
NetworkOperationData.OnNewItemListener listener) {
return new NetworkOperationData(link, listener);
public final Set<String> getUrlKeys() {
return myInfos.keySet();
}
public final void setSupportsBasket() {
if (myBooksInBasketOption == null) {
myBooksInBasketOption = new ZLStringListOption(mySiteName, "Basket", null);
}
}
public final Basket basket() {
return myBooksInBasketOption != null ? this : null;
}
// method from Basket interface
public final void add(NetworkBookItem book) {
if (book.Id != null && !"".equals(book.Id)) {
List<String> ids = myBooksInBasketOption.getValue();
if (!ids.contains(book.Id)) {
ids = new ArrayList(ids);
ids.add(book.Id);
myBooksInBasketOption.setValue(ids);
}
}
}
// method from Basket interface
public final void remove(NetworkBookItem book) {
if (book.Id != null && !"".equals(book.Id)) {
List<String> ids = myBooksInBasketOption.getValue();
if (ids.contains(book.Id)) {
ids = new ArrayList(ids);
ids.remove(book.Id);
myBooksInBasketOption.setValue(ids);
}
}
}
// method from Basket interface
public final void clear() {
myBooksInBasketOption.setValue(null);
}
// method from Basket interface
public final boolean contains(NetworkBookItem book) {
return myBooksInBasketOption.getValue().contains(book.Id);
}
// method from Basket interface
public final List<String> bookIds() {
return myBooksInBasketOption.getValue();
}
public NetworkOperationData createOperationData(NetworkOperationData.OnNewItemListener listener) {
return new NetworkOperationData(this, listener);
}
@Override
public String toString() {
String icon = myIcon;
if (icon.length() > 64) {
icon = icon.substring(0, 61) + "...";
String icon = getUrlInfo(URL_ICON).URL;
if (icon != null) {
if (icon.length() > 64) {
icon = icon.substring(0, 61) + "...";
}
icon = icon.replaceAll("\n", "");
}
icon = icon.replaceAll("\n", "");
return "AbstractNetworkLink: {"
+ "siteName=" + mySiteName
+ "; title=" + myTitle
+ "; summary=" + mySummary
+ "; icon=" + icon
+ "; links=" + myLinks
+ "; infos=" + myInfos
+ "}";
}
@ -114,8 +163,7 @@ public abstract class AbstractNetworkLink implements INetworkLink {
if (!mySiteName.equals(lnk.mySiteName)
|| !myTitle.equals(lnk.myTitle)
|| !ZLMiscUtil.equals(mySummary, lnk.mySummary)
|| !ZLMiscUtil.equals(myIcon, lnk.myIcon)
|| !ZLMiscUtil.mapsEquals(myLinks, lnk.myLinks)) {
|| !ZLMiscUtil.mapsEquals(myInfos, lnk.myInfos)) {
return false;
}
return true;

View file

@ -0,0 +1,30 @@
/*
* 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.fbreader.network;
import java.util.*;
public interface Basket {
void add(NetworkBookItem book);
void remove(NetworkBookItem book);
boolean contains(NetworkBookItem book);
List<String> bookIds();
void clear();
}

View file

@ -21,6 +21,8 @@ package org.geometerplus.fbreader.network;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import java.util.HashMap;
public interface ICustomNetworkLink extends INetworkLink {
public static final int INVALID_ID = -1;
@ -30,12 +32,13 @@ public interface ICustomNetworkLink extends INetworkLink {
void setSiteName(String name);
void setTitle(String title);
void setSummary(String summary);
void setIcon(String icon);
void setLink(String urlKey, String url);
void removeLink(String urlKey);
HashMap<String,UrlInfo> urlInfoMap();
void setUrl(String urlKey, String url);
void removeUrl(String urlKey);
void reloadInfo() throws ZLNetworkException;
boolean isObsolete(long milliSeconds);
void reloadInfo(boolean urlsOnly) throws ZLNetworkException;
// returns true if next methods have changed link's data:
// setSiteName, setTitle, setSummary, setIcon, setLink, removeLink

View file

@ -19,7 +19,7 @@
package org.geometerplus.fbreader.network;
import java.util.Set;
import java.util.*;
import org.geometerplus.zlibrary.core.network.ZLNetworkRequest;
@ -27,9 +27,9 @@ import org.geometerplus.fbreader.network.authentication.NetworkAuthenticationMan
public interface INetworkLink {
String URL_MAIN = "main";
String URL_SEARCH = "search";
String URL_ICON = "icon";
String URL_SIGN_IN = "signIn";
String URL_SIGN_OUT = "signOut";
String URL_SIGN_UP = "signUp";
@ -39,14 +39,20 @@ public interface INetworkLink {
String getSiteName();
String getTitle();
String getSummary();
String getIcon();
String getLink(String urlKey);
UrlInfo getUrlInfo(String urlKey);
Set<String> getUrlKeys();
/**
* @return 2-letters language code or special token "multi"
*/
String getLanguage();
Set<String> getLinkKeys();
NetworkOperationData createOperationData(INetworkLink link,
NetworkOperationData.OnNewItemListener listener);
/**
* @param listener Network operation listener
* @return instance, which represents the state of the network operation.
*/
NetworkOperationData createOperationData(NetworkOperationData.OnNewItemListener listener);
ZLNetworkRequest simpleSearchRequest(String pattern, NetworkOperationData data);
ZLNetworkRequest resume(NetworkOperationData data);
@ -54,5 +60,7 @@ public interface INetworkLink {
NetworkCatalogItem libraryItem();
NetworkAuthenticationManager authenticationManager();
Basket basket();
String rewriteUrl(String url, boolean isUrlExternal);
}

View file

@ -25,7 +25,7 @@ import java.io.File;
import org.geometerplus.fbreader.network.authentication.NetworkAuthenticationManager;
public final class NetworkBookItem extends NetworkLibraryItem {
public final class NetworkBookItem extends NetworkItem {
public static class AuthorData implements Comparable<AuthorData> {
public final String DisplayName;
@ -59,7 +59,7 @@ public final class NetworkBookItem extends NetworkLibraryItem {
return false;
}
final AuthorData data = (AuthorData) o;
return SortKey == data.SortKey && DisplayName == data.DisplayName;
return SortKey.equals(data.SortKey) && DisplayName.equals(data.DisplayName);
}
@Override
@ -80,7 +80,7 @@ public final class NetworkBookItem extends NetworkLibraryItem {
private final LinkedList<BookReference> myReferences;
/**
* Creates new NetworkLibraryItem instance.
* Creates new NetworkItem instance.
*
* @param link corresponding NetworkLink object. Must be not <code>null</code>.
* @param id string that uniquely identifies this book item. Must be not <code>null</code>.

View file

@ -22,10 +22,8 @@ package org.geometerplus.fbreader.network;
import java.util.Comparator;
import java.util.LinkedList;
public final class NetworkBookItemComparator implements Comparator<NetworkLibraryItem> {
public int compare(NetworkLibraryItem item0, NetworkLibraryItem item1) {
public final class NetworkBookItemComparator implements Comparator<NetworkItem> {
public int compare(NetworkItem item0, NetworkItem item1) {
final boolean item0isABook = item0 instanceof NetworkBookItem;
final boolean item1isABook = item1 instanceof NetworkBookItem;

View file

@ -23,10 +23,12 @@ import java.util.*;
import org.geometerplus.zlibrary.core.util.ZLBoolean3;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.zlibrary.core.network.ZLNetworkManager;
import org.geometerplus.zlibrary.core.network.ZLNetworkRequest;
import org.geometerplus.fbreader.network.authentication.NetworkAuthenticationManager;
public abstract class NetworkCatalogItem extends NetworkLibraryItem {
public abstract class NetworkCatalogItem extends NetworkItem {
// catalog types:
public static enum CatalogType {
@ -65,21 +67,6 @@ public abstract class NetworkCatalogItem extends NetworkLibraryItem {
this(link, title, summary, cover, urlByType, Accessibility.ALWAYS, CatalogType.OTHER);
}
/**
* Creates new NetworkCatalogItem instance with specified accessibility and <code>CatalogType.OTHER</code> type.
*
* @param link corresponding NetworkLink object. Must be not <code>null</code>.
* @param title title of this library item. Must be not <code>null</code>.
* @param summary description of this library item. Can be <code>null</code>.
* @param cover cover url. Can be <code>null</code>.
* @param urlByType map contains URLs and their types. Must be not <code>null</code>.
* @param accessibility value defines when this library item will be accessible
* in the network library view.
*/
public NetworkCatalogItem(INetworkLink link, String title, String summary, String cover, Map<Integer, String> urlByType, Accessibility accessibility) {
this(link, title, summary, cover, urlByType, accessibility, CatalogType.OTHER);
}
/**
* Creates new NetworkCatalogItem instance with specified accessibility and type.
*
@ -144,11 +131,44 @@ public abstract class NetworkCatalogItem extends NetworkLibraryItem {
return ZLBoolean3.B3_UNDEFINED;
}
case HAS_BOOKS:
if (mgr != null && mgr.purchasedBooks().size() > 0) {
if ((Link.basket() != null && Link.basket().bookIds().size() > 0) ||
(mgr != null && mgr.purchasedBooks().size() > 0)) {
return ZLBoolean3.B3_TRUE;
} else {
return ZLBoolean3.B3_FALSE;
}
}
}
/**
* Performs all necessary operations with NetworkOperationData and NetworkRequest
* to complete loading children items.
*
* @param data Network operation data instance
* @param networkRequest initial network request
*
* @throws ZLNetworkException when network operation couldn't be completed
*/
protected final void doLoadChildren(NetworkOperationData data,
ZLNetworkRequest networkRequest) throws ZLNetworkException {
while (networkRequest != null) {
ZLNetworkManager.Instance().perform(networkRequest);
if (data.Listener.confirmInterrupt()) {
return;
}
networkRequest = data.resume();
}
}
/**
* Override this method if result of the request depends not only from URL
* (e.g. result of the POST request depends from the URL and the body of the request).
*
* @return unique String for corresponding network request, for which:
* {@code item1.getFullRequestString().equals(item2.getFullRequestString())}
* iff network requests for items {@code item1} and {@code item2} are the same.
*/
public String getFullRequestString() {
return URLByType.get(URL_CATALOG);
}
}

View file

@ -35,7 +35,7 @@ public abstract class NetworkDatabase {
protected abstract void executeAsATransaction(Runnable actions);
public interface ICustomLinksHandler {
void handleCustomLinkData(int id, String siteName, String title, String summary, String icon, Map<String, String> links);
void handleCustomLinkData(int id, String siteName, String title, String summary, Map<String,UrlInfo> infos);
}
protected abstract void loadCustomLinks(ICustomLinksHandler handler);

View file

@ -19,8 +19,7 @@
package org.geometerplus.fbreader.network;
public abstract class NetworkLibraryItem {
public abstract class NetworkItem {
public final INetworkLink Link;
public final String Title;
public final String Summary;
@ -29,14 +28,14 @@ public abstract class NetworkLibraryItem {
//public org.geometerplus.fbreader.network.atom.ATOMEntry dbgEntry;
/**
* Creates new NetworkLibraryItem instance.
* Creates new NetworkItem instance.
*
* @param link corresponding NetworkLink object. Must be not <code>null</code>.
* @param title title of this library item. Must be not <code>null</code>.
* @param summary description of this library item. Can be <code>null</code>.
* @param cover cover url. Can be <code>null</code>.
*/
protected NetworkLibraryItem(INetworkLink link, String title, String summary, String cover) {
protected NetworkItem(INetworkLink link, String title, String summary, String cover) {
Link = link;
Title = title;
Summary = summary;

View file

@ -31,9 +31,9 @@ import org.geometerplus.zlibrary.core.language.ZLLanguageUtil;
import org.geometerplus.fbreader.tree.FBTree;
import org.geometerplus.fbreader.network.tree.*;
import org.geometerplus.fbreader.network.opds.OPDSCustomLink;
import org.geometerplus.fbreader.network.opds.OPDSLinkReader;
public class NetworkLibrary {
private static NetworkLibrary ourInstance;
@ -119,6 +119,7 @@ public class NetworkLibrary {
allCodes.removeAll(languageCodes());
allCodes.addAll(codes);
activeLanguageCodesOption().setValue(commaSeparatedString(allCodes));
invalidateChildren();
}
private String commaSeparatedString(Collection<String> codes) {
@ -148,9 +149,9 @@ public class NetworkLibrary {
}
private final RootTree myRootTree = new RootTree();
private SearchItemTree mySearchItemTree;
private boolean myUpdateChildren = true;
private boolean myInvalidateChildren;
private boolean myChildrenAreInvalid = true;
private boolean myUpdateVisibility;
private NetworkLibrary() {
@ -178,9 +179,13 @@ public class NetworkLibrary {
db.loadCustomLinks(
new NetworkDatabase.ICustomLinksHandler() {
public void handleCustomLinkData(int id, String siteName,
String title, String summary, String icon, Map<String, String> links) {
final ICustomNetworkLink link = OPDSLinkReader.createCustomLink(id, siteName, title, summary, icon, links);
if (link != null) {
String title, String summary, Map<String,UrlInfo> infos) {
if (title != null &&
siteName != null &&
infos.get(INetworkLink.URL_MAIN) != null) {
final ICustomNetworkLink link = new OPDSCustomLink(
id, siteName, title, summary, infos
);
addLinkInternal(link);
}
}
@ -188,24 +193,6 @@ public class NetworkLibrary {
);
}
/*testDate(new ATOMUpdated(2010, 1, 1, 1, 0, 0, 0, 2, 0),
new ATOMUpdated(2009, 12, 31, 23, 0, 0, 0, 0, 0));
testDate(new ATOMUpdated(2010, 12, 31, 23, 40, 0, 0, -1, -30),
new ATOMUpdated(2011, 1, 1, 1, 10, 0, 0, 0, 0));
testDate(new ATOMUpdated(2010, 1, 31, 23, 40, 0, 0, -1, -30),
new ATOMUpdated(2010, 2, 1, 1, 10, 0, 0, 0, 0));
testDate(new ATOMUpdated(2010, 2, 28, 23, 40, 0, 0, -1, -30),
new ATOMUpdated(2010, 3, 1, 1, 10, 0, 0, 0, 0));
testDate(new ATOMUpdated(2012, 2, 28, 23, 40, 0, 0, -1, -30),
new ATOMUpdated(2012, 2, 29, 1, 10, 0, 0, 0, 0));
testDate(new ATOMUpdated(2012, 2, 15, 23, 40, 0, 0, -1, -30),
new ATOMUpdated(2012, 2, 16, 1, 10, 0, 0, 0, 0));
testDate(new ATOMUpdated(2012, 2, 15, 23, 40, 1, 0, 3, 30),
new ATOMUpdated(2012, 2, 15, 23, 40, 0, 0, 3, 30));
testDate(new ATOMUpdated(2012, 2, 15, 23, 40, 0, 0, 3, 30),
new ATOMUpdated(2012, 2, 15, 23, 40, 1, 0, 3, 30));
testDate(new ATOMUpdated(2012, 2, 15, 23, 40, 0, 0.001f, 3, 30),
new ATOMUpdated(2012, 2, 15, 23, 40, 0, 0, 3, 30));*/
myIsAlreadyInitialized = true;
}
@ -257,6 +244,15 @@ public class NetworkLibrary {
}
}
}
for (INetworkLink link : myLinks) {
if (link instanceof ICustomNetworkLink) {
final ICustomNetworkLink customLink = (ICustomNetworkLink)link;
if (customLink.isObsolete(12 * 60 * 60 * 1000)) { // 12 hours
customLink.reloadInfo(true);
NetworkDatabase.Instance().saveCustomLink(customLink);
}
}
}
}
}
@ -266,13 +262,12 @@ public class NetworkLibrary {
// synchronize() method MUST be called after this method
public void finishBackgroundUpdate() {
synchronized (myBackgroundLock) {
if (myBackgroundLinks == null) {
return;
}
synchronized (myLinks) {
removeAllLoadedLinks();
myLinks.addAll(myBackgroundLinks);
updateChildren();
if (myBackgroundLinks != null) {
removeAllLoadedLinks();
myLinks.addAll(myBackgroundLinks);
}
invalidateChildren();
}
}
}
@ -290,31 +285,23 @@ public class NetworkLibrary {
return url;
}
public void invalidateChildren() {
myInvalidateChildren = true;
}
public void updateChildren() {
myUpdateChildren = true;
private void invalidateChildren() {
myChildrenAreInvalid = true;
}
public void invalidateVisibility() {
myUpdateVisibility = true;
}
private static boolean linkIsInvalid(INetworkLink link, INetworkLink nodeLink) {
if (link instanceof ICustomNetworkLink) {
if (link != nodeLink) {
throw new RuntimeException("Two equal custom links!!! That's impossible");
}
return ((ICustomNetworkLink) link).hasChanges();
}
return !link.equals(nodeLink);
private static boolean linkIsChanged(INetworkLink link) {
return
link instanceof ICustomNetworkLink &&
((ICustomNetworkLink)link).hasChanges();
}
private static void makeValid(INetworkLink link) {
if (link instanceof ICustomNetworkLink) {
((ICustomNetworkLink) link).resetChanges();
((ICustomNetworkLink)link).resetChanges();
}
}
@ -336,13 +323,14 @@ public class NetworkLibrary {
currentNode = nodeIterator.next();
}
if (!(currentNode instanceof NetworkCatalogTree)) {
toRemove.add(currentNode);
currentNode = null;
++nodeCount;
continue;
}
final INetworkLink nodeLink = ((NetworkCatalogTree) currentNode).Item.Link;
final INetworkLink nodeLink = ((NetworkCatalogTree)currentNode).Item.Link;
if (link == nodeLink) {
if (linkIsInvalid(link, nodeLink)) {
if (linkIsChanged(link)) {
toRemove.add(currentNode);
} else {
processed = true;
@ -359,7 +347,7 @@ public class NetworkLibrary {
break;
}
}
if (newNodeLink == null || linkIsInvalid(newNodeLink, nodeLink)) {
if (newNodeLink == null || linkIsChanged(nodeLink)) {
toRemove.add(currentNode);
currentNode = null;
++nodeCount;
@ -381,30 +369,28 @@ public class NetworkLibrary {
if (currentNode == null) {
currentNode = nodeIterator.next();
}
if (currentNode instanceof NetworkCatalogTree) {
toRemove.add(currentNode);
}
toRemove.add(currentNode);
currentNode = null;
}
for (FBTree tree : toRemove) {
tree.removeSelf();
}
new AddCustomCatalogItemTree(myRootTree);
mySearchItemTree = new SearchItemTree(myRootTree, 0);
}
private void updateVisibility() {
for (FBTree tree : myRootTree.subTrees()) {
if (!(tree instanceof NetworkCatalogTree)) {
continue;
if (tree instanceof NetworkCatalogTree) {
((NetworkCatalogTree)tree).updateVisibility();
}
((NetworkCatalogTree) tree).updateVisibility();
}
}
public void synchronize() {
if (myUpdateChildren || myInvalidateChildren) {
myUpdateChildren = false;
myInvalidateChildren = false;
if (myChildrenAreInvalid) {
myChildrenAreInvalid = false;
makeUpToDate();
}
if (myUpdateVisibility) {
@ -413,17 +399,37 @@ public class NetworkLibrary {
}
}
public NetworkTree getTree() {
public NetworkTree getRootTree() {
return myRootTree;
}
public SearchItemTree getSearchItemTree() {
return mySearchItemTree;
}
public NetworkTree getTreeByKey(NetworkTree.Key key) {
if (key.Parent == null) {
return key.equals(myRootTree.getUniqueKey()) ? myRootTree : null;
}
final NetworkTree parentTree = getTreeByKey(key.Parent);
if (parentTree == null) {
return null;
}
for (FBTree tree : parentTree.subTrees()) {
final NetworkTree nTree = (NetworkTree)tree;
if (key.equals(nTree.getUniqueKey())) {
return nTree;
}
}
return null;
}
public void simpleSearch(String pattern, final NetworkOperationData.OnNewItemListener listener) throws ZLNetworkException {
LinkedList<ZLNetworkRequest> requestList = new LinkedList<ZLNetworkRequest>();
LinkedList<NetworkOperationData> dataList = new LinkedList<NetworkOperationData>();
final NetworkOperationData.OnNewItemListener synchronizedListener = new NetworkOperationData.OnNewItemListener() {
public synchronized void onNewItem(INetworkLink link, NetworkLibraryItem item) {
public synchronized void onNewItem(INetworkLink link, NetworkItem item) {
listener.onNewItem(link, item);
}
public synchronized boolean confirmInterrupt() {
@ -436,7 +442,7 @@ public class NetworkLibrary {
synchronized (myLinks) {
for (INetworkLink link : activeLinks()) {
final NetworkOperationData data = link.createOperationData(link, synchronizedListener);
final NetworkOperationData data = link.createOperationData(synchronizedListener);
final ZLNetworkRequest request = link.simpleSearchRequest(pattern, data);
if (request != null) {
dataList.add(data);
@ -483,6 +489,7 @@ public class NetworkLibrary {
}
}
NetworkDatabase.Instance().saveCustomLink(link);
invalidateChildren();
}
public void removeCustomLink(ICustomNetworkLink link) {
@ -490,27 +497,6 @@ public class NetworkLibrary {
myLinks.remove(link);
}
NetworkDatabase.Instance().deleteCustomLink(link);
}
public boolean hasCustomLinkTitle(String title, INetworkLink exceptFor) {
synchronized (myLinks) {
for (INetworkLink link : myLinks) {
if (link != exceptFor && link.getTitle().equals(title)) {
return true;
}
}
}
return false;
}
public boolean hasCustomLinkSite(String siteName, INetworkLink exceptFor) {
synchronized (myLinks) {
for (INetworkLink link : myLinks) {
if (link != exceptFor && link.getSiteName().equals(siteName)) {
return true;
}
}
}
return false;
invalidateChildren();
}
}

View file

@ -25,12 +25,16 @@ import org.geometerplus.zlibrary.core.network.ZLNetworkRequest;
public class NetworkOperationData {
public interface OnNewItemListener {
void onNewItem(INetworkLink link, NetworkLibraryItem item);
void onNewItem(INetworkLink link, NetworkItem item);
void commitItems(INetworkLink link);
// returns true to confirm interrupt reading; return false to continue reading.
// once true has been returned, all next calls must return true.
/**
* @return <code>true</code> to confirm interrupt reading;
* <code>false</code> to continue reading.
* Once <code>true</code> has been returned,
* all next calls MUST return <code>true</code>.
*/
boolean confirmInterrupt();
}
@ -43,11 +47,11 @@ public class NetworkOperationData {
Listener = listener;
}
public void clear() {
protected void clear() {
ResumeURI = null;
}
public ZLNetworkRequest resume() {
public final ZLNetworkRequest resume() {
final ZLNetworkRequest request = Link.resume(this);
clear();
return request;

View file

@ -21,15 +21,48 @@ package org.geometerplus.fbreader.network;
import java.util.LinkedList;
import java.util.Set;
import java.io.Serializable;
import org.geometerplus.zlibrary.core.constants.MimeTypes;
import org.geometerplus.zlibrary.core.image.ZLImage;
import org.geometerplus.zlibrary.core.util.ZLMiscUtil;
import org.geometerplus.fbreader.tree.FBTree;
public abstract class NetworkTree extends FBTree {
protected NetworkTree(int level) {
super(level);
public static class Key implements Serializable {
final Key Parent;
final String Id;
private Key(Key parent, String id) {
if (id == null) {
throw new IllegalArgumentException("NetworkTree string id must be non-null");
}
Parent = parent;
Id = id;
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof NetworkTree.Key)) {
return false;
}
final NetworkTree.Key key = (NetworkTree.Key)other;
return Id.equals(key.Id) && ZLMiscUtil.equals(Parent, key.Parent);
}
@Override
public int hashCode() {
return Id.hashCode();
}
@Override
public String toString() {
return Parent == null ? Id : Parent.toString() + " :: " + Id;
}
}
protected NetworkTree() {
@ -44,7 +77,7 @@ public abstract class NetworkTree extends FBTree {
super(parent, position);
}
public static ZLImage createCover(NetworkLibraryItem item) {
public static ZLImage createCover(NetworkItem item) {
if (item.Cover == null) {
return null;
}
@ -88,15 +121,38 @@ public abstract class NetworkTree extends FBTree {
}
public abstract NetworkLibraryItem getHoldedItem();
public abstract NetworkItem getHoldedItem();
public void removeItems(Set<NetworkLibraryItem> items) {
private Key myKey;
/**
* Returns unique identifier which can be used in NetworkView methods
* @return unique Key instance
*/
public final Key getUniqueKey() {
if (myKey == null) {
//final ZLTree parentTree = getParent();
final Key parentKey = Parent instanceof NetworkTree ?
((NetworkTree)Parent).getUniqueKey() : null;
myKey = new Key(parentKey, getStringId());
}
return myKey;
}
/**
* Returns id used as a part of unique key above. This string must be
* not null
* and
* be unique for all children of same tree
*/
protected abstract String getStringId();
public void removeItems(Set<NetworkItem> items) {
if (items.isEmpty() || subTrees().isEmpty()) {
return;
}
final LinkedList<FBTree> treesList = new LinkedList<FBTree>();
for (FBTree tree: subTrees()) {
final NetworkLibraryItem treeItem = ((NetworkTree)tree).getHoldedItem();
final NetworkItem treeItem = ((NetworkTree)tree).getHoldedItem();
if (treeItem != null && items.contains(treeItem)) {
treesList.add(tree);
items.remove(treeItem);

View file

@ -23,13 +23,12 @@ import java.util.LinkedHashMap;
import java.util.LinkedList;
public class SearchResult {
public final String Summary;
public final LinkedHashMap<NetworkBookItem.AuthorData, LinkedList<NetworkBookItem>> BooksMap;
public final LinkedHashMap<NetworkBookItem.AuthorData, LinkedList<NetworkBookItem>> BooksMap =
new LinkedHashMap<NetworkBookItem.AuthorData, LinkedList<NetworkBookItem>>();
public SearchResult(String summary) {
Summary = summary;
BooksMap = new LinkedHashMap<NetworkBookItem.AuthorData, LinkedList<NetworkBookItem>>();
}
public void addBook(NetworkBookItem book) {
@ -43,7 +42,7 @@ public class SearchResult {
}
}
public boolean empty() {
public boolean isEmpty() {
return BooksMap.size() == 0;
}
}

View file

@ -0,0 +1,33 @@
/*
* 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.fbreader.network;
import org.geometerplus.zlibrary.core.resources.ZLResource;
public class TopUpItem extends NetworkItem {
public TopUpItem(INetworkLink link, String cover) {
super(
link,
ZLResource.resource("networkView").getResource("refillTitle").getValue(),
ZLResource.resource("networkView").getResource("refillSummary").getValue(),
cover
);
}
}

View file

@ -0,0 +1,59 @@
/*
* 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.fbreader.network;
import java.util.Date;
import java.io.Serializable;
import org.geometerplus.zlibrary.core.util.ZLMiscUtil;
public final class UrlInfo implements Serializable {
public static final UrlInfo NULL = new UrlInfo(null, null);
public final String URL;
public final Date Updated;
public UrlInfo(String url, Date updated) {
URL = url;
Updated = updated;
}
public UrlInfo(String url) {
this(url, new Date());
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof UrlInfo)) {
return false;
}
final UrlInfo info = (UrlInfo)o;
return ZLMiscUtil.equals(URL, info.URL) && ZLMiscUtil.equals(Updated, info.Updated);
}
@Override
public int hashCode() {
return ZLMiscUtil.hashCode(URL) + ZLMiscUtil.hashCode(Updated);
}
}

View file

@ -39,7 +39,7 @@ public class LitResAuthenticationManager extends NetworkAuthenticationManager {
private String myInitializedDataSid;
private String myAccount;
private final HashMap<String, NetworkLibraryItem> myPurchasedBooks = new HashMap<String, NetworkLibraryItem>();
private final HashMap<String, NetworkItem> myPurchasedBooks = new HashMap<String, NetworkItem>();
public LitResAuthenticationManager(INetworkLink link, String sslCertificate) {
super(link, sslCertificate);
@ -89,7 +89,7 @@ public class LitResAuthenticationManager extends NetworkAuthenticationManager {
sid = mySidOption.getValue();
}
String url = Link.getLink(INetworkLink.URL_SIGN_IN);
String url = Link.getUrlInfo(INetworkLink.URL_SIGN_IN).URL;
if (url == null) {
throw new ZLNetworkException(NetworkException.ERROR_UNSUPPORTED_OPERATION);
}
@ -119,7 +119,7 @@ public class LitResAuthenticationManager extends NetworkAuthenticationManager {
@Override
public void authorise(String password) throws ZLNetworkException {
String url = Link.getLink(INetworkLink.URL_SIGN_IN);
String url = Link.getUrlInfo(INetworkLink.URL_SIGN_IN).URL;
if (url == null) {
throw new ZLNetworkException(NetworkException.ERROR_UNSUPPORTED_OPERATION);
}
@ -241,7 +241,7 @@ public class LitResAuthenticationManager extends NetworkAuthenticationManager {
if (sid.length() == 0) {
return null;
}
final String url = Link.getLink(INetworkLink.URL_REFILL_ACCOUNT);
final String url = Link.getUrlInfo(INetworkLink.URL_REFILL_ACCOUNT).URL;
if (url == null) {
return null;
}
@ -352,7 +352,7 @@ public class LitResAuthenticationManager extends NetworkAuthenticationManager {
return new LitResNetworkRequest(
LitResUtil.url(Link, query),
SSLCertificate,
new LitResXMLReader(Link, new LinkedList<NetworkLibraryItem>())
new LitResXMLReader(Link, new LinkedList<NetworkItem>())
);
}
@ -363,7 +363,7 @@ public class LitResAuthenticationManager extends NetworkAuthenticationManager {
private void loadPurchasedBooksOnSuccess(LitResNetworkRequest purchasedBooksRequest) {
LitResXMLReader reader = (LitResXMLReader)purchasedBooksRequest.Reader;
myPurchasedBooks.clear();
for (NetworkLibraryItem item: reader.Books) {
for (NetworkItem item: reader.Books) {
if (item instanceof NetworkBookItem) {
NetworkBookItem book = (NetworkBookItem)item;
myPurchasedBooks.put(book.Id, book);
@ -401,7 +401,7 @@ public class LitResAuthenticationManager extends NetworkAuthenticationManager {
@Override
public void recoverPassword(String email) throws ZLNetworkException {
String url = Link.getLink(INetworkLink.URL_RECOVER_PASSWORD);
String url = Link.getUrlInfo(INetworkLink.URL_RECOVER_PASSWORD).URL;
if (url == null) {
throw new ZLNetworkException(NetworkException.ERROR_UNSUPPORTED_OPERATION);
}

View file

@ -21,15 +21,127 @@ package org.geometerplus.fbreader.network.authentication.litres;
import java.util.*;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.fbreader.network.*;
abstract class SortedCatalogItem extends NetworkCatalogItem {
private final List<NetworkItem> myChildren = new LinkedList<NetworkItem>();
private SortedCatalogItem(NetworkCatalogItem parent, ZLResource resource, List<NetworkItem> children) {
super(parent.Link, resource.getValue(), resource.getResource("summary").getValue(), "", parent.URLByType);
for (NetworkItem child : children) {
if (accepts(child)) {
myChildren.add(child);
}
}
final Comparator<NetworkItem> comparator = getComparator();
if (comparator != null) {
Collections.sort(myChildren, comparator);
}
}
public boolean isEmpty() {
return myChildren.isEmpty();
}
protected abstract Comparator<NetworkItem> getComparator();
protected boolean accepts(NetworkItem item) {
return item instanceof NetworkBookItem;
}
public SortedCatalogItem(NetworkCatalogItem parent, String resourceKey, List<NetworkItem> children) {
this(parent, ZLResource.resource("networkView").getResource(resourceKey), children);
}
@Override
public void onDisplayItem() {
}
@Override
public void loadChildren(NetworkOperationData.OnNewItemListener listener) throws ZLNetworkException {
for (NetworkItem child : myChildren) {
listener.onNewItem(Link, child);
}
listener.commitItems(Link);
}
}
class ByAuthorCatalogItem extends SortedCatalogItem {
ByAuthorCatalogItem(NetworkCatalogItem parent, List<NetworkItem> children) {
super(parent, "byAuthor", children);
}
@Override
protected Comparator<NetworkItem> getComparator() {
return new NetworkBookItemComparator();
}
}
class ByTitleCatalogItem extends SortedCatalogItem {
ByTitleCatalogItem(NetworkCatalogItem parent, List<NetworkItem> children) {
super(parent, "byTitle", children);
}
@Override
protected Comparator<NetworkItem> getComparator() {
return new Comparator<NetworkItem>() {
public int compare(NetworkItem item0, NetworkItem item1) {
return item0.Title.compareTo(item1.Title);
}
};
}
}
class ByDateCatalogItem extends SortedCatalogItem {
ByDateCatalogItem(NetworkCatalogItem parent, List<NetworkItem> children) {
super(parent, "byDate", children);
}
@Override
protected Comparator<NetworkItem> getComparator() {
return new Comparator<NetworkItem>() {
public int compare(NetworkItem item0, NetworkItem item1) {
return 0;
}
};
}
}
class BySeriesCatalogItem extends SortedCatalogItem {
BySeriesCatalogItem(NetworkCatalogItem parent, List<NetworkItem> children) {
super(parent, "bySeries", children);
}
@Override
protected Comparator<NetworkItem> getComparator() {
return new Comparator<NetworkItem>() {
public int compare(NetworkItem item0, NetworkItem item1) {
final NetworkBookItem book0 = (NetworkBookItem)item0;
final NetworkBookItem book1 = (NetworkBookItem)item1;
int diff = book0.SeriesTitle.compareTo(book1.SeriesTitle);
if (diff == 0) {
diff = book0.IndexInSeries - book1.IndexInSeries;
}
return diff != 0 ? diff : book0.Title.compareTo(book1.Title);
}
};
}
@Override
protected boolean accepts(NetworkItem item) {
return
item instanceof NetworkBookItem &&
((NetworkBookItem)item).SeriesTitle != null;
}
}
public class LitResBookshelfItem extends NetworkCatalogItem {
private boolean myForceReload;
public LitResBookshelfItem(INetworkLink link, String title, String summary, String cover, Map<Integer, String> urlByType, Accessibility accessibility) {
super(link, title, summary, cover, urlByType, accessibility);
super(link, title, summary, cover, urlByType, accessibility, CatalogType.OTHER);
}
@Override
@ -54,11 +166,21 @@ public class LitResBookshelfItem extends NetworkCatalogItem {
} finally {
myForceReload = true;
// TODO: implement asynchronous loading
ArrayList<NetworkBookItem> children =
new ArrayList<NetworkBookItem>(mgr.purchasedBooks());
Collections.sort(children, new NetworkBookItemComparator());
for (NetworkLibraryItem item : children) {
listener.onNewItem(Link, item);
ArrayList<NetworkItem> children =
new ArrayList<NetworkItem>(mgr.purchasedBooks());
if (children.size() <= 5) {
Collections.sort(children, new NetworkBookItemComparator());
for (NetworkItem item : children) {
listener.onNewItem(Link, item);
}
} else {
listener.onNewItem(Link, new ByDateCatalogItem(this, children));
listener.onNewItem(Link, new ByAuthorCatalogItem(this, children));
listener.onNewItem(Link, new ByTitleCatalogItem(this, children));
final BySeriesCatalogItem bySeries = new BySeriesCatalogItem(this, children);
if (!bySeries.isEmpty()) {
listener.onNewItem(Link, bySeries);
}
}
listener.commitItems(Link);
}

View file

@ -24,8 +24,10 @@ import java.util.Map;
import org.geometerplus.zlibrary.core.util.ZLNetworkUtil;
import org.geometerplus.fbreader.network.INetworkLink;
import org.geometerplus.fbreader.network.Basket;
import org.geometerplus.fbreader.network.NetworkBookItem;
import org.geometerplus.fbreader.network.opds.OPDSCatalogItem;
import org.geometerplus.fbreader.network.opds.OPDSNetworkLink;
public class LitResRecommendationsItem extends OPDSCatalogItem {
public LitResRecommendationsItem(INetworkLink link, String title, String summary, String cover, Map<Integer,String> urlByType, Accessibility accessibility) {
@ -46,6 +48,17 @@ public class LitResRecommendationsItem extends OPDSCatalogItem {
}
builder.append(book.Id);
}
final Basket basket = Link.basket();
if (basket != null) {
for (String bookId : basket.bookIds()) {
if (flag) {
builder.append(',');
} else {
flag = true;
}
builder.append(bookId);
}
}
return ZLNetworkUtil.appendParameter(URLByType.get(URL_CATALOG), "ids", builder.toString());
}

View file

@ -31,7 +31,7 @@ import org.geometerplus.fbreader.network.opds.HtmlToString;
class LitResXMLReader extends LitResAuthenticationXMLReader {
public final INetworkLink Link;
public final List<NetworkLibraryItem> Books;
public final List<NetworkItem> Books;
private int myIndex;
@ -55,7 +55,7 @@ class LitResXMLReader extends LitResAuthenticationXMLReader {
private HashMap<Integer, String> myURLByType = new HashMap<Integer, String>(); // TODO: remove
private LinkedList<BookReference> myReferences = new LinkedList<BookReference>();
public LitResXMLReader(INetworkLink link, List<NetworkLibraryItem> books) {
public LitResXMLReader(INetworkLink link, List<NetworkItem> books) {
super(link.getSiteName());
Link = link;
Books = books;

View file

@ -0,0 +1,52 @@
/*
* 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.fbreader.network.opds;
import java.util.Map;
import org.geometerplus.zlibrary.core.util.ZLNetworkUtil;
import org.geometerplus.fbreader.network.INetworkLink;
import org.geometerplus.fbreader.network.NetworkBookItem;
import org.geometerplus.fbreader.network.opds.OPDSNetworkLink;
import org.geometerplus.fbreader.network.opds.OPDSCatalogItem;
public class BasketItem extends OPDSCatalogItem {
BasketItem(OPDSNetworkLink link, String title, String summary, String cover, Map<Integer,String> urlByType, Accessibility accessibility) {
super(link, title, summary, cover, urlByType, accessibility, CatalogType.BY_SERIES);
link.setSupportsBasket();
}
@Override
protected String getUrl() {
final StringBuilder builder = new StringBuilder();
boolean flag = false;
for (String bookId : Link.basket().bookIds()) {
if (flag) {
builder.append(',');
} else {
flag = true;
}
builder.append(bookId);
}
return ZLNetworkUtil.appendParameter(URLByType.get(URL_CATALOG), "ids", builder.toString());
}
}

View file

@ -42,7 +42,7 @@ class NetworkOPDSFeedReader implements OPDSFeedReader, OPDSConstants, MimeTypes
private int myItemsToLoad = -1;
/**
* Creates new OPDSFeedReader instance that can be used to get NetworkLibraryItem objects from OPDS feeds.
* Creates new OPDSFeedReader instance that can be used to get NetworkItem objects from OPDS feeds.
*
* @param baseURL string that contains URL of the OPDS feed, that will be read using this instance of the reader
* @param result network results buffer. Must be created using OPDSNetworkLink corresponding to the OPDS feed,
@ -203,7 +203,7 @@ class NetworkOPDSFeedReader implements OPDSFeedReader, OPDSConstants, MimeTypes
}
}
NetworkLibraryItem item;
NetworkItem item;
if (hasBookLink) {
item = readBookItem(entry);
} else {
@ -218,7 +218,7 @@ class NetworkOPDSFeedReader implements OPDSFeedReader, OPDSConstants, MimeTypes
private static final String AuthorPrefix = "author:";
private static final String AuthorsPrefix = "authors:";
private NetworkLibraryItem readBookItem(OPDSEntry entry) {
private NetworkItem readBookItem(OPDSEntry entry) {
final OPDSNetworkLink opdsNetworkLink = (OPDSNetworkLink)myData.Link;
/*final String date;
if (entry.DCIssued != null) {
@ -368,7 +368,7 @@ class NetworkOPDSFeedReader implements OPDSFeedReader, OPDSConstants, MimeTypes
}
}
private NetworkLibraryItem readCatalogItem(OPDSEntry entry) {
private NetworkItem readCatalogItem(OPDSEntry entry) {
final OPDSNetworkLink opdsLink = (OPDSNetworkLink)myData.Link;
String coverURL = null;
String url = null;
@ -457,6 +457,20 @@ class NetworkOPDSFeedReader implements OPDSFeedReader, OPDSConstants, MimeTypes
urlMap,
opdsLink.getCondition(entry.Id.Uri)
);
} else if (REL_BASKET.equals(litresRel)) {
return null;
/*
return new BasketItem(
opdsLink,
entry.Title,
annotation,
coverURL,
urlMap,
opdsLink.getCondition(entry.Id.Uri)
);
*/
} else if (REL_TOPUP.equals(litresRel)) {
return new TopUpItem(opdsLink, coverURL);
} else {
return null;
}

View file

@ -21,7 +21,6 @@ package org.geometerplus.fbreader.network.opds;
import java.util.*;
import org.geometerplus.zlibrary.core.network.ZLNetworkManager;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.zlibrary.core.network.ZLNetworkRequest;
@ -49,19 +48,12 @@ public class OPDSCatalogItem extends NetworkCatalogItem {
myExtraData = null;
}
private void doLoadChildren(NetworkOperationData.OnNewItemListener listener,
ZLNetworkRequest networkRequest) throws ZLNetworkException {
while (networkRequest != null) {
try {
ZLNetworkManager.Instance().perform(networkRequest);
} catch (ZLNetworkException e) {
myLoadingState = null;
throw e;
}
if (listener.confirmInterrupt()) {
return;
}
networkRequest = myLoadingState.resume();
private void doLoadChildren(ZLNetworkRequest networkRequest) throws ZLNetworkException {
try {
super.doLoadChildren(myLoadingState, networkRequest);
} catch (ZLNetworkException e) {
myLoadingState = null;
throw e;
}
}
@ -78,12 +70,12 @@ public class OPDSCatalogItem extends NetworkCatalogItem {
public final void loadChildren(NetworkOperationData.OnNewItemListener listener) throws ZLNetworkException {
OPDSNetworkLink opdsLink = (OPDSNetworkLink) Link;
myLoadingState = opdsLink.createOperationData(Link, listener);
myLoadingState = opdsLink.createOperationData(listener);
ZLNetworkRequest networkRequest =
opdsLink.createNetworkData(getUrl(), myLoadingState);
doLoadChildren(listener, networkRequest);
doLoadChildren(networkRequest);
}
@Override
@ -96,7 +88,7 @@ public class OPDSCatalogItem extends NetworkCatalogItem {
if (myLoadingState != null) {
myLoadingState.Listener = listener;
ZLNetworkRequest networkRequest = myLoadingState.resume();
doLoadChildren(listener, networkRequest);
doLoadChildren(networkRequest);
}
}
}

View file

@ -24,6 +24,8 @@ interface OPDSConstants {
// Feed level
String REL_BOOKSHELF = "http://data.fbreader.org/rel/bookshelf";
String REL_RECOMMENDATIONS = "http://data.fbreader.org/rel/recommendations";
String REL_BASKET = "http://data.fbreader.org/rel/basket";
String REL_TOPUP = "http://data.fbreader.org/rel/topup";
//String REL_SUBSCRIPTIONS = "http://opds-spec.org/subscriptions";
// Entry level / catalog types

View file

@ -32,14 +32,22 @@ import org.geometerplus.zlibrary.core.util.ZLMiscUtil;
import org.geometerplus.fbreader.network.ICustomNetworkLink;
import org.geometerplus.fbreader.network.INetworkLink;
import org.geometerplus.fbreader.network.NetworkException;
import org.geometerplus.fbreader.network.UrlInfo;
class OPDSCustomLink extends OPDSNetworkLink implements ICustomNetworkLink {
public class OPDSCustomLink extends OPDSNetworkLink implements ICustomNetworkLink {
private int myId;
private boolean myHasChanges;
OPDSCustomLink(int id, String siteName, String title, String summary, String icon, Map<String, String> links) {
super(siteName, title, summary, icon, null, links, false);
private static String removeWWWPrefix(String siteName) {
if (siteName != null && siteName.startsWith("www.")) {
return siteName.substring(4);
}
return siteName;
}
public OPDSCustomLink(int id, String siteName, String title, String summary, Map<String,UrlInfo> infos) {
super(removeWWWPrefix(siteName), title, summary, null, infos, false);
myId = id;
}
@ -59,12 +67,6 @@ class OPDSCustomLink extends OPDSNetworkLink implements ICustomNetworkLink {
myHasChanges = false;
}
public final void setIcon(String icon) {
myHasChanges = myHasChanges || !ZLMiscUtil.equals(myIcon, icon);
myIcon = icon;
}
public final void setSiteName(String name) {
myHasChanges = myHasChanges || !ZLMiscUtil.equals(mySiteName, name);
mySiteName = name;
@ -80,28 +82,39 @@ class OPDSCustomLink extends OPDSNetworkLink implements ICustomNetworkLink {
myTitle = title;
}
public final void setLink(String urlKey, String url) {
if (url == null) {
removeLink(urlKey);
} else {
final String oldUrl = myLinks.put(urlKey, url);
myHasChanges = myHasChanges || !url.equals(oldUrl);
}
public final void setUrl(String urlKey, String url) {
myInfos.put(urlKey, new UrlInfo(url, new Date()));
myHasChanges = true;
}
public final void removeLink(String urlKey) {
final String oldUrl = myLinks.remove(urlKey);
public final void removeUrl(String urlKey) {
final UrlInfo oldUrl = myInfos.remove(urlKey);
myHasChanges = myHasChanges || oldUrl != null;
}
public boolean isObsolete(long milliSeconds) {
final long old = System.currentTimeMillis() - milliSeconds;
final Date searchUpdateDate = getUrlInfo(URL_SEARCH).Updated;
if (searchUpdateDate == null || searchUpdateDate.getTime() < old) {
return true;
}
public void reloadInfo() throws ZLNetworkException {
final Date iconUpdateDate = getUrlInfo(URL_ICON).Updated;
if (iconUpdateDate == null || iconUpdateDate.getTime() < old) {
return true;
}
return false;
}
public void reloadInfo(final boolean urlsOnly) throws ZLNetworkException {
final LinkedList<String> opensearchDescriptionURLs = new LinkedList<String>();
final List<OpenSearchDescription> descriptions = Collections.synchronizedList(new LinkedList<OpenSearchDescription>());
ZLNetworkException error = null;
try {
ZLNetworkManager.Instance().perform(new ZLNetworkRequest(getLink(INetworkLink.URL_MAIN)) {
ZLNetworkManager.Instance().perform(new ZLNetworkRequest(getUrlInfo(URL_MAIN).URL) {
@Override
public void handleStream(URLConnection connection, InputStream inputStream) throws IOException, ZLNetworkException {
final CatalogInfoReader info = new CatalogInfoReader(URL, OPDSCustomLink.this, opensearchDescriptionURLs);
@ -113,16 +126,14 @@ class OPDSCustomLink extends OPDSNetworkLink implements ICustomNetworkLink {
if (info.Title == null) {
throw new ZLNetworkException(NetworkException.ERROR_NO_REQUIRED_INFORMATION);
}
myTitle = info.Title;
if (info.Icon != null) {
myIcon = info.Icon;
}
if (info.Summary != null) {
mySummary = info.Summary;
}
setUrl(URL_ICON, info.Icon);
if (info.DirectOpenSearchDescription != null) {
descriptions.add(info.DirectOpenSearchDescription);
}
if (!urlsOnly) {
myTitle = info.Title;
mySummary = info.Summary;
}
}
});
} catch (ZLNetworkException e) {
@ -136,7 +147,7 @@ class OPDSCustomLink extends OPDSNetworkLink implements ICustomNetworkLink {
requests.add(new ZLNetworkRequest(url) {
@Override
public void handleStream(URLConnection connection, InputStream inputStream) throws IOException, ZLNetworkException {
new OpenSearchXMLReader(URL, descriptions, 20).read(inputStream);
new OpenSearchXMLReader(URL, descriptions).read(inputStream);
}
});
}
@ -151,7 +162,9 @@ class OPDSCustomLink extends OPDSNetworkLink implements ICustomNetworkLink {
if (!descriptions.isEmpty()) {
// TODO: May be do not use '%s'??? Use Description instead??? (this needs to rewrite SEARCH engine logic a little)
setLink(URL_SEARCH, descriptions.get(0).makeQuery("%s"));
setUrl(URL_SEARCH, descriptions.get(0).makeQuery("%s"));
} else {
setUrl(URL_SEARCH, null);
}
if (error != null) {
throw error;

View file

@ -39,23 +39,6 @@ import org.geometerplus.fbreader.network.atom.ATOMUpdated;
public class OPDSLinkReader {
static final String CATALOGS_URL = "http://data.fbreader.org/catalogs/generic-1.2.xml";
public static ICustomNetworkLink createCustomLink(int id, String siteName, String title, String summary, String icon, Map<String, String> links) {
if (siteName == null || title == null || links.get(INetworkLink.URL_MAIN) == null) {
return null;
}
return new OPDSCustomLink(id, siteName, title, summary, icon, links);
}
public static ICustomNetworkLink createCustomLink(int id, String siteName, String title, String summary, String icon, String url) {
final HashMap<String, String> links = new HashMap<String, String>();
links.put(INetworkLink.URL_MAIN, url);
return new OPDSCustomLink(id, siteName, title, summary, icon, links);
}
public static ICustomNetworkLink createCustomLink(String siteName, String title, String summary, String icon, String url) {
return createCustomLink(ICustomNetworkLink.INVALID_ID, siteName, title, summary, icon, url);
}
public static final int CACHE_LOAD = 0;
public static final int CACHE_UPDATE = 1;
public static final int CACHE_CLEAR = 2;

View file

@ -30,6 +30,7 @@ import org.geometerplus.zlibrary.core.xml.ZLStringMap;
import org.geometerplus.fbreader.network.INetworkLink;
import org.geometerplus.fbreader.network.NetworkLibrary;
import org.geometerplus.fbreader.network.NetworkCatalogItem;
import org.geometerplus.fbreader.network.UrlInfo;
import org.geometerplus.fbreader.network.atom.ATOMLink;
import org.geometerplus.fbreader.network.atom.ATOMUpdated;
import org.geometerplus.fbreader.network.authentication.NetworkAuthenticationManager;
@ -98,8 +99,7 @@ class OPDSLinkXMLReader extends OPDSXMLReader implements OPDSConstants, MimeType
final String summary = entry.Content;
final String language = entry.DCLanguage;
String icon = null;
final HashMap<String,String> links = new HashMap<String,String>();
final HashMap<String,UrlInfo> infos = new HashMap<String,UrlInfo>();
final HashMap<String,NetworkCatalogItem.Accessibility> urlConditions =
new HashMap<String,NetworkCatalogItem.Accessibility>();
for (ATOMLink link: entry.Links) {
@ -108,34 +108,35 @@ class OPDSLinkXMLReader extends OPDSXMLReader implements OPDSConstants, MimeType
final String rel = link.getRel();
if (rel == REL_IMAGE_THUMBNAIL || rel == REL_THUMBNAIL) {
if (type == MIME_IMAGE_PNG || type == MIME_IMAGE_JPEG) {
icon = href;
infos.put(INetworkLink.URL_ICON, new UrlInfo(href));
}
} else if ((rel != null && rel.startsWith(REL_IMAGE_PREFIX)) || rel == REL_COVER) {
if (icon == null && (type == MIME_IMAGE_PNG || type == MIME_IMAGE_JPEG)) {
icon = href;
if (infos.get(INetworkLink.URL_ICON) == null &&
(type == MIME_IMAGE_PNG || type == MIME_IMAGE_JPEG)) {
infos.put(INetworkLink.URL_ICON, new UrlInfo(href));
}
} else if (rel == null) {
if (type == MIME_APP_ATOM) {
links.put(INetworkLink.URL_MAIN, href);
infos.put(INetworkLink.URL_MAIN, new UrlInfo(href));
}
} else if (rel == "search") {
if (type == MIME_APP_ATOM) {
final OpenSearchDescription descr = OpenSearchDescription.createDefault(href);
if (descr.isValid()) {
// TODO: May be do not use '%s'??? Use Description instead??? (this needs to rewrite SEARCH engine logic a little)
links.put(INetworkLink.URL_SEARCH, descr.makeQuery("%s"));
infos.put(INetworkLink.URL_SEARCH, new UrlInfo(descr.makeQuery("%s")));
}
}
} else if (rel == REL_LINK_SIGN_IN) {
links.put(INetworkLink.URL_SIGN_IN, href);
infos.put(INetworkLink.URL_SIGN_IN, new UrlInfo(href));
} else if (rel == REL_LINK_SIGN_OUT) {
links.put(INetworkLink.URL_SIGN_OUT, href);
infos.put(INetworkLink.URL_SIGN_OUT, new UrlInfo(href));
} else if (rel == REL_LINK_SIGN_UP) {
links.put(INetworkLink.URL_SIGN_UP, href);
infos.put(INetworkLink.URL_SIGN_UP, new UrlInfo(href));
} else if (rel == REL_LINK_REFILL_ACCOUNT) {
links.put(INetworkLink.URL_REFILL_ACCOUNT, href);
infos.put(INetworkLink.URL_REFILL_ACCOUNT, new UrlInfo(href));
} else if (rel == REL_LINK_RECOVER_PASSWORD) {
links.put(INetworkLink.URL_RECOVER_PASSWORD, href);
infos.put(INetworkLink.URL_RECOVER_PASSWORD, new UrlInfo(href));
} else if (rel == REL_CONDITION_NEVER) {
urlConditions.put(href, NetworkCatalogItem.Accessibility.NEVER);
} else if (rel == REL_CONDITION_SIGNED_IN) {
@ -153,7 +154,7 @@ class OPDSLinkXMLReader extends OPDSXMLReader implements OPDSConstants, MimeType
sslCertificate = null;
}
INetworkLink result = link(siteName, title, summary, icon, language, links, urlConditions, sslCertificate);
INetworkLink result = link(siteName, title, summary, language, infos, urlConditions, sslCertificate);
if (result != null) {
myListener.onNewLink(result);
}
@ -164,13 +165,12 @@ class OPDSLinkXMLReader extends OPDSXMLReader implements OPDSConstants, MimeType
String siteName,
String title,
String summary,
String icon,
String language,
Map<String,String> links,
Map<String,UrlInfo> infos,
HashMap<String,NetworkCatalogItem.Accessibility> urlConditions,
String sslCertificate
) {
if (siteName == null || title == null || links.get(INetworkLink.URL_MAIN) == null) {
if (siteName == null || title == null || infos.get(INetworkLink.URL_MAIN) == null) {
return null;
}
@ -178,9 +178,8 @@ class OPDSLinkXMLReader extends OPDSXMLReader implements OPDSConstants, MimeType
siteName,
title,
summary,
icon,
language,
links,
infos,
myHasStableIdentifiers
);

View file

@ -34,7 +34,7 @@ import org.geometerplus.zlibrary.core.network.ZLNetworkRequest;
import org.geometerplus.fbreader.network.*;
import org.geometerplus.fbreader.network.authentication.NetworkAuthenticationManager;
class OPDSNetworkLink extends AbstractNetworkLink {
public class OPDSNetworkLink extends AbstractNetworkLink {
private TreeMap<RelationAlias, String> myRelationAliases;
private TreeMap<String,NetworkCatalogItem.Accessibility> myUrlConditions;
@ -44,9 +44,9 @@ class OPDSNetworkLink extends AbstractNetworkLink {
private final boolean myHasStableIdentifiers;
OPDSNetworkLink(String siteName, String title, String summary, String icon, String language,
Map<String, String> links, boolean hasStableIdentifiers) {
super(siteName, title, summary, icon, language, links);
OPDSNetworkLink(String siteName, String title, String summary, String language,
Map<String,UrlInfo> infos, boolean hasStableIdentifiers) {
super(siteName, title, summary, language, infos);
myHasStableIdentifiers = hasStableIdentifiers;
}
@ -113,13 +113,12 @@ class OPDSNetworkLink extends AbstractNetworkLink {
}
@Override
public OPDSCatalogItem.State createOperationData(INetworkLink link,
NetworkOperationData.OnNewItemListener listener) {
return new OPDSCatalogItem.State(link, listener);
public OPDSCatalogItem.State createOperationData(NetworkOperationData.OnNewItemListener listener) {
return new OPDSCatalogItem.State(this, listener);
}
public ZLNetworkRequest simpleSearchRequest(String pattern, NetworkOperationData data) {
final String url = getLink(URL_SEARCH);
final String url = getUrlInfo(URL_SEARCH).URL;
if (url == null) {
return null;
}
@ -135,9 +134,9 @@ class OPDSNetworkLink extends AbstractNetworkLink {
}
public NetworkCatalogItem libraryItem() {
TreeMap<Integer, String> urlMap = new TreeMap<Integer, String>();
urlMap.put(NetworkCatalogItem.URL_CATALOG, getLink(URL_MAIN));
return new OPDSCatalogItem(this, getTitle(), getSummary(), getIcon(), urlMap, myExtraData);
TreeMap<Integer,String> urlMap = new TreeMap<Integer,String>();
urlMap.put(NetworkCatalogItem.URL_CATALOG, getUrlInfo(URL_MAIN).URL);
return new OPDSCatalogItem(this, getTitle(), getSummary(), getUrlInfo(URL_ICON).URL, urlMap, myExtraData);
}
public NetworkAuthenticationManager authenticationManager() {

View file

@ -172,7 +172,7 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
private int myState = START;
private final StringBuffer myBuffer = new StringBuffer();
private final StringBuilder myBuffer = new StringBuilder();
private HtmlToString myHtmlToString = new HtmlToString();
private boolean myFeedMetadataProcessed;

View file

@ -23,22 +23,20 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
class OpenSearchDescription {
public static OpenSearchDescription createDefault(String template) {
return new OpenSearchDescription(template, 20, -1, -1);
return new OpenSearchDescription(template, -1, -1);
}
public final String Template;
public final int IndexOffset;
public final int PageOffset;
public final int ItemsPerPage;
public final int ItemsPerPage = 20;
public OpenSearchDescription(String template, int itemsPerPage, int indexOffset, int pageOffset) {
OpenSearchDescription(String template, int indexOffset, int pageOffset) {
Template = template;
IndexOffset = indexOffset;
PageOffset = pageOffset;
ItemsPerPage = itemsPerPage;
}
public boolean isValid() {
@ -62,13 +60,7 @@ class OpenSearchDescription {
if (name == "searchTerms") {
m.appendReplacement(query, searchTerms);
} else if (name == "count") {
if (ItemsPerPage > 0) {
m.appendReplacement(query, String.valueOf(ItemsPerPage));
} else if (optional) {
m.appendReplacement(query, "");
} else {
return null;
}
m.appendReplacement(query, String.valueOf(ItemsPerPage));
} else if (optional) {
m.appendReplacement(query, "");
} else if (name == "startIndex") {

View file

@ -30,15 +30,12 @@ import org.geometerplus.zlibrary.core.xml.ZLStringMap;
import org.geometerplus.zlibrary.core.xml.ZLXMLReaderAdapter;
class OpenSearchXMLReader extends ZLXMLReaderAdapter {
private final List<OpenSearchDescription> myDescriptions;
private final int myItemsPerPage;
private final String myBaseURL;
public OpenSearchXMLReader(String baseUrl, List<OpenSearchDescription> descriptions, int itemsPerPage) {
public OpenSearchXMLReader(String baseUrl, List<OpenSearchDescription> descriptions) {
myDescriptions = descriptions;
myItemsPerPage = itemsPerPage;
myBaseURL = baseUrl;
}
@ -115,7 +112,7 @@ class OpenSearchXMLReader extends ZLXMLReaderAdapter {
final int indexOffset = parseInt(attributes.getValue("indexOffset"));
final int pageOffset = parseInt(attributes.getValue("pageOffset"));
final OpenSearchDescription descr =
new OpenSearchDescription(template, myItemsPerPage, indexOffset, pageOffset);
new OpenSearchDescription(template, indexOffset, pageOffset);
if (descr.isValid()) {
myDescriptions.add(descr);
}

View file

@ -17,20 +17,16 @@
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.network;
package org.geometerplus.fbreader.network.tree;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.fbreader.network.NetworkLibraryItem;
import org.geometerplus.fbreader.network.NetworkItem;
import org.geometerplus.fbreader.network.NetworkTree;
import org.geometerplus.android.fbreader.tree.ZLAndroidTree;
public class AddCustomCatalogItemTree extends NetworkTree implements ZLAndroidTree {
public AddCustomCatalogItemTree() {
super(1);
public class AddCustomCatalogItemTree extends NetworkTree {
public AddCustomCatalogItemTree(NetworkTree parent) {
super(parent);
}
@Override
@ -44,11 +40,12 @@ public class AddCustomCatalogItemTree extends NetworkTree implements ZLAndroidTr
}
@Override
public NetworkLibraryItem getHoldedItem() {
public NetworkItem getHoldedItem() {
return null;
}
public int getCoverResourceId() {
return R.drawable.ic_list_plus;
@Override
protected String getStringId() {
return "@Add Custom Catalog";
}
}

View file

@ -120,7 +120,12 @@ public class NetworkAuthorTree extends NetworkTree {
}
@Override
public NetworkLibraryItem getHoldedItem() {
public NetworkItem getHoldedItem() {
return null;
}
@Override
protected String getStringId() {
return "@Author:" + Author.DisplayName + ":" + Author.SortKey;
}
}

View file

@ -69,7 +69,12 @@ public class NetworkBookTree extends NetworkTree {
}
@Override
public NetworkLibraryItem getHoldedItem() {
public NetworkItem getHoldedItem() {
return Book;
}
@Override
protected String getStringId() {
return "@Book:" + Book.Id + ":" + Book.Title;
}
}

View file

@ -22,8 +22,7 @@ package org.geometerplus.fbreader.network.tree;
import org.geometerplus.fbreader.network.*;
public class NetworkCatalogRootTree extends NetworkCatalogTree {
public NetworkCatalogRootTree(RootTree parent, INetworkLink link, int position) {
super(parent, (NetworkCatalogItem) link.libraryItem(), position);
super(parent, (NetworkCatalogItem)link.libraryItem(), position);
}
}

View file

@ -28,9 +28,8 @@ import org.geometerplus.fbreader.tree.FBTree;
import org.geometerplus.fbreader.network.*;
public class NetworkCatalogTree extends NetworkTree {
public final NetworkCatalogItem Item;
public final ArrayList<NetworkLibraryItem> ChildrenItems = new ArrayList<NetworkLibraryItem>();
public final ArrayList<NetworkItem> ChildrenItems = new ArrayList<NetworkItem>();
private long myLoadedTime = -1;
@ -62,7 +61,6 @@ public class NetworkCatalogTree extends NetworkTree {
return createCover(Item);
}
public boolean isContentValid() {
if (myLoadedTime < 0) {
return false;
@ -80,7 +78,6 @@ public class NetworkCatalogTree extends NetworkTree {
}
}
public void updateVisibility() {
final LinkedList<FBTree> toRemove = new LinkedList<FBTree>();
@ -89,7 +86,7 @@ public class NetworkCatalogTree extends NetworkTree {
int nodeCount = 0;
for (int i = 0; i < ChildrenItems.size(); ++i) {
NetworkLibraryItem currentItem = ChildrenItems.get(i);
NetworkItem currentItem = ChildrenItems.get(i);
if (!(currentItem instanceof NetworkCatalogItem)) {
continue;
}
@ -161,13 +158,18 @@ public class NetworkCatalogTree extends NetworkTree {
}
@Override
public NetworkLibraryItem getHoldedItem() {
public NetworkItem getHoldedItem() {
return Item;
}
@Override
public void removeItems(Set<NetworkLibraryItem> items) {
public void removeItems(Set<NetworkItem> items) {
ChildrenItems.removeAll(items);
super.removeItems(items);
}
@Override
protected String getStringId() {
return Item.getFullRequestString();
}
}

View file

@ -87,15 +87,20 @@ public class NetworkSeriesTree extends NetworkTree {
}
@Override
public NetworkLibraryItem getHoldedItem() {
public NetworkItem getHoldedItem() {
return null;
}
@Override
public void removeItems(Set<NetworkLibraryItem> items) {
public void removeItems(Set<NetworkItem> items) {
super.removeItems(items);
if (subTrees().isEmpty()) {
removeSelf();
}
}
@Override
protected String getStringId() {
return "@Series:" + SeriesTitle;
}
}

View file

@ -25,11 +25,11 @@ import org.geometerplus.fbreader.network.*;
public class NetworkTreeFactory {
public static NetworkTree createNetworkTree(NetworkCatalogTree parent, NetworkLibraryItem item) {
public static NetworkTree createNetworkTree(NetworkCatalogTree parent, NetworkItem item) {
return createNetworkTree(parent, item, -1);
}
public static NetworkTree createNetworkTree(NetworkCatalogTree parent, NetworkLibraryItem item, int position) {
public static NetworkTree createNetworkTree(NetworkCatalogTree parent, NetworkItem item, int position) {
final int subtreesSize = parent.subTrees().size();
if (position == -1) {
position = subtreesSize;
@ -80,6 +80,8 @@ public class NetworkTreeFactory {
}
return new NetworkBookTree(parent, book, position, showAuthors);
} else if (item instanceof TopUpItem) {
return new TopUpTree(parent, (TopUpItem)item);
}
return null;
}

View file

@ -19,7 +19,7 @@
package org.geometerplus.fbreader.network.tree;
import org.geometerplus.fbreader.network.NetworkLibraryItem;
import org.geometerplus.fbreader.network.NetworkItem;
import org.geometerplus.fbreader.network.NetworkTree;
public final class RootTree extends NetworkTree {
@ -29,7 +29,12 @@ public final class RootTree extends NetworkTree {
}
@Override
public NetworkLibraryItem getHoldedItem() {
public NetworkItem getHoldedItem() {
return null;
}
@Override
protected String getStringId() {
return "@Root";
}
}

View file

@ -17,7 +17,7 @@
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.network;
package org.geometerplus.fbreader.network.tree;
import java.util.Set;
import java.util.LinkedList;
@ -29,16 +29,11 @@ import org.geometerplus.fbreader.tree.FBTree;
import org.geometerplus.fbreader.network.*;
import org.geometerplus.fbreader.network.tree.NetworkAuthorTree;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.android.fbreader.tree.ZLAndroidTree;
public class SearchItemTree extends NetworkTree implements ZLAndroidTree {
public class SearchItemTree extends NetworkTree {
private SearchResult myResult;
public SearchItemTree() {
super(1);
public SearchItemTree(NetworkTree parent, int position) {
super(parent, position);
}
@Override
@ -51,10 +46,6 @@ public class SearchItemTree extends NetworkTree implements ZLAndroidTree {
return ZLResource.resource("networkView").getResource("searchSummary").getValue();
}
public int getCoverResourceId() {
return R.drawable.ic_list_searchresult;
}
public void setSearchResult(SearchResult result) {
myResult = result;
clear();
@ -99,7 +90,12 @@ public class SearchItemTree extends NetworkTree implements ZLAndroidTree {
}
@Override
public NetworkLibraryItem getHoldedItem() {
public NetworkItem getHoldedItem() {
return null;
}
@Override
protected String getStringId() {
return "@Search";
}
}

View file

@ -17,47 +17,36 @@
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.network;
package org.geometerplus.fbreader.network.tree;
import org.geometerplus.zlibrary.core.image.ZLImage;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.fbreader.network.INetworkLink;
import org.geometerplus.fbreader.network.NetworkLibraryItem;
import org.geometerplus.fbreader.network.TopUpItem;
import org.geometerplus.fbreader.network.NetworkTree;
import org.geometerplus.fbreader.network.tree.NetworkCatalogTree;
import org.geometerplus.fbreader.network.authentication.NetworkAuthenticationManager;
import org.geometerplus.zlibrary.ui.android.R;
public class TopUpTree extends NetworkTree {
public final TopUpItem Item;
import org.geometerplus.android.fbreader.tree.ZLAndroidTree;
class RefillAccountTree extends NetworkTree implements ZLAndroidTree {
public final INetworkLink Link;
public RefillAccountTree(NetworkCatalogTree parentTree) {
super(parentTree.Level + 1);
Link = parentTree.Item.Link;
}
public RefillAccountTree(INetworkLink link) {
super(1);
Link = link;
TopUpTree(NetworkCatalogTree parentTree, TopUpItem item) {
super(parentTree);
Item = item;
}
@Override
public String getName() {
return ZLResource.resource("networkView").getResource("refillTitle").getValue();
return Item.Title;
}
@Override
public String getSummary() {
final NetworkAuthenticationManager mgr = Link.authenticationManager();
final NetworkAuthenticationManager mgr = Item.Link.authenticationManager();
try {
if (mgr.isAuthorised(false)) {
final String account = mgr.currentAccount();
if (account != null) {
return ZLResource.resource("networkView").getResource("refillSummary").getValue().replace("%s", account);
return Item.Summary.replace("%s", account);
}
}
} catch (ZLNetworkException e) {
@ -66,11 +55,17 @@ class RefillAccountTree extends NetworkTree implements ZLAndroidTree {
}
@Override
public NetworkLibraryItem getHoldedItem() {
return null;
protected ZLImage createCover() {
return createCover(Item);
}
public int getCoverResourceId() {
return R.drawable.ic_list_library_wallet;
@Override
public TopUpItem getHoldedItem() {
return Item;
}
@Override
protected String getStringId() {
return "@TopUp Account";
}
}

View file

@ -29,10 +29,6 @@ public abstract class FBTree extends ZLTree<FBTree> implements Comparable<FBTree
private ZLImage myCover;
private boolean myCoverRequested;
protected FBTree(int level) {
super(level);
}
protected FBTree() {
super();
}

View file

@ -62,7 +62,7 @@ public abstract class ZLBase64EncodedImage extends ZLSingleImage {
try {
decode();
final File file = new File(decodedFileName());
return "imagefile://" + decodedFileName() + "\0000\000" + (int)file.length();
return ZLFileImage.SCHEME + "://" + decodedFileName() + "\0000\000" + (int)file.length();
} catch (Exception e) {
return null;
}

View file

@ -24,6 +24,8 @@ import java.io.*;
import org.geometerplus.zlibrary.core.filesystem.ZLFile;
public class ZLFileImage extends ZLSingleImage {
public static final String SCHEME = "imagefile";
private final ZLFile myFile;
private final int myOffset;
private final int myLength;
@ -40,7 +42,7 @@ public class ZLFileImage extends ZLSingleImage {
}
public String getURI() {
return "imagefile://" + myFile.getPath() + "\000" + myOffset + "\000" + myLength;
return SCHEME + "://" + myFile.getPath() + "\000" + myOffset + "\000" + myLength;
}
@Override

View file

@ -0,0 +1,84 @@
/*
* Copyright (C) 2007-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.zlibrary.core.options;
import java.util.*;
public class ZLStringListOption extends ZLOption {
private final List<String> myDefaultValue;
private List<String> myValue;
private static String listToString(List<String> list) {
if (list == null || list.isEmpty()) {
return "";
}
final StringBuilder builder = new StringBuilder();
boolean first = true;
for (String s : list) {
if (first) {
first = false;
} else {
builder.append(",");
}
builder.append(s);
}
return builder.toString();
}
private static List<String> stringToList(String str) {
if (str == null || "".equals(str)) {
return Collections.emptyList();
}
return Arrays.asList(str.split(","));
}
public ZLStringListOption(String group, String optionName, List<String> defaultValue) {
super(group, optionName);
myDefaultValue = (defaultValue != null) ? defaultValue : Collections.<String>emptyList();
myValue = myDefaultValue;
}
public List<String> getValue() {
if (!myIsSynchronized) {
final String value = getConfigValue(listToString(myDefaultValue));
if (value != null) {
myValue = stringToList(value);
}
myIsSynchronized = true;
}
return Collections.unmodifiableList(myValue);
}
public void setValue(List<String> value) {
if (value == null) {
value = Collections.emptyList();
}
if (myIsSynchronized && (myValue.equals(value))) {
return;
}
myValue = new ArrayList<String>(value);
if (value.equals(myDefaultValue)) {
unsetConfigValue();
} else {
setConfigValue(listToString(value));
}
myIsSynchronized = true;
}
}

View file

@ -27,10 +27,6 @@ public abstract class ZLTree<T extends ZLTree<T>> implements Iterable<T> {
public final int Level;
private ArrayList<T> mySubTrees;
protected ZLTree(int level) {
this(level, null, 0);
}
protected ZLTree() {
this(null);
}
@ -40,10 +36,6 @@ public abstract class ZLTree<T extends ZLTree<T>> implements Iterable<T> {
}
protected ZLTree(T parent, int position) {
this(0, parent, position);
}
private ZLTree(int nullLevel, T parent, int position) {
if (parent != null && (position < 0 || position > parent.subTrees().size())) {
throw new IndexOutOfBoundsException("`position` value equals " + position + " but must be in range [0; " + parent.subTrees().size() + "]");
}
@ -52,7 +44,7 @@ public abstract class ZLTree<T extends ZLTree<T>> implements Iterable<T> {
Level = parent.Level + 1;
parent.addSubTree((T)this, position);
} else {
Level = nullLevel;
Level = 0;
}
}

View file

@ -27,6 +27,10 @@ public abstract class ZLMiscUtil {
return (o0 == null) ? (o1 == null) : o0.equals(o1);
}
public static int hashCode(Object o) {
return o != null ? o.hashCode() : 0;
}
public static <T> boolean listsEquals(List<T> list1, List<T> list2) {
if (list1 == null) {
return list2 == null || list2.isEmpty();