diff --git a/src/org/geometerplus/android/fbreader/BookmarksActivity.java b/src/org/geometerplus/android/fbreader/BookmarksActivity.java index 47282a21e..d1643c918 100644 --- a/src/org/geometerplus/android/fbreader/BookmarksActivity.java +++ b/src/org/geometerplus/android/fbreader/BookmarksActivity.java @@ -46,7 +46,7 @@ public class BookmarksActivity extends TabActivity implements MenuItem.OnMenuIte private final BookCollectionShadow myCollection = new BookCollectionShadow(this); - List AllBooksBookmarks; + private List myAllBooksBookmarks; private final List myThisBookBookmarks = new LinkedList(); private final List mySearchResults = new LinkedList(); @@ -71,8 +71,6 @@ public class BookmarksActivity extends TabActivity implements MenuItem.OnMenuIte Thread.setDefaultUncaughtExceptionHandler(new org.geometerplus.zlibrary.ui.android.library.UncaughtExceptionHandler(this)); - myCollection.bindToService(); - requestWindowFeature(Window.FEATURE_NO_TITLE); setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); @@ -81,14 +79,16 @@ public class BookmarksActivity extends TabActivity implements MenuItem.OnMenuIte final TabHost host = getTabHost(); LayoutInflater.from(this).inflate(R.layout.bookmarks, host.getTabContentView(), true); + } - AllBooksBookmarks = Library.Instance().allBookmarks(); - Collections.sort(AllBooksBookmarks, new Bookmark.ByTimeComparator()); + private void init() { + myAllBooksBookmarks = new ArrayList(myCollection.allBookmarks()); + Collections.sort(myAllBooksBookmarks, new Bookmark.ByTimeComparator()); final FBReaderApp fbreader = (FBReaderApp)FBReaderApp.Instance(); if (fbreader.Model != null) { final long bookId = fbreader.Model.Book.getId(); - for (Bookmark bookmark : AllBooksBookmarks) { + for (Bookmark bookmark : myAllBooksBookmarks) { if (bookmark.getBookId() == bookId) { myThisBookBookmarks.add(bookmark); } @@ -101,7 +101,7 @@ public class BookmarksActivity extends TabActivity implements MenuItem.OnMenuIte } myAllBooksView = createTab("allBooks", R.id.all_books); - new BookmarksAdapter(myAllBooksView, AllBooksBookmarks, false); + new BookmarksAdapter(myAllBooksView, myAllBooksBookmarks, false); findViewById(R.id.search_results).setVisibility(View.GONE); } @@ -109,6 +109,15 @@ public class BookmarksActivity extends TabActivity implements MenuItem.OnMenuIte @Override protected void onStart() { super.onStart(); + + myCollection.bindToService(new Runnable() { + public void run() { + if (myAllBooksBookmarks == null) { + init(); + } + } + }); + OrientationUtil.setOrientation(this, getIntent()); } @@ -124,7 +133,7 @@ public class BookmarksActivity extends TabActivity implements MenuItem.OnMenuIte final LinkedList bookmarks = new LinkedList(); pattern = pattern.toLowerCase(); - for (Bookmark b : AllBooksBookmarks) { + for (Bookmark b : myAllBooksBookmarks) { if (ZLMiscUtil.matchesIgnoreCase(b.getText(), pattern)) { bookmarks.add(b); } @@ -138,12 +147,18 @@ public class BookmarksActivity extends TabActivity implements MenuItem.OnMenuIte @Override public void onPause() { - for (Bookmark bookmark : AllBooksBookmarks) { + for (Bookmark bookmark : myAllBooksBookmarks) { bookmark.save(); } super.onPause(); } + @Override + protected void onStop() { + myCollection.unbind(); + super.onStop(); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); @@ -212,7 +227,7 @@ public class BookmarksActivity extends TabActivity implements MenuItem.OnMenuIte case DELETE_ITEM_ID: bookmark.delete(); myThisBookBookmarks.remove(bookmark); - AllBooksBookmarks.remove(bookmark); + myAllBooksBookmarks.remove(bookmark); mySearchResults.remove(bookmark); invalidateAllViews(); return true; @@ -225,7 +240,7 @@ public class BookmarksActivity extends TabActivity implements MenuItem.OnMenuIte final Bookmark bookmark = fbreader.addBookmark(20, true); if (bookmark != null) { myThisBookBookmarks.add(0, bookmark); - AllBooksBookmarks.add(0, bookmark); + myAllBooksBookmarks.add(0, bookmark); invalidateAllViews(); } } diff --git a/src/org/geometerplus/android/fbreader/library/SQLiteBooksDatabase.java b/src/org/geometerplus/android/fbreader/library/SQLiteBooksDatabase.java index 3a8da4b66..a5b76ea3d 100644 --- a/src/org/geometerplus/android/fbreader/library/SQLiteBooksDatabase.java +++ b/src/org/geometerplus/android/fbreader/library/SQLiteBooksDatabase.java @@ -799,9 +799,9 @@ public final class SQLiteBooksDatabase extends BooksDatabase { statement.bindLong(1, bookmark.getBookId()); statement.bindString(2, bookmark.getText()); - SQLiteUtil.bindDate(statement, 3, bookmark.getTime(Bookmark.CREATION)); - SQLiteUtil.bindDate(statement, 4, bookmark.getTime(Bookmark.MODIFICATION)); - SQLiteUtil.bindDate(statement, 5, bookmark.getTime(Bookmark.ACCESS)); + SQLiteUtil.bindDate(statement, 3, bookmark.getDate(Bookmark.DateType.Creation)); + SQLiteUtil.bindDate(statement, 4, bookmark.getDate(Bookmark.DateType.Modification)); + SQLiteUtil.bindDate(statement, 5, bookmark.getDate(Bookmark.DateType.Access)); statement.bindLong(6, bookmark.getAccessCount()); SQLiteUtil.bindString(statement, 7, bookmark.ModelId); statement.bindLong(8, bookmark.ParagraphIndex); diff --git a/src/org/geometerplus/android/fbreader/libraryService/BookCollectionShadow.java b/src/org/geometerplus/android/fbreader/libraryService/BookCollectionShadow.java index 5bef031d2..61c2babc2 100644 --- a/src/org/geometerplus/android/fbreader/libraryService/BookCollectionShadow.java +++ b/src/org/geometerplus/android/fbreader/libraryService/BookCollectionShadow.java @@ -31,12 +31,14 @@ import org.geometerplus.fbreader.library.*; public class BookCollectionShadow implements IBookCollection, ServiceConnection { private final Context myContext; private volatile LibraryInterface myInterface; + private Runnable myOnBindAction; public BookCollectionShadow(Context context) { myContext = context; } - public void bindToService() { + public void bindToService(Runnable onBindAction) { + myOnBindAction = onBindAction; myContext.bindService( new Intent(myContext, LibraryService.class), this, @@ -44,6 +46,10 @@ public class BookCollectionShadow implements IBookCollection, ServiceConnection ); } + public void unbind() { + myContext.unbindService(this); + } + public synchronized Book getBookById(long id) { if (myInterface == null) { return null; @@ -69,6 +75,9 @@ public class BookCollectionShadow implements IBookCollection, ServiceConnection // method from ServiceConnection interface public synchronized void onServiceConnected(ComponentName name, IBinder service) { myInterface = LibraryInterface.Stub.asInterface(service); + if (myOnBindAction != null) { + myOnBindAction.run(); + } } // method from ServiceConnection interface diff --git a/src/org/geometerplus/fbreader/library/Bookmark.java b/src/org/geometerplus/fbreader/library/Bookmark.java index 9d764d4cc..caa5cdc6a 100644 --- a/src/org/geometerplus/fbreader/library/Bookmark.java +++ b/src/org/geometerplus/fbreader/library/Bookmark.java @@ -24,10 +24,12 @@ import java.util.*; import org.geometerplus.zlibrary.text.view.*; public final class Bookmark extends ZLTextFixedPosition { - public final static int CREATION = 0; - public final static int MODIFICATION = 1; - public final static int ACCESS = 2; - public final static int LATEST = 3; + public enum DateType { + Creation, + Modification, + Access, + Latest + } private long myId; private final long myBookId; @@ -99,16 +101,16 @@ public final class Bookmark extends ZLTextFixedPosition { return myBookTitle; } - public Date getTime(int timeStamp) { - switch (timeStamp) { - default: - case CREATION: + public Date getDate(DateType type) { + switch (type) { + case Creation: return myCreationDate; - case MODIFICATION: + case Modification: return myModificationDate; - case ACCESS: + case Access: return myAccessDate; - case LATEST: + default: + case Latest: return myLatestDate; } } @@ -148,7 +150,7 @@ public final class Bookmark extends ZLTextFixedPosition { public static class ByTimeComparator implements Comparator { public int compare(Bookmark bm0, Bookmark bm1) { - return bm1.getTime(LATEST).compareTo(bm0.getTime(LATEST)); + return bm1.getDate(DateType.Latest).compareTo(bm0.getDate(DateType.Latest)); } } diff --git a/src/org/geometerplus/fbreader/library/XMLSerializer.java b/src/org/geometerplus/fbreader/library/XMLSerializer.java index c99591f4b..65ae8be0f 100644 --- a/src/org/geometerplus/fbreader/library/XMLSerializer.java +++ b/src/org/geometerplus/fbreader/library/XMLSerializer.java @@ -19,7 +19,9 @@ package org.geometerplus.fbreader.library; -import java.util.ArrayList; +import java.util.*; +import java.text.DateFormat; +import java.text.ParseException; import org.geometerplus.zlibrary.core.constants.XMLNamespaces; import org.geometerplus.zlibrary.core.filesystem.ZLFile; @@ -30,26 +32,26 @@ class XMLSerializer extends AbstractSerializer { @Override public String serialize(Book book) { final StringBuilder buffer = new StringBuilder(); - appendTagWithAttributes( + appendTag( buffer, "entry", false, "xmlns:dc", XMLNamespaces.DublinCore, "xmlns:calibre", XMLNamespaces.CalibreMetadata ); - appendTagWithContent(buffer, "id", String.valueOf(book.getId())); + appendTagWithContent(buffer, "id", book.getId()); appendTagWithContent(buffer, "title", book.getTitle()); appendTagWithContent(buffer, "dc:language", book.getLanguage()); appendTagWithContent(buffer, "dc:encoding", book.getEncodingNoDetection()); for (Author author : book.authors()) { - buffer.append("\n"); + appendTag(buffer, "author", false); appendTagWithContent(buffer, "uri", author.SortKey); appendTagWithContent(buffer, "name", author.DisplayName); - buffer.append("\n"); + closeTag(buffer, "author"); } for (Tag tag : book.tags()) { - appendTagWithAttributes( + appendTag( buffer, "category", true, "term", tag.toString("/"), "label", tag.Name @@ -60,13 +62,13 @@ class XMLSerializer extends AbstractSerializer { if (seriesInfo != null) { appendTagWithContent(buffer, "calibre:series", seriesInfo.Title); if (seriesInfo.Index != null) { - appendTagWithContent(buffer, "calibre:series_index", String.valueOf(seriesInfo.Index)); + appendTagWithContent(buffer, "calibre:series_index", seriesInfo.Index); } } // TODO: serialize description (?) // TODO: serialize cover (?) - appendTagWithAttributes( + appendTag( buffer, "link", true, "href", book.File.getUrl(), // TODO: real book mimetype @@ -74,27 +76,81 @@ class XMLSerializer extends AbstractSerializer { "rel", "http://opds-spec.org/acquisition" ); - buffer.append("\n"); + closeTag(buffer, "entry"); return buffer.toString(); } @Override public Book deserializeBook(String xml) { - final Deserializer deserializer = new Deserializer(); + final BookDeserializer deserializer = new BookDeserializer(); deserializer.readQuietly(xml); return deserializer.getBook(); } @Override public String serialize(Bookmark bookmark) { - // TODO: implement - return null; + final StringBuilder buffer = new StringBuilder(); + appendTag( + buffer, "bookmark", false, + "id", String.valueOf(bookmark.getId()), + "visible", String.valueOf(bookmark.IsVisible) + ); + appendTag( + buffer, "book", true, + "id", String.valueOf(bookmark.getBookId()), + "title", bookmark.getBookTitle() + ); + appendTagWithContent(buffer, "text", bookmark.getText()); + appendTag( + buffer, "history", true, + "date-creation", formatDate(bookmark.getDate(Bookmark.DateType.Creation)), + "date-modification", formatDate(bookmark.getDate(Bookmark.DateType.Modification)), + "date-access", formatDate(bookmark.getDate(Bookmark.DateType.Access)), + "access-count", String.valueOf(bookmark.getAccessCount()) + ); + appendTag( + buffer, "position", true, + "model", bookmark.ModelId, + "paragraph", String.valueOf(bookmark.getParagraphIndex()), + "element", String.valueOf(bookmark.getElementIndex()), + "char", String.valueOf(bookmark.getCharIndex()) + ); + closeTag(buffer, "bookmark"); + return buffer.toString(); } @Override public Bookmark deserializeBookmark(String xml) { - // TODO: implement - return null; + final BookmarkDeserializer deserializer = new BookmarkDeserializer(); + deserializer.readQuietly(xml); + return deserializer.getBookmark(); + } + + private static DateFormat ourDateFormatter = DateFormat.getDateInstance(DateFormat.FULL, Locale.ENGLISH); + private static String formatDate(Date date) { + return date != null ? ourDateFormatter.format(date) : null; + } + private static Date parseDate(String str) throws ParseException { + return str != null ? ourDateFormatter.parse(str) : null; + } + + private static void appendTag(StringBuilder buffer, String tag, boolean close, String ... attrs) { + buffer.append('<').append(tag); + for (int i = 0; i < attrs.length - 1; i += 2) { + if (attrs[i + 1] != null) { + buffer.append(' ') + .append(escapeForXml(attrs[i])).append("=\"") + .append(escapeForXml(attrs[i + 1])).append('"'); + } + } + if (close) { + buffer.append('/'); + } + buffer.append(">\n"); + } + + private static void closeTag(StringBuilder buffer, String tag) { + buffer.append(""); } private static void appendTagWithContent(StringBuilder buffer, String tag, String content) { @@ -106,17 +162,10 @@ class XMLSerializer extends AbstractSerializer { } } - private static void appendTagWithAttributes(StringBuilder buffer, String tag, boolean close, String ... attrs) { - buffer.append('<').append(tag); - for (int i = 0; i < attrs.length - 1; i += 2) { - buffer.append(' ') - .append(escapeForXml(attrs[i])).append("=\"") - .append(escapeForXml(attrs[i + 1])).append('"'); + private static void appendTagWithContent(StringBuilder buffer, String tag, Object content) { + if (content != null) { + appendTagWithContent(buffer, tag, String.valueOf(content)); } - if (close) { - buffer.append('/'); - } - buffer.append(">\n"); } private static String escapeForXml(String data) { @@ -138,7 +187,15 @@ class XMLSerializer extends AbstractSerializer { return data; } - private static final class Deserializer extends ZLXMLReaderAdapter { + private static void clear(StringBuilder buffer) { + buffer.delete(0, buffer.length()); + } + + private static String string(StringBuilder buffer) { + return buffer.length() != 0 ? buffer.toString() : null; + } + + private static final class BookDeserializer extends ZLXMLReaderAdapter { private static enum State { READ_NOTHING, READ_ENTRY, @@ -157,15 +214,15 @@ class XMLSerializer extends AbstractSerializer { private long myId = -1; private String myUrl; - private StringBuilder myTitle = new StringBuilder(); - private StringBuilder myLanguage = new StringBuilder(); - private StringBuilder myEncoding = new StringBuilder(); - private ArrayList myAuthors = new ArrayList(); - private ArrayList myTags = new ArrayList(); - private StringBuilder myAuthorSortKey = new StringBuilder(); - private StringBuilder myAuthorName = new StringBuilder(); - private StringBuilder mySeriesTitle = new StringBuilder(); - private StringBuilder mySeriesIndex = new StringBuilder(); + private final StringBuilder myTitle = new StringBuilder(); + private final StringBuilder myLanguage = new StringBuilder(); + private final StringBuilder myEncoding = new StringBuilder(); + private final ArrayList myAuthors = new ArrayList(); + private final ArrayList myTags = new ArrayList(); + private final StringBuilder myAuthorSortKey = new StringBuilder(); + private final StringBuilder myAuthorName = new StringBuilder(); + private final StringBuilder mySeriesTitle = new StringBuilder(); + private final StringBuilder mySeriesIndex = new StringBuilder(); private Book myBook; @@ -173,14 +230,6 @@ class XMLSerializer extends AbstractSerializer { return myState == State.READ_NOTHING ? myBook : null; } - private static void clear(StringBuilder buffer) { - buffer.delete(0, buffer.length()); - } - - private static String string(StringBuilder buffer) { - return buffer.length() != 0 ? buffer.toString() : null; - } - @Override public void startDocumentHandler() { myBook = null; @@ -332,4 +381,143 @@ class XMLSerializer extends AbstractSerializer { } } } + + private static final class BookmarkDeserializer extends ZLXMLReaderAdapter { + private static enum State { + READ_NOTHING, + READ_BOOKMARK, + READ_TEXT + } + + private State myState = State.READ_NOTHING; + private Bookmark myBookmark; + + private long myId = -1; + private long myBookId; + private String myBookTitle; + private final StringBuilder myText = new StringBuilder(); + private Date myCreationDate; + private Date myModificationDate; + private Date myAccessDate; + private int myAccessCount; + private String myModelId; + private int myParagraphIndex; + private int myElementIndex; + private int myCharIndex; + private boolean myIsVisible; + + public Bookmark getBookmark() { + return myState == State.READ_NOTHING ? myBookmark : null; + } + + @Override + public void startDocumentHandler() { + myBookmark = null; + + myId = -1; + myBookId = -1; + myBookTitle = null; + clear(myText); + myCreationDate = null; + myModificationDate = null; + myAccessDate = null; + myAccessCount = 0; + myModelId = null; + myParagraphIndex = 0; + myElementIndex = 0; + myCharIndex = 0; + myIsVisible = false; + + myState = State.READ_NOTHING; + } + + @Override + public void endDocumentHandler() { + if (myId == -1 || myBookId == -1) { + return; + } + myBookmark = new Bookmark( + myId, myBookId, myBookTitle, myText.toString(), + myCreationDate, myModificationDate, myAccessDate, myAccessCount, + myModelId, myParagraphIndex, myElementIndex, myCharIndex, myIsVisible + ); + } + + //appendTagWithContent(buffer, "text", bookmark.getText()); + @Override + public boolean startElementHandler(String tag, ZLStringMap attributes) { + switch (myState) { + case READ_NOTHING: + if (!"bookmark".equals(tag)) { + return true; + } + try { + myId = Long.parseLong(attributes.getValue("id")); + myIsVisible = Boolean.parseBoolean(attributes.getValue("visible")); + myState = State.READ_BOOKMARK; + } catch (Exception e) { + return true; + } + break; + case READ_BOOKMARK: + if ("book".equals(tag)) { + try { + myBookId = Long.parseLong(attributes.getValue("id")); + myBookTitle = attributes.getValue("title"); + } catch (Exception e) { + return true; + } + } else if ("text".equals(tag)) { + myState = State.READ_TEXT; + } else if ("history".equals(tag)) { + try { + myCreationDate = parseDate(attributes.getValue("date-creation")); + myModificationDate = parseDate(attributes.getValue("date-modification")); + myAccessDate = parseDate(attributes.getValue("date-access")); + myAccessCount = Integer.parseInt(attributes.getValue("access-count")); + } catch (Exception e) { + return true; + } + } else if ("position".equals(tag)) { + try { + myModelId = attributes.getValue("model"); + myParagraphIndex = Integer.parseInt(attributes.getValue("paragraph")); + myElementIndex = Integer.parseInt(attributes.getValue("element")); + myCharIndex = Integer.parseInt(attributes.getValue("char")); + } catch (Exception e) { + return true; + } + } else { + return true; + } + break; + case READ_TEXT: + return true; + } + return false; + } + + @Override + public boolean endElementHandler(String tag) { + switch (myState) { + case READ_NOTHING: + return true; + case READ_BOOKMARK: + if ("bookmark".equals(tag)) { + myState = State.READ_NOTHING; + } + break; + case READ_TEXT: + myState = State.READ_BOOKMARK; + } + return false; + } + + @Override + public void characterDataHandler(char[] ch, int start, int length) { + if (myState == State.READ_TEXT) { + myText.append(ch, start, length); + } + } + } }