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

View file

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

View file

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

View file

@ -73,4 +73,9 @@ public abstract class AbstractNetworkLink implements INetworkLink {
public Set<String> getLinkKeys() {
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();
NetworkOperationData createOperationData(INetworkLink link,
NetworkOperationData.OnNewItemListener listener);
ZLNetworkRequest simpleSearchRequest(String pattern, 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 final String loadChildren(final List<NetworkLibraryItem> children) {
return loadChildren(new CatalogListener() {
public void onNewItem(NetworkLibraryItem item) {
children.add(item);
}
});
}*/
public boolean supportsResumeLoading() {
return false;
}
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.

View file

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

View file

@ -25,12 +25,15 @@ import org.geometerplus.zlibrary.core.network.ZLNetworkRequest;
public class NetworkOperationData {
public interface OnNewItemListener {
// return true to interrupt reading; return false to continue reading
boolean onNewItem(NetworkLibraryItem item);
void onNewItem(NetworkLibraryItem item);
// return true to confirm interrupt reading; return false to continue reading
boolean requestInterrupt();
boolean confirmInterrupt();
}
public final INetworkLink Link;
public final OnNewItemListener Listener;
public OnNewItemListener Listener;
public String ResumeURI;
private int myResumeCount;
@ -48,6 +51,8 @@ public class NetworkOperationData {
if (++myResumeCount >= 10) { // FIXME: hardcoded resume limit constant!!!
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 {
private final String myBaseURL;
private final NetworkOperationData myData;
private final OPDSCatalogItem.State myData;
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.
@ -43,15 +47,17 @@ class NetworkOPDSFeedReader implements OPDSFeedReader {
* @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.
*/
NetworkOPDSFeedReader(String baseURL, NetworkOperationData result) {
NetworkOPDSFeedReader(String baseURL, OPDSCatalogItem.State result) {
myBaseURL = baseURL;
myData = result;
mySkipUntilTitle = myData.LastLoadedTitle;
if (!(result.Link instanceof OPDSLink)) {
throw new IllegalArgumentException("Parameter `result` has invalid `Link` field value: result.Link must be an instance of OPDSLink class.");
}
}
public void processFeedStart() {
myData.ResumeURI = myBaseURL;
}
private static String filter(String value) {
@ -64,6 +70,13 @@ class NetworkOPDSFeedReader implements OPDSFeedReader {
public void processFeedMetadata(OPDSFeedMetadata feed, boolean beforeEntries) {
if (beforeEntries) {
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;
}
final OPDSLink opdsLink = (OPDSLink) myData.Link;
@ -72,12 +85,20 @@ class NetworkOPDSFeedReader implements OPDSFeedReader {
String type = link.getType();
String rel = opdsLink.relation(filter(link.getRel()), type);
if (type == OPDSConstants.MIME_APP_ATOM && rel == "next") {
myData.ResumeURI = href;
myNextURL = href;
}
}
}
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) {
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;
if (opdsLink.getCondition(entry.Id.Uri) == OPDSLink.FeedCondition.NEVER) {
return false;
return tryInterrupt();
}
boolean hasBookLink = false;
for (ATOMLink link: entry.Links) {
@ -137,11 +186,9 @@ class NetworkOPDSFeedReader implements OPDSFeedReader {
item = readCatalogItem(entry);
}
if (item != null) {
//item.dbgEntry = entry;
//myData.Items.add(item);
return myData.Listener.onNewItem(item);
myData.Listener.onNewItem(item);
}
return false;
return tryInterrupt();
}
private static final String AuthorPrefix = "author:";

View file

@ -29,6 +29,23 @@ import org.geometerplus.fbreader.network.*;
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) {
super(link, title, summary, cover, urlByType);
}
@ -41,21 +58,54 @@ class OPDSCatalogItem extends NetworkCatalogItem {
super(link, title, summary, cover, urlByType, visibility, catalogType);
}
@Override
public String loadChildren(NetworkOperationData.OnNewItemListener listener) {
final NetworkOperationData data = new NetworkOperationData(Link, listener);
ZLNetworkRequest networkRequest =
((OPDSLink) Link).createNetworkData(URLByType.get(URL_CATALOG), data);
private String doLoadChildren(NetworkOperationData.OnNewItemListener listener,
ZLNetworkRequest networkRequest) {
while (networkRequest != null) {
final String errorMessage = ZLNetworkManager.Instance().perform(networkRequest);
if (errorMessage != null) {
myLoadingState = null;
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;
}
@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;
}
ZLNetworkRequest createNetworkData(String url, final NetworkOperationData result) {
ZLNetworkRequest createNetworkData(String url, final OPDSCatalogItem.State result) {
if (url == null) {
return null;
}
@ -97,20 +97,24 @@ class OPDSLink extends AbstractNetworkLink {
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) {
if (getLink(URL_SEARCH) == null) {
return null;
}
return createNetworkData(
searchURL(ZLNetworkUtil.htmlEncode(pattern)),
data
(OPDSCatalogItem.State) data
);
}
public ZLNetworkRequest resume(NetworkOperationData data) {
String url = data.ResumeURI;
data.clear();
return createNetworkData(url, data);
return createNetworkData(data.ResumeURI, (OPDSCatalogItem.State) data);
}
public NetworkLibraryItem libraryItem() {

View file

@ -173,6 +173,7 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
private final StringBuffer myBuffer = new StringBuffer();
private HtmlToString myHtmlToString = new HtmlToString();
private boolean myFeedMetadataProcessed;
@Override
public boolean startElementHandler(String tag, ZLStringMap attributes) {
@ -202,6 +203,7 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
myFeed = new OPDSFeedMetadata();
myFeed.readAttributes(attributes);
myState = FEED;
myFeedMetadataProcessed = false;
}
break;
case FEED:
@ -236,8 +238,9 @@ class OPDSXMLReader extends ZLXMLReaderAdapter {
myEntry.readAttributes(attributes);
myState = F_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);
myFeedMetadataProcessed = true;
}
}
} else if (tagPrefix == myOpenSearchNamespaceId) {