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:
parent
0566a317cc
commit
d0230f72cb
14 changed files with 219 additions and 68 deletions
|
@ -8,7 +8,7 @@ DONE сделать SeekBar (как в ветке alex) в пункте Navigate
|
||||||
** синхронизация ресурсов (последняя - 1604)
|
** синхронизация ресурсов (последняя - 1604)
|
||||||
** Записать изменения в ChangeLog
|
** Записать изменения в ChangeLog
|
||||||
|
|
||||||
* каталог должен иметь не 2 состояния (загружен/не загружен), а загружен полностью/не полностью/не загружен:
|
DONE каталог должен иметь не 2 состояния (загружен/не загружен), а загружен полностью/не полностью/не загружен:
|
||||||
в случае загрузки "не полностью" при повторном открытии продолжать загрузку с точки, где прервались (для тех каталогов, что поделены на куски по 20 книг)
|
в случае загрузки "не полностью" при повторном открытии продолжать загрузку с точки, где прервались (для тех каталогов, что поделены на куски по 20 книг)
|
||||||
* 3 иконки в верхнем-правом углу для книжек
|
* 3 иконки в верхнем-правом углу для книжек
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:";
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue