mirror of
https://github.com/geometer/FBReaderJ.git
synced 2025-10-05 10:49:24 +02:00
OPDS catalogs cache
git-svn-id: https://only.mawhrin.net/repos/FBReaderJ/trunk@1675 6a642e6f-84f6-412e-ac94-c4a38d5a04b0
This commit is contained in:
parent
d029660d2e
commit
105b518a46
22 changed files with 370 additions and 59 deletions
|
@ -89,5 +89,6 @@
|
|||
<service android:name="org.geometerplus.android.fbreader.network.ItemsLoadingService" android:process=":networkLibrary" />
|
||||
<activity android:name="org.geometerplus.android.fbreader.network.NetworkBookInfoActivity" android:process=":networkLibrary" android:configChanges="orientation|keyboardHidden" />
|
||||
<receiver android:name="org.geometerplus.android.fbreader.network.BookDownloaderCallback" android:process=":networkLibrary" />
|
||||
<service android:name="org.geometerplus.android.fbreader.network.LibraryInitializationService" android:process=":networkLibrary" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
@ -89,5 +89,6 @@
|
|||
<service android:name="org.geometerplus.android.fbreader.network.ItemsLoadingService" android:process=":networkLibrary" />
|
||||
<activity android:name="org.geometerplus.android.fbreader.network.NetworkBookInfoActivity" android:process=":networkLibrary" android:configChanges="orientation|keyboardHidden" />
|
||||
<receiver android:name="org.geometerplus.android.fbreader.network.BookDownloaderCallback" android:process=":networkLibrary" />
|
||||
<service android:name="org.geometerplus.android.fbreader.network.LibraryInitializationService" android:process=":networkLibrary" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
@ -15,7 +15,8 @@ NP: оповещение об изменениях в namespace'ах проис
|
|||
** Загрузка не из xml, а из OPDS-каталога с расширениями
|
||||
DONE сделать DTD для xml-ей, как в OPDS, вместо существующего
|
||||
DONE перенести xml на сервер
|
||||
** кэширование каталога
|
||||
DONE кэширование каталога
|
||||
** реализовать обновление библиотеки так, чтобы не терялись загруженные элементы дерева
|
||||
** сделать чтение информации о поиске напрямую из каталога
|
||||
|
||||
** Использовать default e-mail при регистрации новых пользователей в AuthenticationDialog (возможно только в Android 2.0+)
|
||||
|
|
|
@ -296,6 +296,7 @@
|
|||
<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 name="cacheDirectoryError" toBeTranslated="true" value="Unable to create cache directory" />
|
||||
</node>
|
||||
<node name="emptyCatalogBox">
|
||||
<node name="title" value="Information" />
|
||||
|
|
|
@ -295,6 +295,7 @@
|
|||
<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 name="cacheDirectoryError" value="Unable to create cache directory" />
|
||||
</node>
|
||||
<node name="emptyCatalogBox">
|
||||
<node name="title" value="Information" />
|
||||
|
|
|
@ -296,6 +296,7 @@
|
|||
<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 name="cacheDirectoryError" toBeTranslated="true" value="Unable to create cache directory" />
|
||||
</node>
|
||||
<node name="emptyCatalogBox">
|
||||
<node name="title" value="Information"/>
|
||||
|
|
|
@ -296,6 +296,7 @@
|
|||
<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 name="cacheDirectoryError" toBeTranslated="true" value="Unable to create cache directory" />
|
||||
</node>
|
||||
<node name="emptyCatalogBox">
|
||||
<node name="title" value="Információ" />
|
||||
|
|
|
@ -296,6 +296,7 @@
|
|||
<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 name="cacheDirectoryError" toBeTranslated="true" value="Unable to create cache directory" />
|
||||
</node>
|
||||
<node name="emptyCatalogBox">
|
||||
<node name="title" value="Informazioni" />
|
||||
|
|
|
@ -295,6 +295,7 @@
|
|||
<node name="unsupportedOperation" value="Неподдерживаемая операция" />
|
||||
<node name="notAnOPDS" value="Это не OPDS каталог" />
|
||||
<node name="noRequiredInformation" value="Требуемая информация не найдена в каталоге" />
|
||||
<node name="cacheDirectoryError" value="Не удается создать каталог для кэша" />
|
||||
</node>
|
||||
<node name="emptyCatalogBox">
|
||||
<node name="title" value="Информация" />
|
||||
|
|
|
@ -296,6 +296,7 @@
|
|||
<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 name="cacheDirectoryError" toBeTranslated="true" value="Unable to create cache directory" />
|
||||
</node>
|
||||
<node name="emptyCatalogBox">
|
||||
<node name="title" value="Thông tin" />
|
||||
|
|
|
@ -296,6 +296,7 @@
|
|||
<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 name="cacheDirectoryError" toBeTranslated="true" value="Unable to create cache directory" />
|
||||
</node>
|
||||
<node name="emptyCatalogBox">
|
||||
<node name="title" value="信息" />
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.android.fbreader.network;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
|
||||
|
||||
public class LibraryInitializationService extends Service {
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(Intent intent, int startId) {
|
||||
super.onStart(intent, startId);
|
||||
|
||||
final NetworkView view = NetworkView.Instance();
|
||||
if (!view.isInitialized()) {
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
|
||||
final Handler handler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if (msg.what > 0) {
|
||||
view.finishBackgroundUpdate();
|
||||
}
|
||||
stopSelf();
|
||||
}
|
||||
};
|
||||
|
||||
final Thread thread = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
boolean result = false;
|
||||
try {
|
||||
result = view.runBackgroundUpdate();
|
||||
} finally {
|
||||
handler.sendEmptyMessage(result ? 1 : 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.setPriority(Thread.MIN_PRIORITY);
|
||||
thread.start();
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ package org.geometerplus.android.fbreader.network;
|
|||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
@ -59,10 +60,6 @@ public class NetworkLibraryActivity extends NetworkBaseActivity {
|
|||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
tryResume();
|
||||
}
|
||||
|
||||
private void tryResume() {
|
||||
if (!NetworkView.Instance().isInitialized()) {
|
||||
new Initializator().start();
|
||||
} else {
|
||||
|
@ -114,6 +111,7 @@ public class NetworkLibraryActivity extends NetworkBaseActivity {
|
|||
if (message.what == 0) {
|
||||
runInitialization(); // run initialization process
|
||||
} else if (message.obj == null) {
|
||||
startService(new Intent(getApplicationContext(), LibraryInitializationService.class));
|
||||
prepareView(); // initialization is complete successfully
|
||||
} else {
|
||||
processResults((String) message.obj); // handle initialization error
|
||||
|
|
|
@ -79,6 +79,19 @@ class NetworkView {
|
|||
return null;
|
||||
}
|
||||
|
||||
// This method must be called from background thread
|
||||
public boolean runBackgroundUpdate() {
|
||||
return NetworkLibrary.Instance().runBackgroundUpdate();
|
||||
}
|
||||
|
||||
// This method MUST be called from main thread
|
||||
// This method can be called only after runBackgroundUpdate method has returned true
|
||||
public void finishBackgroundUpdate() {
|
||||
NetworkLibrary library = NetworkLibrary.Instance();
|
||||
library.finishBackgroundUpdate();
|
||||
library.synchronize();
|
||||
fireModelChangedInternal();
|
||||
}
|
||||
|
||||
/*
|
||||
* NetworkLibraryItem's actions
|
||||
|
|
|
@ -29,4 +29,8 @@ public abstract class Paths {
|
|||
public static String cacheDirectory() {
|
||||
return BooksDirectoryOption.getValue() + "/.FBReader";
|
||||
}
|
||||
|
||||
public static String networkCacheDirectory() {
|
||||
return cacheDirectory() + "/cache";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ import java.io.*;
|
|||
|
||||
import org.geometerplus.zlibrary.core.image.ZLBase64EncodedImage;
|
||||
|
||||
import org.geometerplus.fbreader.Paths;
|
||||
|
||||
final class Base64EncodedImage extends ZLBase64EncodedImage {
|
||||
|
||||
private static final String ENCODED_SUFFIX = ".base64";
|
||||
|
@ -36,7 +38,7 @@ final class Base64EncodedImage extends ZLBase64EncodedImage {
|
|||
}
|
||||
|
||||
public static String makeImagesDir() {
|
||||
return NetworkImage.makeImagesDir() + File.separator + "base64";
|
||||
return Paths.networkCacheDirectory() + "/base64";
|
||||
}
|
||||
|
||||
public void setData(String data) {
|
||||
|
|
|
@ -23,7 +23,7 @@ import java.io.*;
|
|||
import java.net.*;
|
||||
|
||||
import org.geometerplus.zlibrary.core.image.ZLSingleImage;
|
||||
import org.geometerplus.zlibrary.core.network.*;
|
||||
import org.geometerplus.zlibrary.core.network.ZLNetworkManager;
|
||||
|
||||
import org.geometerplus.fbreader.Paths;
|
||||
|
||||
|
@ -40,15 +40,11 @@ public final class NetworkImage extends ZLSingleImage {
|
|||
public NetworkImage(String url, String mimeType) {
|
||||
super(mimeType);
|
||||
Url = url;
|
||||
new File(makeImagesDir()).mkdirs();
|
||||
new File(Paths.networkCacheDirectory()).mkdirs();
|
||||
}
|
||||
|
||||
private static final String TOESCAPE = "<>:\"|?*\\";
|
||||
|
||||
public static String makeImagesDir() {
|
||||
return Paths.cacheDirectory() + File.separator + "cache";
|
||||
}
|
||||
|
||||
// mimeType string MUST be interned
|
||||
public static String makeImageFileName(String url, String mimeType) {
|
||||
URI uri;
|
||||
|
@ -65,7 +61,7 @@ public final class NetworkImage extends ZLSingleImage {
|
|||
path.delete(0, 4);
|
||||
}
|
||||
path.insert(0, File.separator);
|
||||
path.insert(0, makeImagesDir());
|
||||
path.insert(0, Paths.networkCacheDirectory());
|
||||
|
||||
int index = path.length();
|
||||
|
||||
|
@ -191,24 +187,7 @@ public final class NetworkImage extends ZLSingleImage {
|
|||
return;
|
||||
}
|
||||
|
||||
ZLNetworkManager.Instance().perform(new ZLNetworkRequest(Url) {
|
||||
public String handleStream(URLConnection connection, InputStream inputStream) throws IOException {
|
||||
OutputStream outStream = new FileOutputStream(imageFile);
|
||||
try {
|
||||
final byte[] buffer = new byte[8192];
|
||||
while (true) {
|
||||
final int size = inputStream.read(buffer);
|
||||
if (size <= 0) {
|
||||
break;
|
||||
}
|
||||
outStream.write(buffer, 0, size);
|
||||
}
|
||||
} finally {
|
||||
outStream.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
ZLNetworkManager.Instance().downloadToFile(Url, imageFile);
|
||||
} finally {
|
||||
mySynchronized = true;
|
||||
}
|
||||
|
|
|
@ -232,15 +232,11 @@ public class NetworkLibrary {
|
|||
public String initialize() {
|
||||
final LinksComparator comparator = new LinksComparator();
|
||||
|
||||
final String url = "http://data.fbreader.org/catalogs/generic-1.0.xml";
|
||||
|
||||
final String error = ZLNetworkManager.Instance().perform(
|
||||
OPDSLinkReader.loadOPDSLinksRequest(url, new OnNewLinkListener() {
|
||||
final String error = OPDSLinkReader.loadOPDSLinks(false, new OnNewLinkListener() {
|
||||
public void onNewLink(INetworkLink link) {
|
||||
addLinkInternal(myLoadedLinks, link, comparator);
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
if (error != null) {
|
||||
synchronized (myLinks) {
|
||||
|
@ -262,9 +258,84 @@ public class NetworkLibrary {
|
|||
}
|
||||
);
|
||||
|
||||
/*testDate(new ATOMUpdated(2010, 1, 1, 1, 0, 0, 0, 2, 0),
|
||||
new ATOMUpdated(2009, 12, 31, 23, 0, 0, 0, 0, 0));
|
||||
testDate(new ATOMUpdated(2010, 12, 31, 23, 40, 0, 0, -1, -30),
|
||||
new ATOMUpdated(2011, 1, 1, 1, 10, 0, 0, 0, 0));
|
||||
testDate(new ATOMUpdated(2010, 1, 31, 23, 40, 0, 0, -1, -30),
|
||||
new ATOMUpdated(2010, 2, 1, 1, 10, 0, 0, 0, 0));
|
||||
testDate(new ATOMUpdated(2010, 2, 28, 23, 40, 0, 0, -1, -30),
|
||||
new ATOMUpdated(2010, 3, 1, 1, 10, 0, 0, 0, 0));
|
||||
testDate(new ATOMUpdated(2012, 2, 28, 23, 40, 0, 0, -1, -30),
|
||||
new ATOMUpdated(2012, 2, 29, 1, 10, 0, 0, 0, 0));
|
||||
testDate(new ATOMUpdated(2012, 2, 15, 23, 40, 0, 0, -1, -30),
|
||||
new ATOMUpdated(2012, 2, 16, 1, 10, 0, 0, 0, 0));
|
||||
testDate(new ATOMUpdated(2012, 2, 15, 23, 40, 1, 0, 3, 30),
|
||||
new ATOMUpdated(2012, 2, 15, 23, 40, 0, 0, 3, 30));
|
||||
testDate(new ATOMUpdated(2012, 2, 15, 23, 40, 0, 0, 3, 30),
|
||||
new ATOMUpdated(2012, 2, 15, 23, 40, 1, 0, 3, 30));
|
||||
testDate(new ATOMUpdated(2012, 2, 15, 23, 40, 0, 0.001f, 3, 30),
|
||||
new ATOMUpdated(2012, 2, 15, 23, 40, 0, 0, 3, 30));*/
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*private void testDate(ATOMDateConstruct date1, ATOMDateConstruct date2) {
|
||||
String sign = " == ";
|
||||
final int diff = date1.compareTo(date2);
|
||||
if (diff > 0) {
|
||||
sign = " > ";
|
||||
} else if (diff < 0) {
|
||||
sign = " < ";
|
||||
}
|
||||
Log.w("FBREADER", "" + date1 + sign + date2);
|
||||
}*/
|
||||
|
||||
private ArrayList<INetworkLink> myBackgroundLinks;
|
||||
private Object myBackgroundLock = new Object();
|
||||
|
||||
// This method must be called from background thread
|
||||
public boolean runBackgroundUpdate() {
|
||||
synchronized (myBackgroundLock) {
|
||||
myBackgroundLinks = new ArrayList<INetworkLink>();
|
||||
|
||||
final String error = OPDSLinkReader.loadOPDSLinks(true, new OnNewLinkListener() {
|
||||
public void onNewLink(INetworkLink link) {
|
||||
myBackgroundLinks.add(link);
|
||||
}
|
||||
});
|
||||
|
||||
if (error != null || myBackgroundLinks.isEmpty()) {
|
||||
myBackgroundLinks = null;
|
||||
}
|
||||
|
||||
if (myBackgroundLinks == null) {
|
||||
return false;
|
||||
} else {
|
||||
Collections.sort(myBackgroundLinks, new LinksComparator());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This method MUST be called from main thread
|
||||
// This method can be called only after runBackgroundUpdate method has returned true
|
||||
//
|
||||
// synchronize() method MUST be called after this method
|
||||
public void finishBackgroundUpdate() {
|
||||
synchronized (myBackgroundLock) {
|
||||
if (myBackgroundLinks == null) {
|
||||
throw new RuntimeException("Invalid state: that's impossible!!!");
|
||||
}
|
||||
synchronized (myLinks) {
|
||||
myLoadedLinks.clear();
|
||||
myLoadedLinks.addAll(myBackgroundLinks);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String rewriteUrl(String url, boolean externalUrl) {
|
||||
final String host = ZLNetworkUtil.hostFromUrl(url).toLowerCase();
|
||||
synchronized (myLinks) {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package org.geometerplus.fbreader.network.atom;
|
||||
|
||||
public abstract class ATOMDateConstruct extends ATOMCommonAttributes {
|
||||
public abstract class ATOMDateConstruct extends ATOMCommonAttributes implements Comparable<ATOMDateConstruct> {
|
||||
|
||||
public int Year;
|
||||
public int Month;
|
||||
|
@ -278,5 +278,51 @@ public abstract class ATOMDateConstruct extends ATOMCommonAttributes {
|
|||
public String toString() {
|
||||
return getDateTime(false);
|
||||
}
|
||||
|
||||
private static final int[] DAYS_IN_MONTHS = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
private int daysInMonth(int month, int year) {
|
||||
--month;
|
||||
while (month > 11) month -= 12;
|
||||
while (month < 0) month += 12;
|
||||
if (month == 1 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)) {
|
||||
return DAYS_IN_MONTHS[1] + 1;
|
||||
}
|
||||
return DAYS_IN_MONTHS[month];
|
||||
}
|
||||
|
||||
public int compareTo(ATOMDateConstruct date) {
|
||||
int dateYear = date.Year;
|
||||
int dateMonth = date.Month;
|
||||
int dateDay = date.Day;
|
||||
int dateHour = date.Hour;
|
||||
int dateMinutes = date.Minutes;
|
||||
if (TZHour != date.TZHour || TZMinutes != date.TZMinutes) {
|
||||
dateMinutes += TZMinutes - date.TZMinutes;
|
||||
while (dateMinutes < 0) { dateMinutes += 60; --dateHour; }
|
||||
while (dateMinutes > 59) { dateMinutes -= 60; ++dateHour; }
|
||||
dateHour += TZHour - date.TZHour;
|
||||
while (dateHour < 0) { dateHour += 24; --dateDay; }
|
||||
while (dateHour > 23) { dateHour -= 24; ++dateDay; }
|
||||
while (dateDay < 1) dateDay += daysInMonth(--dateMonth, dateYear);
|
||||
while (dateDay > daysInMonth(dateMonth, dateYear)) dateDay -= daysInMonth(dateMonth++, dateYear);
|
||||
while (dateMonth < 1) { dateMonth += 12; --dateYear; }
|
||||
while (dateMonth > 12) { dateMonth -= 12; ++dateYear; }
|
||||
}
|
||||
if (Year != dateYear) return Year - dateYear;
|
||||
if (Month != dateMonth) return Month - dateMonth;
|
||||
if (Day != dateDay) return Day - dateDay;
|
||||
if (Hour != dateHour) return Hour - dateHour;
|
||||
if (Minutes != dateMinutes) return Minutes - dateMinutes;
|
||||
if (Seconds != date.Seconds) return Seconds - date.Seconds;
|
||||
return Math.round(100 * SecondFraction) - Math.round(100 * date.SecondFraction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof ATOMDateConstruct)) {
|
||||
return false;
|
||||
}
|
||||
return compareTo((ATOMDateConstruct) obj) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -19,18 +19,23 @@
|
|||
|
||||
package org.geometerplus.fbreader.network.opds;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URLConnection;
|
||||
import java.util.*;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.geometerplus.zlibrary.core.network.ZLNetworkRequest;
|
||||
import org.geometerplus.zlibrary.core.network.ZLNetworkManager;
|
||||
|
||||
import org.geometerplus.fbreader.Paths;
|
||||
import org.geometerplus.fbreader.network.*;
|
||||
import org.geometerplus.fbreader.network.atom.ATOMUpdated;
|
||||
|
||||
|
||||
public class OPDSLinkReader {
|
||||
|
||||
static final String CATALOGS_URL = "http://data.fbreader.org/catalogs/generic-1.0.xml";
|
||||
|
||||
public static ICustomNetworkLink createCustomLink(int id, String siteName, String title, String summary, String icon, Map<String, String> links) {
|
||||
if (siteName == null || title == null || links.get(INetworkLink.URL_MAIN) == null) {
|
||||
return null;
|
||||
|
@ -44,13 +49,71 @@ public class OPDSLinkReader {
|
|||
return new OPDSCustomLink(ICustomNetworkLink.INVALID_ID, siteName, null, null, null, links);
|
||||
}
|
||||
|
||||
public static ZLNetworkRequest loadOPDSLinksRequest(String url, final NetworkLibrary.OnNewLinkListener listener) {
|
||||
return new ZLNetworkRequest(url) {
|
||||
@Override
|
||||
public String handleStream(URLConnection connection, InputStream inputStream) throws IOException {
|
||||
new OPDSLinkXMLReader(listener).read(inputStream);
|
||||
|
||||
public static String loadOPDSLinks(boolean updateNotLoad, final NetworkLibrary.OnNewLinkListener listener) {
|
||||
final File dirFile = new File(Paths.networkCacheDirectory());
|
||||
if (!dirFile.exists() && !dirFile.mkdirs()) {
|
||||
return NetworkErrors.errorMessage("cacheDirectoryError");
|
||||
}
|
||||
|
||||
final String fileName = "fbreader_catalogs-"
|
||||
+ CATALOGS_URL.substring(CATALOGS_URL.lastIndexOf(File.separator) + 1);
|
||||
|
||||
boolean goodCache = false;
|
||||
File oldCache = null;
|
||||
ATOMUpdated cacheUpdatedTime = null;
|
||||
final File catalogsFile = new File(dirFile, fileName);
|
||||
if (catalogsFile.exists()) {
|
||||
if (updateNotLoad) {
|
||||
try {
|
||||
final OPDSLinkXMLReader reader = new OPDSLinkXMLReader();
|
||||
reader.read(new FileInputStream(catalogsFile));
|
||||
cacheUpdatedTime = reader.getUpdatedTime();
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException("That's impossible!!!", e);
|
||||
}
|
||||
|
||||
final long diff = System.currentTimeMillis() - catalogsFile.lastModified();
|
||||
final long valid = 7 * 24 * 60 * 60 * 1000; // one week in milliseconds; FIXME: hardcoded const
|
||||
if (diff >= 0 && diff <= valid) {
|
||||
goodCache = true;
|
||||
} else {
|
||||
oldCache = new File(dirFile, "_" + fileName);
|
||||
oldCache.delete();
|
||||
if (!catalogsFile.renameTo(oldCache)) {
|
||||
catalogsFile.delete();
|
||||
oldCache = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
goodCache = true;
|
||||
}
|
||||
}
|
||||
|
||||
String error = null;
|
||||
if (!goodCache) {
|
||||
error = ZLNetworkManager.Instance().downloadToFile(CATALOGS_URL, catalogsFile);
|
||||
}
|
||||
|
||||
if (error != null) {
|
||||
if (oldCache == null) {
|
||||
return error;
|
||||
}
|
||||
catalogsFile.delete();
|
||||
if (!oldCache.renameTo(catalogsFile)) {
|
||||
oldCache.delete();
|
||||
return error;
|
||||
}
|
||||
} else if (oldCache != null) {
|
||||
oldCache.delete();
|
||||
oldCache = null;
|
||||
}
|
||||
|
||||
try {
|
||||
new OPDSLinkXMLReader(listener, cacheUpdatedTime).read(new FileInputStream(catalogsFile));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException("That's impossible!!!", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.geometerplus.fbreader.network.INetworkLink;
|
|||
import org.geometerplus.fbreader.network.NetworkImage;
|
||||
import org.geometerplus.fbreader.network.NetworkLibrary;
|
||||
import org.geometerplus.fbreader.network.atom.ATOMLink;
|
||||
import org.geometerplus.fbreader.network.atom.ATOMUpdated;
|
||||
import org.geometerplus.fbreader.network.authentication.NetworkAuthenticationManager;
|
||||
import org.geometerplus.fbreader.network.authentication.litres.LitResAuthenticationManager;
|
||||
|
||||
|
@ -45,8 +46,12 @@ public class OPDSLinkXMLReader extends OPDSXMLReader {
|
|||
private final LinkedList<URLRewritingRule> myUrlRewritingRules = new LinkedList<URLRewritingRule>();
|
||||
private HashMap<RelationAlias, String> myRelationAliases = new HashMap<RelationAlias, String>();
|
||||
|
||||
public LinkReader(NetworkLibrary.OnNewLinkListener listener) {
|
||||
private ATOMUpdated myUpdatedTime;
|
||||
private ATOMUpdated myReadAfterTime;
|
||||
|
||||
public LinkReader(NetworkLibrary.OnNewLinkListener listener, ATOMUpdated readAfter) {
|
||||
myListener = listener;
|
||||
myReadAfterTime = readAfter;
|
||||
}
|
||||
|
||||
public void setAuthenticationType(String type) {
|
||||
|
@ -72,6 +77,10 @@ public class OPDSLinkXMLReader extends OPDSXMLReader {
|
|||
myRelationAliases.clear();
|
||||
}
|
||||
|
||||
public ATOMUpdated getUpdatedTime() {
|
||||
return myUpdatedTime;
|
||||
}
|
||||
|
||||
private static final String ENTRY_ID_PREFIX = "urn:fbreader-org-catalog:";
|
||||
|
||||
public boolean processFeedEntry(OPDSEntry entry) {
|
||||
|
@ -185,13 +194,33 @@ public class OPDSLinkXMLReader extends OPDSXMLReader {
|
|||
return opdsLink;
|
||||
}
|
||||
|
||||
public boolean processFeedMetadata(OPDSFeedMetadata feed, boolean beforeEntries) { return false; }
|
||||
public void processFeedStart() {}
|
||||
public void processFeedEnd() {}
|
||||
public boolean processFeedMetadata(OPDSFeedMetadata feed, boolean beforeEntries) {
|
||||
myUpdatedTime = feed.Updated;
|
||||
if (myUpdatedTime != null && myReadAfterTime != null
|
||||
&& myUpdatedTime.compareTo(myReadAfterTime) <= 0) {
|
||||
return true;
|
||||
}
|
||||
return myListener == null; // no listener -- no need to proceed
|
||||
}
|
||||
|
||||
public OPDSLinkXMLReader(NetworkLibrary.OnNewLinkListener listener) {
|
||||
super(new LinkReader(listener));
|
||||
public void processFeedStart() {
|
||||
myUpdatedTime = null;
|
||||
}
|
||||
|
||||
public void processFeedEnd() {
|
||||
}
|
||||
}
|
||||
|
||||
public OPDSLinkXMLReader() {
|
||||
super(new LinkReader(null, null));
|
||||
}
|
||||
|
||||
public OPDSLinkXMLReader(NetworkLibrary.OnNewLinkListener listener, ATOMUpdated readAfter) {
|
||||
super(new LinkReader(listener, readAfter));
|
||||
}
|
||||
|
||||
public ATOMUpdated getUpdatedTime() {
|
||||
return ((LinkReader) myFeedReader).getUpdatedTime();
|
||||
}
|
||||
|
||||
private String myFBReaderNamespaceId;
|
||||
|
|
|
@ -196,4 +196,30 @@ public class ZLNetworkManager {
|
|||
}
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
|
||||
public final String downloadToFile(String url, final File outFile) {
|
||||
return downloadToFile(url, outFile, 8192);
|
||||
}
|
||||
|
||||
public final String downloadToFile(String url, final File outFile, final int bufferSize) {
|
||||
return perform(new ZLNetworkRequest(url) {
|
||||
public String handleStream(URLConnection connection, InputStream inputStream) throws IOException {
|
||||
OutputStream outStream = new FileOutputStream(outFile);
|
||||
try {
|
||||
final byte[] buffer = new byte[bufferSize];
|
||||
while (true) {
|
||||
final int size = inputStream.read(buffer);
|
||||
if (size <= 0) {
|
||||
break;
|
||||
}
|
||||
outStream.write(buffer, 0, size);
|
||||
}
|
||||
} finally {
|
||||
outStream.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue