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

Partially loaded catalogs

git-svn-id: https://only.mawhrin.net/repos/FBReaderJ/trunk@1607 6a642e6f-84f6-412e-ac94-c4a38d5a04b0
This commit is contained in:
Vasiliy Bout 2010-07-20 18:45:51 +00:00
parent 0566a317cc
commit d0230f72cb
14 changed files with 219 additions and 68 deletions

View file

@ -8,7 +8,7 @@ DONE сделать SeekBar (как в ветке alex) в пункте Navigate
** синхронизация ресурсов (последняя - 1604) ** синхронизация ресурсов (последняя - 1604)
** Записать изменения в ChangeLog ** Записать изменения в ChangeLog
* каталог должен иметь не 2 состояния (загружен/не загружен), а загружен полностью/не полностью/не загружен: DONE каталог должен иметь не 2 состояния (загружен/не загружен), а загружен полностью/не полностью/не загружен:
в случае загрузки "не полностью" при повторном открытии продолжать загрузку с точки, где прервались (для тех каталогов, что поделены на куски по 20 книг) в случае загрузки "не полностью" при повторном открытии продолжать загрузку с точки, где прервались (для тех каталогов, что поделены на куски по 20 книг)
* 3 иконки в верхнем-правом углу для книжек * 3 иконки в верхнем-правом углу для книжек

View file

@ -30,8 +30,8 @@ abstract class ItemsLoadingRunnable implements Runnable {
private final ItemsLoadingHandler myHandler; private final ItemsLoadingHandler myHandler;
private final long myUpdateInterval; // in milliseconds public final long UpdateInterval; // in milliseconds
private final int myItemsLimit; public final int ItemsLimit;
private boolean myInterruptRequested; private boolean myInterruptRequested;
private boolean myInterruptConfirmed; private boolean myInterruptConfirmed;
@ -42,13 +42,13 @@ abstract class ItemsLoadingRunnable implements Runnable {
private Object myFinishedLock = new Object(); private Object myFinishedLock = new Object();
public void interrupt() { public void interruptLoading() {
synchronized (myInterruptLock) { synchronized (myInterruptLock) {
myInterruptRequested = true; myInterruptRequested = true;
} }
} }
private boolean confirmInterrupt() { private boolean confirmInterruptLoading() {
synchronized (myInterruptLock) { synchronized (myInterruptLock) {
if (myInterruptRequested) { if (myInterruptRequested) {
myInterruptConfirmed = true; myInterruptConfirmed = true;
@ -57,7 +57,7 @@ abstract class ItemsLoadingRunnable implements Runnable {
} }
} }
public boolean tryResume() { public boolean tryResumeLoading() {
synchronized (myInterruptLock) { synchronized (myInterruptLock) {
if (!myInterruptConfirmed) { if (!myInterruptConfirmed) {
myInterruptRequested = false; myInterruptRequested = false;
@ -66,12 +66,18 @@ abstract class ItemsLoadingRunnable implements Runnable {
} }
} }
private boolean isInterrupted() { private boolean isLoadingInterrupted() {
synchronized (myInterruptLock) { synchronized (myInterruptLock) {
return myInterruptConfirmed; return myInterruptConfirmed;
} }
} }
private boolean isLoadingInterruptRequested() {
synchronized (myInterruptLock) {
return myInterruptRequested;
}
}
public ItemsLoadingRunnable(ItemsLoadingHandler handler) { public ItemsLoadingRunnable(ItemsLoadingHandler handler) {
this(handler, 1000, 500); this(handler, 1000, 500);
@ -79,8 +85,8 @@ abstract class ItemsLoadingRunnable implements Runnable {
public ItemsLoadingRunnable(ItemsLoadingHandler handler, long updateIntervalMillis, int itemsLimit) { public ItemsLoadingRunnable(ItemsLoadingHandler handler, long updateIntervalMillis, int itemsLimit) {
myHandler = handler; myHandler = handler;
myUpdateInterval = updateIntervalMillis; UpdateInterval = updateIntervalMillis;
myItemsLimit = itemsLimit; ItemsLimit = itemsLimit;
} }
public abstract String doBefore(); public abstract String doBefore();
@ -98,24 +104,29 @@ abstract class ItemsLoadingRunnable implements Runnable {
err = doLoading(new NetworkOperationData.OnNewItemListener() { err = doLoading(new NetworkOperationData.OnNewItemListener() {
private long myUpdateTime; private long myUpdateTime;
private int myItemsNumber; private int myItemsNumber;
public boolean onNewItem(NetworkLibraryItem item) { public void onNewItem(NetworkLibraryItem item) {
myHandler.addItem(item); myHandler.addItem(item);
final boolean interrupted = confirmInterrupt(); ++myItemsNumber;
if (interrupted || myItemsNumber++ >= myItemsLimit) {
return true;
}
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
if (now > myUpdateTime) { if (now > myUpdateTime) {
myHandler.sendUpdateItems(); myHandler.sendUpdateItems();
myUpdateTime = now + myUpdateInterval; myUpdateTime = now + UpdateInterval;
} }
return false; }
public boolean requestInterrupt() {
return isLoadingInterruptRequested() || myItemsNumber >= ItemsLimit;
}
public boolean confirmInterrupt() {
return confirmInterruptLoading() || myItemsNumber >= ItemsLimit;
} }
}); });
myHandler.sendUpdateItems(); myHandler.sendUpdateItems();
myHandler.ensureItemsProcessed(); myHandler.ensureItemsProcessed();
myHandler.sendFinish(err, isInterrupted()); myHandler.sendFinish(err, isLoadingInterrupted());
myHandler.ensureFinishProcessed(); myHandler.ensureFinishProcessed();
}
void runFinishHandler() {
synchronized (myFinishedLock) { synchronized (myFinishedLock) {
if (myFinishedHandler != null) { if (myFinishedHandler != null) {
myFinishedHandler.sendEmptyMessage(0); myFinishedHandler.sendEmptyMessage(0);
@ -124,6 +135,7 @@ abstract class ItemsLoadingRunnable implements Runnable {
} }
} }
public void runOnFinish(final Runnable runnable) { public void runOnFinish(final Runnable runnable) {
if (myFinishedHandler != null) { if (myFinishedHandler != null) {
return; return;

View file

@ -288,7 +288,8 @@ class NetworkCatalogActions extends NetworkTreeActions {
} }
public void onFinish(String errorMessage, boolean interrupted) { public void onFinish(String errorMessage, boolean interrupted) {
if (interrupted) { if (interrupted &&
(!myTree.Item.supportsResumeLoading() || errorMessage != null)) {
myTree.ChildrenItems.clear(); myTree.ChildrenItems.clear();
myTree.clear(); myTree.clear();
} else { } else {
@ -336,11 +337,14 @@ class NetworkCatalogActions extends NetworkTreeActions {
private final NetworkCatalogTree myTree; private final NetworkCatalogTree myTree;
private final boolean myCheckAuthentication; private final boolean myCheckAuthentication;
private final boolean myResumeNotLoad;
public ExpandCatalogRunnable(ItemsLoadingHandler handler, NetworkCatalogTree tree, boolean checkAuthentication) { public ExpandCatalogRunnable(ItemsLoadingHandler handler,
NetworkCatalogTree tree, boolean checkAuthentication, boolean resumeNotLoad) {
super(handler); super(handler);
myTree = tree; myTree = tree;
myCheckAuthentication = checkAuthentication; myCheckAuthentication = checkAuthentication;
myResumeNotLoad = resumeNotLoad;
} }
public String getResourceKey() { public String getResourceKey() {
@ -369,6 +373,9 @@ class NetworkCatalogActions extends NetworkTreeActions {
} }
public String doLoading(NetworkOperationData.OnNewItemListener doWithListener) { public String doLoading(NetworkOperationData.OnNewItemListener doWithListener) {
if (myResumeNotLoad) {
return myTree.Item.resumeLoading(doWithListener);
}
return myTree.Item.loadChildren(doWithListener); return myTree.Item.loadChildren(doWithListener);
} }
} }
@ -380,10 +387,15 @@ class NetworkCatalogActions extends NetworkTreeActions {
} }
NetworkView.Instance().tryResumeLoading(activity, tree, url, new Runnable() { NetworkView.Instance().tryResumeLoading(activity, tree, url, new Runnable() {
public void run() { public void run() {
boolean resumeNotLoad = false;
if (tree.hasChildren()) { if (tree.hasChildren()) {
if (tree.isContentValid()) { if (tree.isContentValid()) {
if (tree.Item.supportsResumeLoading()) {
resumeNotLoad = true;
} else {
NetworkView.Instance().openTree(activity, tree, url); NetworkView.Instance().openTree(activity, tree, url);
return; return;
}
} else { } else {
tree.ChildrenItems.clear(); tree.ChildrenItems.clear();
tree.clear(); tree.clear();
@ -394,7 +406,7 @@ class NetworkCatalogActions extends NetworkTreeActions {
NetworkView.Instance().startItemsLoading( NetworkView.Instance().startItemsLoading(
activity, activity,
url, url,
new ExpandCatalogRunnable(handler, tree, true) new ExpandCatalogRunnable(handler, tree, true, resumeNotLoad)
); );
NetworkView.Instance().openTree(activity, tree, url); NetworkView.Instance().openTree(activity, tree, url);
} }
@ -416,7 +428,7 @@ class NetworkCatalogActions extends NetworkTreeActions {
NetworkView.Instance().startItemsLoading( NetworkView.Instance().startItemsLoading(
activity, activity,
url, url,
new ExpandCatalogRunnable(handler, tree, false) new ExpandCatalogRunnable(handler, tree, false, false)
); );
} }

View file

@ -216,7 +216,7 @@ public class NetworkCatalogActivity extends NetworkBaseActivity {
if (key != null && NetworkView.Instance().isInitialized()) { if (key != null && NetworkView.Instance().isInitialized()) {
final ItemsLoadingRunnable runnable = NetworkView.Instance().getItemsLoadingRunnable(key); final ItemsLoadingRunnable runnable = NetworkView.Instance().getItemsLoadingRunnable(key);
if (runnable != null) { if (runnable != null) {
runnable.interrupt(); runnable.interruptLoading();
} }
} }
} }

View file

@ -153,9 +153,12 @@ class NetworkView {
} }
} }
ItemsLoadingRunnable removeItemsLoadingRunnable(String key) { void removeItemsLoadingRunnable(String key) {
synchronized (myItemsLoadingRunnables) { synchronized (myItemsLoadingRunnables) {
return myItemsLoadingRunnables.remove(key); ItemsLoadingRunnable runnable = myItemsLoadingRunnables.remove(key);
if (runnable != null) {
runnable.runFinishHandler();
}
} }
} }
@ -164,8 +167,8 @@ class NetworkView {
} }
public void tryResumeLoading(NetworkBaseActivity activity, NetworkTree tree, String key, Runnable expandRunnable) { public void tryResumeLoading(NetworkBaseActivity activity, NetworkTree tree, String key, Runnable expandRunnable) {
final ItemsLoadingRunnable runnable = NetworkView.Instance().getItemsLoadingRunnable(key); final ItemsLoadingRunnable runnable = getItemsLoadingRunnable(key);
if (runnable != null && runnable.tryResume()) { if (runnable != null && runnable.tryResumeLoading()) {
openTree(activity, tree, key); openTree(activity, tree, key);
return; return;
} }

View file

@ -73,4 +73,9 @@ public abstract class AbstractNetworkLink implements INetworkLink {
public Set<String> getLinkKeys() { public Set<String> getLinkKeys() {
return myLinks.keySet(); return myLinks.keySet();
} }
public NetworkOperationData createOperationData(INetworkLink link,
NetworkOperationData.OnNewItemListener listener) {
return new NetworkOperationData(link, listener);
}
} }

View file

@ -44,6 +44,9 @@ public interface INetworkLink {
Set<String> getLinkKeys(); Set<String> getLinkKeys();
NetworkOperationData createOperationData(INetworkLink link,
NetworkOperationData.OnNewItemListener listener);
ZLNetworkRequest simpleSearchRequest(String pattern, NetworkOperationData data); ZLNetworkRequest simpleSearchRequest(String pattern, NetworkOperationData data);
ZLNetworkRequest resume(NetworkOperationData data); ZLNetworkRequest resume(NetworkOperationData data);

View file

@ -92,13 +92,14 @@ public abstract class NetworkCatalogItem extends NetworkLibraryItem {
public abstract String loadChildren(NetworkOperationData.OnNewItemListener listener); // returns Error Message public abstract String loadChildren(NetworkOperationData.OnNewItemListener listener); // returns Error Message
/*public final String loadChildren(final List<NetworkLibraryItem> children) { public boolean supportsResumeLoading() {
return loadChildren(new CatalogListener() { return false;
public void onNewItem(NetworkLibraryItem item) {
children.add(item);
} }
});
}*/ public String resumeLoading(NetworkOperationData.OnNewItemListener listener) { // returns Error Message
return NetworkErrors.errorMessage(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
}
/** /**
* Method is called each time this item is displayed to the user. * Method is called each time this item is displayed to the user.

View file

@ -410,9 +410,15 @@ public class NetworkLibrary {
LinkedList<ZLNetworkRequest> requestList = new LinkedList<ZLNetworkRequest>(); LinkedList<ZLNetworkRequest> requestList = new LinkedList<ZLNetworkRequest>();
LinkedList<NetworkOperationData> dataList = new LinkedList<NetworkOperationData>(); LinkedList<NetworkOperationData> dataList = new LinkedList<NetworkOperationData>();
NetworkOperationData.OnNewItemListener synchronizedListener = new NetworkOperationData.OnNewItemListener() { final NetworkOperationData.OnNewItemListener synchronizedListener = new NetworkOperationData.OnNewItemListener() {
public synchronized boolean onNewItem(NetworkLibraryItem item) { public synchronized void onNewItem(NetworkLibraryItem item) {
return listener.onNewItem(item); listener.onNewItem(item);
}
public synchronized boolean requestInterrupt() {
return listener.requestInterrupt();
}
public synchronized boolean confirmInterrupt() {
return listener.confirmInterrupt();
} }
}; };
@ -421,8 +427,8 @@ public class NetworkLibrary {
//if (link.OnOption.getValue()) { //if (link.OnOption.getValue()) {
// execute next code only if link is enabled // execute next code only if link is enabled
//} //}
NetworkOperationData data = new NetworkOperationData(link, synchronizedListener); final NetworkOperationData data = link.createOperationData(link, synchronizedListener);
ZLNetworkRequest request = link.simpleSearchRequest(pattern, data); final ZLNetworkRequest request = link.simpleSearchRequest(pattern, data);
if (request != null) { if (request != null) {
dataList.add(data); dataList.add(data);
requestList.add(request); requestList.add(request);

View file

@ -25,12 +25,15 @@ import org.geometerplus.zlibrary.core.network.ZLNetworkRequest;
public class NetworkOperationData { public class NetworkOperationData {
public interface OnNewItemListener { public interface OnNewItemListener {
// return true to interrupt reading; return false to continue reading void onNewItem(NetworkLibraryItem item);
boolean onNewItem(NetworkLibraryItem item);
// return true to confirm interrupt reading; return false to continue reading
boolean requestInterrupt();
boolean confirmInterrupt();
} }
public final INetworkLink Link; public final INetworkLink Link;
public final OnNewItemListener Listener; public OnNewItemListener Listener;
public String ResumeURI; public String ResumeURI;
private int myResumeCount; private int myResumeCount;
@ -48,6 +51,8 @@ public class NetworkOperationData {
if (++myResumeCount >= 10) { // FIXME: hardcoded resume limit constant!!! if (++myResumeCount >= 10) { // FIXME: hardcoded resume limit constant!!!
return null; return null;
} }
return Link.resume(this); final ZLNetworkRequest request = Link.resume(this);
clear();
return request;
} }
} }

View file

@ -31,10 +31,14 @@ import org.geometerplus.fbreader.network.authentication.litres.LitResBookshelfIt
class NetworkOPDSFeedReader implements OPDSFeedReader { class NetworkOPDSFeedReader implements OPDSFeedReader {
private final String myBaseURL; private final String myBaseURL;
private final NetworkOperationData myData; private final OPDSCatalogItem.State myData;
private int myIndex; private int myIndex;
private String myNextURL;
private String mySkipUntilTitle;
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 NetworkLibraryItem objects from OPDS feeds.
@ -43,15 +47,17 @@ class NetworkOPDSFeedReader implements OPDSFeedReader {
* @param result network results buffer. Must be created using OPDSLink corresponding to the OPDS feed, * @param result network results buffer. Must be created using OPDSLink corresponding to the OPDS feed,
* that will be read using this instance of the reader. * that will be read using this instance of the reader.
*/ */
NetworkOPDSFeedReader(String baseURL, NetworkOperationData result) { NetworkOPDSFeedReader(String baseURL, OPDSCatalogItem.State result) {
myBaseURL = baseURL; myBaseURL = baseURL;
myData = result; myData = result;
mySkipUntilTitle = myData.LastLoadedTitle;
if (!(result.Link instanceof OPDSLink)) { if (!(result.Link instanceof OPDSLink)) {
throw new IllegalArgumentException("Parameter `result` has invalid `Link` field value: result.Link must be an instance of OPDSLink class."); throw new IllegalArgumentException("Parameter `result` has invalid `Link` field value: result.Link must be an instance of OPDSLink class.");
} }
} }
public void processFeedStart() { public void processFeedStart() {
myData.ResumeURI = myBaseURL;
} }
private static String filter(String value) { private static String filter(String value) {
@ -64,6 +70,13 @@ class NetworkOPDSFeedReader implements OPDSFeedReader {
public void processFeedMetadata(OPDSFeedMetadata feed, boolean beforeEntries) { public void processFeedMetadata(OPDSFeedMetadata feed, boolean beforeEntries) {
if (beforeEntries) { if (beforeEntries) {
myIndex = feed.OpensearchStartIndex - 1; myIndex = feed.OpensearchStartIndex - 1;
if (feed.OpensearchItemsPerPage > 0) {
myItemsToLoad = feed.OpensearchItemsPerPage;
final int len = feed.OpensearchTotalResults - myIndex;
if (len > 0 && len < myItemsToLoad) {
myItemsToLoad = len;
}
}
return; return;
} }
final OPDSLink opdsLink = (OPDSLink) myData.Link; final OPDSLink opdsLink = (OPDSLink) myData.Link;
@ -72,12 +85,20 @@ class NetworkOPDSFeedReader implements OPDSFeedReader {
String type = link.getType(); String type = link.getType();
String rel = opdsLink.relation(filter(link.getRel()), type); String rel = opdsLink.relation(filter(link.getRel()), type);
if (type == OPDSConstants.MIME_APP_ATOM && rel == "next") { if (type == OPDSConstants.MIME_APP_ATOM && rel == "next") {
myData.ResumeURI = href; myNextURL = href;
} }
} }
} }
public void processFeedEnd() { public void processFeedEnd() {
if (mySkipUntilTitle != null) {
// Last loaded element was not found => resume error => DO NOT RESUME
// TODO: notify user about error???
// TODO: do reload???
myNextURL = null;
}
myData.ResumeURI = myNextURL;
myData.LastLoadedTitle = null;
} }
@ -110,10 +131,38 @@ class NetworkOPDSFeedReader implements OPDSFeedReader {
} }
} }
private boolean tryInterrupt() {
if (myData.Listener.requestInterrupt()) {
myData.Interrupt = true;
final int noninterruptableRemainder = 10;
if (myItemsToLoad < 0 || myItemsToLoad > noninterruptableRemainder) {
if (myData.Listener.confirmInterrupt()) {
return true;
} else {
myData.Interrupt = false;
}
}
}
return false;
}
public boolean processFeedEntry(OPDSEntry entry) { public boolean processFeedEntry(OPDSEntry entry) {
if (myItemsToLoad >= 0) {
--myItemsToLoad;
}
if (mySkipUntilTitle != null) {
if (mySkipUntilTitle.equals(entry.Title)) {
mySkipUntilTitle = null;
}
return tryInterrupt();
}
myData.LastLoadedTitle = entry.Title;
final OPDSLink opdsLink = (OPDSLink) myData.Link; final OPDSLink opdsLink = (OPDSLink) myData.Link;
if (opdsLink.getCondition(entry.Id.Uri) == OPDSLink.FeedCondition.NEVER) { if (opdsLink.getCondition(entry.Id.Uri) == OPDSLink.FeedCondition.NEVER) {
return false; return tryInterrupt();
} }
boolean hasBookLink = false; boolean hasBookLink = false;
for (ATOMLink link: entry.Links) { for (ATOMLink link: entry.Links) {
@ -137,11 +186,9 @@ class NetworkOPDSFeedReader implements OPDSFeedReader {
item = readCatalogItem(entry); item = readCatalogItem(entry);
} }
if (item != null) { if (item != null) {
//item.dbgEntry = entry; myData.Listener.onNewItem(item);
//myData.Items.add(item);
return myData.Listener.onNewItem(item);
} }
return false; return tryInterrupt();
} }
private static final String AuthorPrefix = "author:"; private static final String AuthorPrefix = "author:";

View file

@ -29,6 +29,23 @@ import org.geometerplus.fbreader.network.*;
class OPDSCatalogItem extends NetworkCatalogItem { class OPDSCatalogItem extends NetworkCatalogItem {
static class State extends NetworkOperationData {
public String LastLoadedTitle;
public boolean Interrupt;
public State(INetworkLink link, OnNewItemListener listener) {
super(link, listener);
}
@Override
public void clear() {
super.clear();
Interrupt = false;
}
}
private State myLoadingState;
OPDSCatalogItem(INetworkLink link, String title, String summary, String cover, Map<Integer, String> urlByType) { OPDSCatalogItem(INetworkLink link, String title, String summary, String cover, Map<Integer, String> urlByType) {
super(link, title, summary, cover, urlByType); super(link, title, summary, cover, urlByType);
} }
@ -41,21 +58,54 @@ class OPDSCatalogItem extends NetworkCatalogItem {
super(link, title, summary, cover, urlByType, visibility, catalogType); super(link, title, summary, cover, urlByType, visibility, catalogType);
} }
@Override private String doLoadChildren(NetworkOperationData.OnNewItemListener listener,
public String loadChildren(NetworkOperationData.OnNewItemListener listener) { ZLNetworkRequest networkRequest) {
final NetworkOperationData data = new NetworkOperationData(Link, listener);
ZLNetworkRequest networkRequest =
((OPDSLink) Link).createNetworkData(URLByType.get(URL_CATALOG), data);
while (networkRequest != null) { while (networkRequest != null) {
final String errorMessage = ZLNetworkManager.Instance().perform(networkRequest); final String errorMessage = ZLNetworkManager.Instance().perform(networkRequest);
if (errorMessage != null) { if (errorMessage != null) {
myLoadingState = null;
return errorMessage; return errorMessage;
} }
networkRequest = data.resume(); if (myLoadingState.Interrupt) {
if (myLoadingState.LastLoadedTitle == null) {
// In this case Catalog loading was not interrupted, and
// we must confirm interruption here.
if (listener.confirmInterrupt()) {
return null;
}
} else {
return null;
}
}
networkRequest = myLoadingState.resume();
} }
return null; return null;
} }
@Override
public final String loadChildren(NetworkOperationData.OnNewItemListener listener) {
OPDSLink opdsLink = (OPDSLink) Link;
myLoadingState = opdsLink.createOperationData(Link, listener);
ZLNetworkRequest networkRequest =
opdsLink.createNetworkData(URLByType.get(URL_CATALOG), myLoadingState);
return doLoadChildren(listener, networkRequest);
}
@Override
public final boolean supportsResumeLoading() {
return true;
}
@Override
public final String resumeLoading(NetworkOperationData.OnNewItemListener listener) {
if (myLoadingState == null) {
return null;
}
myLoadingState.Listener = listener;
ZLNetworkRequest networkRequest = myLoadingState.resume();
return doLoadChildren(listener, networkRequest);
}
} }

View file

@ -77,7 +77,7 @@ class OPDSLink extends AbstractNetworkLink {
myAuthenticationManager = mgr; myAuthenticationManager = mgr;
} }
ZLNetworkRequest createNetworkData(String url, final NetworkOperationData result) { ZLNetworkRequest createNetworkData(String url, final OPDSCatalogItem.State result) {
if (url == null) { if (url == null) {
return null; return null;
} }
@ -97,20 +97,24 @@ class OPDSLink extends AbstractNetworkLink {
return getLink(URL_SEARCH).replace("%s", query); return getLink(URL_SEARCH).replace("%s", query);
} }
@Override
public OPDSCatalogItem.State createOperationData(INetworkLink link,
NetworkOperationData.OnNewItemListener listener) {
return new OPDSCatalogItem.State(link, listener);
}
public ZLNetworkRequest simpleSearchRequest(String pattern, NetworkOperationData data) { public ZLNetworkRequest simpleSearchRequest(String pattern, NetworkOperationData data) {
if (getLink(URL_SEARCH) == null) { if (getLink(URL_SEARCH) == null) {
return null; return null;
} }
return createNetworkData( return createNetworkData(
searchURL(ZLNetworkUtil.htmlEncode(pattern)), searchURL(ZLNetworkUtil.htmlEncode(pattern)),
data (OPDSCatalogItem.State) data
); );
} }
public ZLNetworkRequest resume(NetworkOperationData data) { public ZLNetworkRequest resume(NetworkOperationData data) {
String url = data.ResumeURI; return createNetworkData(data.ResumeURI, (OPDSCatalogItem.State) data);
data.clear();
return createNetworkData(url, data);
} }
public NetworkLibraryItem libraryItem() { public NetworkLibraryItem libraryItem() {

View file

@ -173,6 +173,7 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
private final StringBuffer myBuffer = new StringBuffer(); private final StringBuffer myBuffer = new StringBuffer();
private HtmlToString myHtmlToString = new HtmlToString(); private HtmlToString myHtmlToString = new HtmlToString();
private boolean myFeedMetadataProcessed;
@Override @Override
public boolean startElementHandler(String tag, ZLStringMap attributes) { public boolean startElementHandler(String tag, ZLStringMap attributes) {
@ -202,6 +203,7 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
myFeed = new OPDSFeedMetadata(); myFeed = new OPDSFeedMetadata();
myFeed.readAttributes(attributes); myFeed.readAttributes(attributes);
myState = FEED; myState = FEED;
myFeedMetadataProcessed = false;
} }
break; break;
case FEED: case FEED:
@ -236,8 +238,9 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
myEntry.readAttributes(attributes); myEntry.readAttributes(attributes);
myState = F_ENTRY; myState = F_ENTRY;
// Process feed metadata just before first feed entry // Process feed metadata just before first feed entry
if (myFeed != null && myFeed.Id != null) { if (myFeed != null && myFeed.Id != null && !myFeedMetadataProcessed) {
myFeedReader.processFeedMetadata(myFeed, true); myFeedReader.processFeedMetadata(myFeed, true);
myFeedMetadataProcessed = true;
} }
} }
} else if (tagPrefix == myOpenSearchNamespaceId) { } else if (tagPrefix == myOpenSearchNamespaceId) {