1
0
Fork 0
mirror of https://github.com/geometer/FBReaderJ.git synced 2025-10-05 19:42:17 +02:00

New CustomCatalogDialog

git-svn-id: https://only.mawhrin.net/repos/FBReaderJ/trunk@1644 6a642e6f-84f6-412e-ac94-c4a38d5a04b0
This commit is contained in:
Vasiliy Bout 2010-08-03 17:38:54 +00:00
parent cb3a2778b7
commit b5b0f37ceb
21 changed files with 396 additions and 85 deletions

View file

@ -6,9 +6,9 @@ DONE Иконка к "Добавить каталог"
DONE Комментарий к этому пункту = "Добавление OPDS-каталога вручную"
** Какую-нибудь иконку к добавленным вручную каталогам
DONE Звездочку к 2-м обязательным пунктам при добавлении каталога
?? Убрать 3-й пункт при добавлении????
DONE Убрать 3-й пункт при добавлении
DONE Может быть сделать примеры мелким шрифтом под полями
** Сразу (по нажатию Ok) проверять и говорить, верный ли URL (Ex: Это не OPDS-каталог)
DONE Сразу (по нажатию Ok) проверять и говорить, верный ли URL (Ex: Это не OPDS-каталог)
Это не должно мешать добавлению, но пользователь д.б. предупрежден
(типа -- это не OPDS каталог -> Edit/Continue/Cancel)
кстати, при проверке можно было бы попробовать получить значение 3-го поля (summary)
@ -39,8 +39,8 @@ DONE Network library: Объединять книги по сериям
DONE Поддержка дополнительных currency codes (см. пункт 8.6.3.1)
?? Читать информацию для custom-каталогов из сети
** поиск (пункт 7.5)
** обложка
** поиск (пункт 7.5): чтение дополнительного XML-файла описания поиска
** обложка: поддержка .ico иконок в теге <icon></icon>
?? Поддержка перехда по URL на OPDS-каталог из браузера (добавлять каталог в библиотеку)
** Отображать ошибки / сообщения при загрузке каталогов

View file

@ -137,6 +137,8 @@
<node name="button">
<node name="buy" value="Kaufen" />
<node name="buyAndDownload" value="Kaufen und runterladen" />
<node name="continue" toBeTranslated="true" value="Continue" />
<node name="editUrl" toBeTranslated="true" value="Edit URL" />
</node>
<node name="BookInfo">
<node name="fileName" value="Datei Name"/>
@ -270,6 +272,7 @@
<node name="signOut" value="Signing out. Bitte warten..." />
<node name="purchaseBook" value="Buch wird gekauft. Bitte warten..." />
<node name="registerUser" value="Neuer Account wird registriert. Bitte warten..." />
<node name="loadingCatalogInfo" toBeTranslated="true" value="Loading catalog information. Please, wait..." />
</node>
<node name="networkError">
<node name="internalError" value="Interner Server-Fehler" />
@ -286,6 +289,8 @@
<node name="tooManyRegistrations" value="Zu viele Registrierungen von Ihrer IP;&#10;bitte in einigen Minuten noch einmal probieren" />
<node name="noUserEmail" value="Es gibt keinen registrierten Nutzer mit dieser Email-Adresse&#10;" />
<node name="unsupportedOperation" value="Operation wird nicht unterstützt" />
<node name="notAnOPDS" toBeTranslated="true" value="This is not an OPDS catalog" />
<node name="noRequiredInformation" toBeTranslated="true" value="Required information is not specified in the catalog" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Information" />

View file

@ -136,6 +136,8 @@
<node name="button">
<node name="buy" value="Buy" />
<node name="buyAndDownload" value="Buy and download" />
<node name="continue" value="Continue" />
<node name="editUrl" value="Edit URL" />
</node>
<node name="BookInfo">
<node name="fileName" value="File name"/>
@ -269,6 +271,7 @@
<node name="signOut" value="Signing out. Please, wait..." />
<node name="purchaseBook" value="Purchase book. Please, wait..." />
<node name="registerUser" value="Registering new account. Please, wait..." />
<node name="loadingCatalogInfo" value="Loading catalog information. Please, wait..." />
</node>
<node name="networkError">
<node name="internalError" value="Internal server error" />
@ -285,6 +288,8 @@
<node name="tooManyRegistrations" value="Too many registrations from your IP;&#10;try again in a few minutes" />
<node name="noUserEmail" value="No user was registered&#10;with specified e-mail address" />
<node name="unsupportedOperation" value="Unsupported operation" />
<node name="notAnOPDS" value="This is not an OPDS catalog" />
<node name="noRequiredInformation" value="Required information is not specified in the catalog" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Information" />

View file

@ -137,6 +137,8 @@
<node name="button">
<node name="buy" value="Acheter"/>
<node name="buyAndDownload" value="Acheter et télécharger"/>
<node name="continue" toBeTranslated="true" value="Continue" />
<node name="editUrl" toBeTranslated="true" value="Edit URL" />
</node>
<node name="BookInfo">
<node name="fileName" value="Nom du fichier"/>
@ -270,6 +272,7 @@
<node name="signOut" value="Déconnexion en cours. Patientez, s'il-vous plait..." />
<node name="purchaseBook" value="Achat du livre. Veuillez patienter..."/>
<node name="registerUser" value="Inscription en cours. Veuillez patienter..."/>
<node name="loadingCatalogInfo" toBeTranslated="true" value="Loading catalog information. Please, wait..." />
</node>
<node name="networkError">
<node name="internalError" value="Erreur interne du serveur"/>
@ -286,6 +289,8 @@
<node name="tooManyRegistrations" value="Trop d'inscriptions depuis votre IP ;&#10;essayer à nouvea dans quelques minutes"/>
<node name="noUserEmail" value="Aucun utilisateur n'est inscrit&#10;avec l'adresse email indiquée"/>
<node name="unsupportedOperation" value="Opération non supportée"/>
<node name="notAnOPDS" toBeTranslated="true" value="This is not an OPDS catalog" />
<node name="noRequiredInformation" toBeTranslated="true" value="Required information is not specified in the catalog" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Information"/>

View file

@ -137,6 +137,8 @@
<node name="button">
<node name="buy" value="Vásárlás" />
<node name="buyAndDownload" value="Vásárlás és letöltés" />
<node name="continue" toBeTranslated="true" value="Continue" />
<node name="editUrl" toBeTranslated="true" value="Edit URL" />
</node>
<node name="BookInfo">
<node name="fileName" value="Fájlnév"/>
@ -270,6 +272,7 @@
<node name="signOut" value="Kijelentkezés. Kérjük, várjon..." />
<node name="purchaseBook" value="Könyv vásárlása. Kérjük, várjon..." />
<node name="registerUser" value="Fiók létrehozása. Kérjük, várjon..." />
<node name="loadingCatalogInfo" toBeTranslated="true" value="Loading catalog information. Please, wait..." />
</node>
<node name="networkError">
<node name="internalError" value="Belső szerverhiba" />
@ -286,6 +289,8 @@
<node name="tooManyRegistrations" value="Túl sok regisztrációs kérés erről az IP-címről;&#10;próbálja meg pár perc múlva ismét!" />
<node name="noUserEmail" value="Nincs regisztrált felhasználónk&#10;ezzel az e-mail címmel" />
<node name="unsupportedOperation" value="nem támogaott művelet" />
<node name="notAnOPDS" toBeTranslated="true" value="This is not an OPDS catalog" />
<node name="noRequiredInformation" toBeTranslated="true" value="Required information is not specified in the catalog" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Információ" />

View file

@ -137,6 +137,8 @@
<node name="button">
<node name="buy" value="Compra" />
<node name="buyAndDownload" value="Compra e scarica" />
<node name="continue" toBeTranslated="true" value="Continue" />
<node name="editUrl" toBeTranslated="true" value="Edit URL" />
</node>
<node name="BookInfo">
<node name="fileName" value="Nome file"/>
@ -270,6 +272,7 @@
<node name="signOut" value="Disconnessione. Attendere prego..." />
<node name="purchaseBook" value="Acquisto libro. Attendere prego..." />
<node name="registerUser" value="Registrazione nuovo account. Attendere prego..." />
<node name="loadingCatalogInfo" toBeTranslated="true" value="Loading catalog information. Please, wait..." />
</node>
<node name="networkError">
<node name="internalError" value="Errore Server Interno" />
@ -286,6 +289,8 @@
<node name="tooManyRegistrations" value="Troppe registrazioni dal tuo IP;&#10;riprova fra qualche minuto" />
<node name="noUserEmail" value="Nessun utente è registrato&#10;con questo indirizzo e-mail" />
<node name="unsupportedOperation" value="Operazione non supportata" />
<node name="notAnOPDS" toBeTranslated="true" value="This is not an OPDS catalog" />
<node name="noRequiredInformation" toBeTranslated="true" value="Required information is not specified in the catalog" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Informazioni" />

View file

@ -136,6 +136,8 @@
<node name="button">
<node name="buy" value="Купить" />
<node name="buyAndDownload" value="Купить и скачать" />
<node name="continue" value="Продолжить" />
<node name="editUrl" value="Изменить URL" />
</node>
<node name="BookInfo">
<node name="fileName" value="Файл"/>
@ -269,6 +271,7 @@
<node name="signOut" value="Выход из системы. Подождите, пожалуйста..." />
<node name="purchaseBook" value="Идет покупка книги. Подождите, пожалуйста..." />
<node name="registerUser" value="Регистрируем пользователя. Подождите, пожалуйста..." />
<node name="loadingCatalogInfo" value="Загрузка информации из каталога. Подождите, пожалуйста..." />
</node>
<node name="networkError">
<node name="internalError" value="Внутренняя ошибка сервера" />
@ -285,6 +288,8 @@
<node name="tooManyRegistrations" value="Слишком много регистраций с вашего IP;&#10;попробуйте еще раз через пару минут" />
<node name="noUserEmail" value="Нет пользователя с таким адресом" />
<node name="unsupportedOperation" value="Неподдерживаемая операция" />
<node name="notAnOPDS" value="Это не OPDS каталог" />
<node name="noRequiredInformation" value="Требуемая информация не найдена в каталоге" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Информация" />

View file

@ -137,6 +137,8 @@
<node name="button">
<node name="buy" value="Mua" />
<node name="buyAndDownload" value="Mua và Tải về" />
<node name="continue" toBeTranslated="true" value="Continue" />
<node name="editUrl" toBeTranslated="true" value="Edit URL" />
</node>
<node name="BookInfo">
<node name="fileName" value="Tên tập tin"/>
@ -270,6 +272,7 @@
<node name="signOut" value="Đang thoát. Xin chờ..." />
<node name="purchaseBook" value="Mua sách. Xin chờ..." />
<node name="registerUser" value="Đang đăng ký tài khoản. Xin chờ..." />
<node name="loadingCatalogInfo" toBeTranslated="true" value="Loading catalog information. Please, wait..." />
</node>
<node name="networkError">
<node name="internalError" value="Lỗi mạng nội bộ " />
@ -286,6 +289,8 @@
<node name="tooManyRegistrations" value="Có quá nhiều đăng ký từ IP của bạn;&#10;thử lại sau vài phút" />
<node name="noUserEmail" value="Không người nào đang ký&#10;với địa chỉ email xác định" />
<node name="unsupportedOperation" value="Thao tác không được hỗ trợ" />
<node name="notAnOPDS" toBeTranslated="true" value="This is not an OPDS catalog" />
<node name="noRequiredInformation" toBeTranslated="true" value="Required information is not specified in the catalog" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="Thông tin" />

View file

@ -137,6 +137,8 @@
<node name="button">
<node name="buy" value="购买" />
<node name="buyAndDownload" value="购买 下载" />
<node name="continue" toBeTranslated="true" value="Continue" />
<node name="editUrl" toBeTranslated="true" value="Edit URL" />
</node>
<node name="BookInfo">
<node name="fileName" value="文件" />
@ -270,6 +272,7 @@
<node name="signOut" value="退出. 正在处理,请稍等..." />
<node name="purchaseBook" value="书籍购买中. 请等待..." />
<node name="registerUser" value="正在注册新账号,请稍等..." />
<node name="loadingCatalogInfo" toBeTranslated="true" value="Loading catalog information. Please, wait..." />
</node>
<node name="networkError">
<node name="internalError" value="内部服务器出错" />
@ -286,6 +289,8 @@
<node name="tooManyRegistrations" value="来自你的IP的注册数过多&#10;请稍候再尝试注册" />
<node name="noUserEmail" value="你的电子邮箱可以注册&#10;" />
<node name="unsupportedOperation" value="非法操作" />
<node name="notAnOPDS" toBeTranslated="true" value="This is not an OPDS catalog" />
<node name="noRequiredInformation" toBeTranslated="true" value="Required information is not specified in the catalog" />
</node>
<node name="emptyCatalogBox">
<node name="title" value="信息" />

View file

@ -11,16 +11,18 @@
android:paddingRight="10dp"
android:orientation="vertical"
>
<TextView
android:id="@+id/network_catalog_title_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
/>
<RelativeLayout
android:id="@+id/network_catalog_title_group"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<TextView
android:id="@+id/network_catalog_title_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:layout_alignParentTop="true"
/>
<TextView
android:id="@+id/network_catalog_title_star"
android:layout_width="wrap_content"
@ -29,6 +31,7 @@
android:textColor="#ffff0000"
android:layout_marginLeft="4dp"
android:layout_alignParentRight="true"
android:layout_below="@id/network_catalog_title_text"
/>
<EditText
android:id="@+id/network_catalog_title"
@ -37,27 +40,32 @@
android:singleLine="true"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/network_catalog_title_star"
android:layout_below="@id/network_catalog_title_text"
/>
<TextView
android:id="@+id/network_catalog_title_example"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:gravity="right"
android:layout_alignParentBottom="true"
android:layout_below="@id/network_catalog_title"
/>
</RelativeLayout>
<TextView
android:id="@+id/network_catalog_title_example"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:gravity="right"
/>
<TextView
android:id="@+id/network_catalog_url_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:layout_marginTop="5dp"
/>
<RelativeLayout
android:id="@+id/network_catalog_url_group"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
>
<TextView
android:id="@+id/network_catalog_url_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:layout_alignParentTop="true"
/>
<TextView
android:id="@+id/network_catalog_url_star"
android:layout_width="wrap_content"
@ -66,6 +74,7 @@
android:textColor="#ffff0000"
android:layout_marginLeft="4dp"
android:layout_alignParentRight="true"
android:layout_below="@id/network_catalog_url_text"
/>
<EditText
android:id="@+id/network_catalog_url"
@ -75,37 +84,49 @@
android:inputType="textUri"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/network_catalog_url_star"
android:layout_below="@id/network_catalog_url_text"
/>
<TextView
android:id="@+id/network_catalog_url_example"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:gravity="right"
android:layout_alignParentBottom="true"
android:layout_below="@id/network_catalog_url"
/>
</RelativeLayout>
<TextView
android:id="@+id/network_catalog_url_example"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:gravity="right"
/>
<TextView
android:id="@+id/network_catalog_summary_text"
<RelativeLayout
android:id="@+id/network_catalog_summary_group"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:layout_marginTop="5dp"
/>
<EditText
android:id="@+id/network_catalog_summary"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
/>
<TextView
android:id="@+id/network_catalog_summary_example"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:gravity="right"
/>
>
<TextView
android:id="@+id/network_catalog_summary_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:layout_alignParentTop="true"
/>
<EditText
android:id="@+id/network_catalog_summary"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:layout_below="@id/network_catalog_summary_text"
/>
<TextView
android:id="@+id/network_catalog_summary_example"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:gravity="right"
android:layout_alignParentBottom="true"
android:layout_below="@id/network_catalog_summary"
/>
</RelativeLayout>
<TextView
android:id="@+id/network_catalog_error"

View file

@ -19,15 +19,18 @@
package org.geometerplus.android.fbreader.network;
import java.util.HashMap;
import android.app.Dialog;
import android.app.AlertDialog;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.TextView;
import android.content.DialogInterface;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.ui.android.dialogs.ZLAndroidDialogManager;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.fbreader.network.*;
@ -40,6 +43,8 @@ class CustomCatalogDialog extends NetworkDialog {
private String myUrl;
private String mySummary;
private boolean myLinkWithoutInfo;
public CustomCatalogDialog() {
super("CustomCatalogDialog");
}
@ -47,6 +52,7 @@ class CustomCatalogDialog extends NetworkDialog {
@Override
protected void clearData() {
myTitle = myUrl = mySummary = null;
myLinkWithoutInfo = false;
}
@Override
@ -72,9 +78,11 @@ class CustomCatalogDialog extends NetworkDialog {
if (myTitle.length() == 0) {
myTitle = null;
final String err = myResource.getResource("titleIsEmpty").getValue();
sendError(true, false, err);
return;
if (myLink != null) {
final String err = myResource.getResource("titleIsEmpty").getValue();
sendError(true, false, err);
return;
}
}
if (myUrl.length() == 0) {
myUrl = null;
@ -103,7 +111,7 @@ class CustomCatalogDialog extends NetworkDialog {
}
final NetworkLibrary library = NetworkLibrary.Instance();
if (library.hasCustomLinkTitle(myTitle, (ICustomNetworkLink) myLink)) {
if (myLink != null && library.hasCustomLinkTitle(myTitle, (ICustomNetworkLink) myLink)) {
final String err = myResource.getResource("titleAlreadyExists").getValue();
sendError(true, false, err);
return;
@ -114,27 +122,75 @@ class CustomCatalogDialog extends NetworkDialog {
return;
}
if (myLink == null) {
final OPDSLinkReader reader = new OPDSLinkReader();
final HashMap<String, String> links = new HashMap<String, String>();
links.put(INetworkLink.URL_MAIN, myUrl);
final ICustomNetworkLink link = reader.createCustomLink(ICustomNetworkLink.INVALID_ID,
siteName, myTitle, mySummary, null, links);
if (link != null) {
NetworkLibrary.Instance().addCustomLink(link);
} else {
throw new RuntimeException("Unable to create link!!! Impossible!!!");
}
myLink = link;
} else {
if (myLink != null) {
final ICustomNetworkLink link = (ICustomNetworkLink) myLink;
link.setSiteName(siteName);
link.setTitle(myTitle);
link.setSummary(mySummary);
link.setLink(INetworkLink.URL_MAIN, myUrl);
link.saveLink();
if (myLinkWithoutInfo) {
NetworkLibrary.Instance().addCustomLink(link);
myLinkWithoutInfo = false;
} else {
link.saveLink();
}
sendSuccess(true);
return;
}
sendSuccess(true);
final OPDSLinkReader reader = new OPDSLinkReader();
myLinkWithoutInfo = true;
myLink = reader.createCustomLinkWithoutInfo(siteName, myUrl);
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
final String err = (String) msg.obj;
if (err != null) {
final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_NEGATIVE) {
sendSuccess(true);
} else {
if (which == DialogInterface.BUTTON_NEUTRAL) {
myLinkWithoutInfo = false;
myLink = null;
}
sendError(true, false, null);
}
}
};
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(err)
.setIcon(0)
.setPositiveButton(buttonResource.getResource("continue").getValue(), listener)
.setNeutralButton(buttonResource.getResource("editUrl").getValue(), listener)
.setNegativeButton(buttonResource.getResource("cancel").getValue(), listener)
.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
listener.onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
}
})
.create().show();
} else {
sendError(true, false, null);
}
}
};
final Runnable loadInfoRunnable = new Runnable() {
public void run() {
final ICustomNetworkLink link = (ICustomNetworkLink) myLink;
final String err = link.reloadInfo();
handler.sendMessage(handler.obtainMessage(0, err));
}
};
((ZLAndroidDialogManager)ZLAndroidDialogManager.Instance()).wait("loadingCatalogInfo", loadInfoRunnable, myActivity);
}
@Override
@ -149,15 +205,19 @@ class CustomCatalogDialog extends NetworkDialog {
if (myUrl == null) myUrl = myLink.getLink(INetworkLink.URL_MAIN);
if (mySummary == null) mySummary = myLink.getSummary();
}
((TextView) dialog.findViewById(R.id.network_catalog_title)).setText((myTitle != null) ? myTitle : "");
((TextView) dialog.findViewById(R.id.network_catalog_url)).setText((myUrl != null) ? myUrl : "");
((TextView) dialog.findViewById(R.id.network_catalog_summary)).setText((mySummary != null) ? mySummary : "");
((TextView) dialog.findViewById(R.id.network_catalog_title)).setText(myTitle);
((TextView) dialog.findViewById(R.id.network_catalog_url)).setText(myUrl);
((TextView) dialog.findViewById(R.id.network_catalog_summary)).setText(mySummary);
final int examplesVisibility = (myLink == null) ? View.VISIBLE : View.GONE;
final int examplesVisibility = (myLink == null || myLinkWithoutInfo) ? View.VISIBLE : View.GONE;
dialog.findViewById(R.id.network_catalog_title_example).setVisibility(examplesVisibility);
dialog.findViewById(R.id.network_catalog_url_example).setVisibility(examplesVisibility);
dialog.findViewById(R.id.network_catalog_summary_example).setVisibility(examplesVisibility);
final int groupsVisibility = (myLink != null) ? View.VISIBLE : View.GONE;
dialog.findViewById(R.id.network_catalog_title_group).setVisibility(groupsVisibility);
dialog.findViewById(R.id.network_catalog_summary_group).setVisibility(groupsVisibility);
final TextView error = (TextView) dialog.findViewById(R.id.network_catalog_error);
if (myErrorMessage == null) {
error.setVisibility(View.GONE);

View file

@ -40,4 +40,6 @@ public interface ICustomNetworkLink extends INetworkLink {
void setLink(String urlKey, String url);
void removeLink(String urlKey);
String reloadInfo();
}

View file

@ -34,7 +34,7 @@ public class ATOMFeedMetadata extends ATOMCommonAttributes {
public LinkedList<ATOMLink> Links = new LinkedList<ATOMLink>();
public ATOMLogo Logo;
//public String Rights; // TODO: implement ATOMTextConstruct
//public String Subtitle; // TODO: implement ATOMTextConstruct
public String Subtitle; // TODO: implement ATOMTextConstruct
public String Title; // TODO: implement ATOMTextConstruct
public ATOMUpdated Updated;
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (C) 2010 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 org.geometerplus.zlibrary.core.util.ZLNetworkUtil;
import org.geometerplus.fbreader.network.atom.ATOMLink;
public class CatalogInfoReader implements OPDSFeedReader {
public boolean FeedStarted;
public String Icon;
public String Title;
public String Summary;
public String OpensearchDescriptionURL;
public String SearchURL;
private final String myBaseURL;
private final OPDSLink myLink;
public CatalogInfoReader(String baseUrl, OPDSLink link) {
myBaseURL = baseUrl;
myLink = link;
}
public boolean processFeedMetadata(OPDSFeedMetadata feed, boolean beforeEntries) {
Icon = (feed.Icon != null) ? feed.Icon.Uri : null;
Title = feed.Title;
Summary = feed.Subtitle;
for (ATOMLink link: feed.Links) {
String type = link.getType();
String rel = myLink.relation(link.getRel(), type);
if (rel == "search") {
if (type == OPDSConstants.MIME_APP_OPENSEARCHDESCRIPTION) {
OpensearchDescriptionURL = ZLNetworkUtil.url(myBaseURL, link.getHref());
} else if (type == OPDSConstants.MIME_APP_ATOM) {
SearchURL = ZLNetworkUtil.url(myBaseURL, link.getHref());
}
}
}
return true;
}
public void processFeedStart() {
FeedStarted = true;
}
public void processFeedEnd() {
}
public boolean processFeedEntry(OPDSEntry entry) {
return true;
}
}

View file

@ -60,7 +60,7 @@ class NetworkOPDSFeedReader implements OPDSFeedReader {
myData.ResumeURI = myBaseURL;
}
public void processFeedMetadata(OPDSFeedMetadata feed, boolean beforeEntries) {
public boolean processFeedMetadata(OPDSFeedMetadata feed, boolean beforeEntries) {
if (beforeEntries) {
myIndex = feed.OpensearchStartIndex - 1;
if (feed.OpensearchItemsPerPage > 0) {
@ -70,7 +70,7 @@ class NetworkOPDSFeedReader implements OPDSFeedReader {
myItemsToLoad = len;
}
}
return;
return false;
}
final OPDSLink opdsLink = (OPDSLink) myData.Link;
for (ATOMLink link: feed.Links) {
@ -80,6 +80,7 @@ class NetworkOPDSFeedReader implements OPDSFeedReader {
myNextURL = ZLNetworkUtil.url(myBaseURL, link.getHref());
}
}
return false;
}
public void processFeedEnd() {
@ -129,11 +130,49 @@ class NetworkOPDSFeedReader implements OPDSFeedReader {
&& myData.Listener.confirmInterrupt();
}
private String calculateEntryId(OPDSEntry entry) {
if (entry.Id != null) {
return entry.Id.Uri;
}
String id = null;
int idType = 0;
final OPDSLink opdsLink = (OPDSLink) myData.Link;
for (ATOMLink link: entry.Links) {
final String type = link.getType();
final String rel = opdsLink.relation(link.getRel(), type);
if (rel == null && type == OPDSConstants.MIME_APP_ATOM) {
return ZLNetworkUtil.url(myBaseURL, link.getHref());
}
int relType = BookReference.Format.NONE;
if (rel == null || rel.equals(OPDSConstants.REL_ACQUISITION_PREFIX)) {
relType = formatByMimeType(type);
}
if (relType != BookReference.Format.NONE
&& (id == null || idType < relType)) {
id = ZLNetworkUtil.url(myBaseURL, link.getHref());
idType = relType;
}
}
return id;
}
public boolean processFeedEntry(OPDSEntry entry) {
if (myItemsToLoad >= 0) {
--myItemsToLoad;
}
if (entry.Id == null) {
final String id = calculateEntryId(entry);
if (id == null) {
return tryInterrupt();
}
entry.Id = new ATOMId();
entry.Id.Uri = id;
}
if (mySkipUntilId != null) {
if (mySkipUntilId.equals(entry.Id.Uri)) {
mySkipUntilId = null;

View file

@ -50,6 +50,9 @@ interface OPDSConstants {
String MIME_APP_PDF = "application/pdf";
String MIME_APP_ATOM = "application/atom+xml";
// MIME type for Opensearch Description XML document
String MIME_APP_OPENSEARCHDESCRIPTION = "application/opensearchdescription+xml";
// a special MIME type for the litres OPDS catalog
String MIME_APP_LITRES = "application/litres+xml";

View file

@ -19,9 +19,17 @@
package org.geometerplus.fbreader.network.opds;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
import java.util.Map;
import org.geometerplus.zlibrary.core.network.ZLNetworkManager;
import org.geometerplus.zlibrary.core.network.ZLNetworkRequest;
import org.geometerplus.fbreader.network.ICustomNetworkLink;
import org.geometerplus.fbreader.network.INetworkLink;
import org.geometerplus.fbreader.network.NetworkErrors;
class OPDSCustomLink extends OPDSLink implements ICustomNetworkLink {
@ -81,4 +89,35 @@ class OPDSCustomLink extends OPDSLink implements ICustomNetworkLink {
public final void removeLink(String urlKey) {
myLinks.remove(urlKey);
}
public String reloadInfo() {
return ZLNetworkManager.Instance().perform(new ZLNetworkRequest(getLink(INetworkLink.URL_MAIN)) {
@Override
public String handleStream(URLConnection connection, InputStream inputStream) throws IOException {
final CatalogInfoReader info = new CatalogInfoReader(URL, OPDSCustomLink.this);
new OPDSXMLReader(info).read(inputStream);
if (!info.FeedStarted) {
return NetworkErrors.errorMessage("notAnOPDS");
}
if (info.Title == null) {
return NetworkErrors.errorMessage("noRequiredInformation");
}
myTitle = info.Title;
if (info.Icon != null) {
myIcon = info.Icon;
}
if (info.Summary != null) {
mySummary = info.Summary;
}
if (info.SearchURL != null) {
setLink(URL_SEARCH, info.SearchURL);
} else if (info.OpensearchDescriptionURL != null) {
// TODO: implement OpensearchDescription reading
}
return null;
}
});
}
}

View file

@ -22,7 +22,9 @@ package org.geometerplus.fbreader.network.opds;
interface OPDSFeedReader {
void processFeedStart();
void processFeedMetadata(OPDSFeedMetadata feed, boolean beforeEntries);
// return true to interrupt reading; return false to continue reading
boolean processFeedMetadata(OPDSFeedMetadata feed, boolean beforeEntries);
// return true to interrupt reading; return false to continue reading
boolean processFeedEntry(OPDSEntry entry);

View file

@ -115,7 +115,12 @@ class OPDSLink extends AbstractNetworkLink {
}
private final String searchURL(String query) {
return getLink(URL_SEARCH).replace("%s", query);
final String url = getLink(URL_SEARCH);
final String searchTerms = "{searchTerms}";
if (url.contains(searchTerms)) {
return url.replace(searchTerms, query);
}
return url.replace("%s", query);
}
@Override

View file

@ -102,6 +102,12 @@ public class OPDSLinkReader extends ZLXMLReaderAdapter {
return link;
}
public ICustomNetworkLink createCustomLinkWithoutInfo(String siteName, String url) {
final HashMap<String, String> links = new HashMap<String, String>();
links.put(INetworkLink.URL_MAIN, url);
return new OPDSCustomLink(ICustomNetworkLink.INVALID_ID, siteName, null, null, null, links);
}
public INetworkLink readDocument(ZLFile file) {
mySiteName = myTitle = mySummary = myIcon = /*mySearchType = */myAuthenticationType = mySSLCertificate = null;
myHasStableIdentifiers = false;

View file

@ -135,6 +135,7 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
private static final int OPENSEARCH_ITEMSPERPAGE = 32;
private static final int OPENSEARCH_STARTINDEX = 33;
private static final int FEC_HACK_SPAN = 34;
private static final int F_SUBTITLE = 35;
private static final String TAG_FEED = "feed";
@ -152,6 +153,7 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
private static final String TAG_TITLE = "title";
private static final String TAG_UPDATED = "updated";
private static final String TAG_PRICE = "price";
private static final String TAG_SUBTITLE = "subtitle";
private static final String TAG_HACK_SPAN = "span";
@ -196,6 +198,7 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
}
myBuffer.delete(0, myBuffer.length());
boolean interruptReading = false;
switch (myState) {
case START:
if (tagPrefix == myAtomNamespaceId && tag == TAG_FEED) {
@ -229,6 +232,11 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
//myTitle.readAttributes(attributes);
myHtmlToString.setupTextContent(attributes.getValue("type"));
myState = F_TITLE;
} else if (tag == TAG_SUBTITLE) {
//mySubtitle = new ATOMTitle(); // TODO:implement ATOMTextConstruct & ATOMSubtitle
//mySubtitle.readAttributes(attributes);
myHtmlToString.setupTextContent(attributes.getValue("type"));
myState = F_SUBTITLE;
} else if (tag == TAG_UPDATED) {
myUpdated = new ATOMUpdated();
myUpdated.readAttributes(attributes);
@ -238,8 +246,8 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
myEntry.readAttributes(attributes);
myState = F_ENTRY;
// Process feed metadata just before first feed entry
if (myFeed != null && myFeed.Id != null && !myFeedMetadataProcessed) {
myFeedReader.processFeedMetadata(myFeed, true);
if (myFeed != null && !myFeedMetadataProcessed) {
interruptReading = myFeedReader.processFeedMetadata(myFeed, true);
myFeedMetadataProcessed = true;
}
}
@ -354,13 +362,14 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
case FE_SUMMARY:
case FE_TITLE:
case F_TITLE:
case F_SUBTITLE:
myHtmlToString.processTextContent(false, tag, attributes, bufferContent);
break;
default:
break;
}
return false;
return interruptReading;
}
@Override
@ -390,8 +399,8 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
break;
case FEED:
if (tagPrefix == myAtomNamespaceId && tag == TAG_FEED) {
if (myFeed != null && myFeed.Id != null) {
myFeedReader.processFeedMetadata(myFeed, false);
if (myFeed != null) {
interruptReading = myFeedReader.processFeedMetadata(myFeed, false);
}
myFeed = null;
myFeedReader.processFeedEnd();
@ -400,7 +409,7 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
break;
case F_ENTRY:
if (tagPrefix == myAtomNamespaceId && tag == TAG_ENTRY) {
if (myEntry != null && myEntry.Id != null) {
if (myEntry != null) {
interruptReading = myFeedReader.processFeedEntry(myEntry);
}
myEntry = null;
@ -448,6 +457,18 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
myHtmlToString.processTextContent(true, tag, null, bufferContent);
}
break;
case F_SUBTITLE:
if (tagPrefix == myAtomNamespaceId && tag == TAG_SUBTITLE) {
// TODO:implement ATOMTextConstruct & ATOMSubtitle
final String subtitle = myHtmlToString.finishTextContent(bufferContent);
if (myFeed != null) {
myFeed.Subtitle = subtitle;
}
myState = FEED;
} else {
myHtmlToString.processTextContent(true, tag, null, bufferContent);
}
break;
case F_UPDATED:
if (tagPrefix == myAtomNamespaceId && tag == TAG_UPDATED) {
// FIXME:uri can be lost:buffer will be truncated, if there are extension tags inside the <id> tag