From 2148db4a2dcb3cb8be13fb74f0797867b99e2620 Mon Sep 17 00:00:00 2001 From: Nikolay Pultsin Date: Sat, 9 Feb 2013 15:53:51 +0400 Subject: [PATCH] title trees on-the-fly --- .../fbreader/library/LibraryTreeAdapter.java | 31 +++--- .../libraryService/BookCollectionShadow.java | 11 +++ .../libraryService/LibraryInterface.aidl | 1 + .../libraryService/LibraryService.java | 4 + .../fbreader/book/BookCollection.java | 10 ++ .../fbreader/book/IBookCollection.java | 3 +- .../fbreader/library/FirstLevelTree.java | 2 +- .../fbreader/library/Library.java | 30 +----- .../fbreader/library/LibraryTree.java | 24 ++--- .../fbreader/library/SearchResultsTree.java | 2 +- .../fbreader/library/TitleListTree.java | 97 +++++++++++++++++++ .../fbreader/library/TitleTree.java | 5 +- 12 files changed, 153 insertions(+), 67 deletions(-) create mode 100644 src/org/geometerplus/fbreader/library/TitleListTree.java diff --git a/src/org/geometerplus/android/fbreader/library/LibraryTreeAdapter.java b/src/org/geometerplus/android/fbreader/library/LibraryTreeAdapter.java index f41f44e1e..6a3b8c86f 100644 --- a/src/org/geometerplus/android/fbreader/library/LibraryTreeAdapter.java +++ b/src/org/geometerplus/android/fbreader/library/LibraryTreeAdapter.java @@ -75,23 +75,20 @@ class LibraryTreeAdapter extends TreeAdapter { private int getCoverResourceId(LibraryTree tree) { if (tree.getBook() != null) { return R.drawable.ic_list_library_book; - } else if (tree instanceof FirstLevelTree) { - final String id = tree.getUniqueKey().Id; - if (LibraryTree.ROOT_FAVORITES.equals(id)) { - return R.drawable.ic_list_library_favorites; - } else if (LibraryTree.ROOT_RECENT.equals(id)) { - return R.drawable.ic_list_library_recent; - } else if (LibraryTree.ROOT_BY_AUTHOR.equals(id)) { - return R.drawable.ic_list_library_authors; - } else if (LibraryTree.ROOT_BY_TITLE.equals(id)) { - return R.drawable.ic_list_library_books; - } else if (LibraryTree.ROOT_BY_TAG.equals(id)) { - return R.drawable.ic_list_library_tags; - } else if (LibraryTree.ROOT_FILE_TREE.equals(id)) { - return R.drawable.ic_list_library_folder; - } else if (LibraryTree.ROOT_FOUND.equals(id)) { - return R.drawable.ic_list_library_search; - } + } else if (tree instanceof FavoritesTree) { + return R.drawable.ic_list_library_favorites; + } else if (tree instanceof RecentBooksTree) { + return R.drawable.ic_list_library_recent; + } else if (tree instanceof AuthorListTree) { + return R.drawable.ic_list_library_authors; + } else if (tree instanceof TitleListTree) { + return R.drawable.ic_list_library_books; + } else if (tree instanceof TagListTree) { + return R.drawable.ic_list_library_tags; + } else if (tree instanceof FileFirstLevelTree) { + return R.drawable.ic_list_library_folder; + } else if (tree instanceof SearchResultsTree) { + return R.drawable.ic_list_library_search; } else if (tree instanceof FileTree) { final ZLFile file = ((FileTree)tree).getFile(); if (file.isArchive()) { diff --git a/src/org/geometerplus/android/fbreader/libraryService/BookCollectionShadow.java b/src/org/geometerplus/android/fbreader/libraryService/BookCollectionShadow.java index 9e1dbea33..67f8da513 100644 --- a/src/org/geometerplus/android/fbreader/libraryService/BookCollectionShadow.java +++ b/src/org/geometerplus/android/fbreader/libraryService/BookCollectionShadow.java @@ -288,6 +288,17 @@ public class BookCollectionShadow extends AbstractBookCollection implements Serv } } + public synchronized List titles() { + if (myInterface == null) { + return Collections.emptyList(); + } + try { + return myInterface.titles(); + } catch (RemoteException e) { + return Collections.emptyList(); + } + } + public synchronized boolean saveBook(Book book, boolean force) { if (myInterface == null) { return false; diff --git a/src/org/geometerplus/android/fbreader/libraryService/LibraryInterface.aidl b/src/org/geometerplus/android/fbreader/libraryService/LibraryInterface.aidl index a3977d494..0f870002c 100644 --- a/src/org/geometerplus/android/fbreader/libraryService/LibraryInterface.aidl +++ b/src/org/geometerplus/android/fbreader/libraryService/LibraryInterface.aidl @@ -25,6 +25,7 @@ interface LibraryInterface { boolean hasSeries(); List series(); List tags(); + List titles(); boolean saveBook(in String book, in boolean force); void removeBook(in String book, in boolean deleteFromDisk); diff --git a/src/org/geometerplus/android/fbreader/libraryService/LibraryService.java b/src/org/geometerplus/android/fbreader/libraryService/LibraryService.java index ec5972bf5..6f1ad649b 100644 --- a/src/org/geometerplus/android/fbreader/libraryService/LibraryService.java +++ b/src/org/geometerplus/android/fbreader/libraryService/LibraryService.java @@ -185,6 +185,10 @@ public class LibraryService extends Service { return strings; } + public List titles() { + return myCollection.titles(); + } + public boolean saveBook(String book, boolean force) { return myCollection.saveBook(SerializerUtil.deserializeBook(book), force); } diff --git a/src/org/geometerplus/fbreader/book/BookCollection.java b/src/org/geometerplus/fbreader/book/BookCollection.java index 78ff0e2e8..7c82ac60b 100644 --- a/src/org/geometerplus/fbreader/book/BookCollection.java +++ b/src/org/geometerplus/fbreader/book/BookCollection.java @@ -332,6 +332,16 @@ public class BookCollection extends AbstractBookCollection { return new ArrayList(series); } + public List titles() { + synchronized (myBooksByFile) { + final List titles = new ArrayList(myBooksByFile.size()); + for (Book book : myBooksByFile.values()) { + titles.add(book.getTitle()); + } + return titles; + } + } + public Book getRecentBook(int index) { List recentIds = myDatabase.loadRecentBookIds(); return recentIds.size() > index ? getBookById(recentIds.get(index)) : null; diff --git a/src/org/geometerplus/fbreader/book/IBookCollection.java b/src/org/geometerplus/fbreader/book/IBookCollection.java index b4b57479d..2fd85fd6e 100644 --- a/src/org/geometerplus/fbreader/book/IBookCollection.java +++ b/src/org/geometerplus/fbreader/book/IBookCollection.java @@ -63,9 +63,10 @@ public interface IBookCollection { Book getBookById(long id); List authors(); - List tags(); boolean hasSeries(); List series(); + List tags(); + List titles(); boolean saveBook(Book book, boolean force); void removeBook(Book book, boolean deleteFromDisk); diff --git a/src/org/geometerplus/fbreader/library/FirstLevelTree.java b/src/org/geometerplus/fbreader/library/FirstLevelTree.java index 5b4c213b2..8365030e1 100644 --- a/src/org/geometerplus/fbreader/library/FirstLevelTree.java +++ b/src/org/geometerplus/fbreader/library/FirstLevelTree.java @@ -21,7 +21,7 @@ package org.geometerplus.fbreader.library; import org.geometerplus.zlibrary.core.resources.ZLResource; -public class FirstLevelTree extends LibraryTree { +abstract class FirstLevelTree extends LibraryTree { private final String myId; private final ZLResource myResource; diff --git a/src/org/geometerplus/fbreader/library/Library.java b/src/org/geometerplus/fbreader/library/Library.java index fa920fb96..3f088518d 100644 --- a/src/org/geometerplus/fbreader/library/Library.java +++ b/src/org/geometerplus/fbreader/library/Library.java @@ -84,7 +84,6 @@ public final class Library { private final Map myBooks = Collections.synchronizedMap(new HashMap()); private final RootTree myRootTree; - private boolean myDoGroupTitlesByFirstLetter; private final static int STATUS_LOADING = 1; private final static int STATUS_SEARCHING = 2; @@ -109,7 +108,7 @@ public final class Library { new FavoritesTree(myRootTree); new RecentBooksTree(myRootTree); new AuthorListTree(myRootTree); - new FirstLevelTree(myRootTree, LibraryTree.ROOT_BY_TITLE); + new TitleListTree(myRootTree); new SeriesListTree(myRootTree); new TagListTree(myRootTree); new FileFirstLevelTree(myRootTree); @@ -231,17 +230,6 @@ public final class Library { } myBooks.put(book.File, book); - if (myDoGroupTitlesByFirstLetter) { - final String letter = TitleTree.firstTitleLetter(book); - if (letter != null) { - final TitleTree tree = - getFirstLevelTree(LibraryTree.ROOT_BY_TITLE).getTitleSubTree(letter); - tree.createBookWithAuthorsSubTree(book); - } - } else { - getFirstLevelTree(LibraryTree.ROOT_BY_TITLE).createBookWithAuthorsSubTree(book); - } - final SearchResultsTree found = (SearchResultsTree)getFirstLevelTree(LibraryTree.ROOT_FOUND); if (found != null && book.matches(found.getPattern())) { @@ -263,7 +251,6 @@ public final class Library { myBooks.remove(book.File); removeFromTree(LibraryTree.ROOT_FOUND, book); - removeFromTree(LibraryTree.ROOT_BY_TITLE, book); addBookToLibrary(book); fireModelChangedEvent(ChangeListener.Code.BookAdded); } @@ -277,21 +264,6 @@ public final class Library { savedBooksByBookId.put(b.getId(), b); } - // Step 1: set myDoGroupTitlesByFirstLetter value, - // add "existing" books into recent and favorites lists - if (savedBooksByFileId.size() > 10) { - final HashSet letterSet = new HashSet(); - for (Book book : savedBooksByFileId.values()) { - final String letter = TitleTree.firstTitleLetter(book); - if (letter != null) { - letterSet.add(letter); - } - } - myDoGroupTitlesByFirstLetter = savedBooksByFileId.values().size() > letterSet.size() * 5 / 4; - } - - fireModelChangedEvent(ChangeListener.Code.BookAdded); - // Step 2: check if files corresponding to "existing" books really exists; // add books to library if yes (and reload book info if needed); // remove from recent/favorites list if no; diff --git a/src/org/geometerplus/fbreader/library/LibraryTree.java b/src/org/geometerplus/fbreader/library/LibraryTree.java index ceb60fbaf..6357069b4 100644 --- a/src/org/geometerplus/fbreader/library/LibraryTree.java +++ b/src/org/geometerplus/fbreader/library/LibraryTree.java @@ -26,13 +26,13 @@ import org.geometerplus.fbreader.tree.FBTree; public abstract class LibraryTree extends FBTree { public static final String ROOT_FOUND = "found"; - public static final String ROOT_FAVORITES = "favorites"; - public static final String ROOT_RECENT = "recent"; - public static final String ROOT_BY_AUTHOR = "byAuthor"; - public static final String ROOT_BY_TITLE = "byTitle"; - public static final String ROOT_BY_SERIES = "bySeries"; - public static final String ROOT_BY_TAG = "byTag"; - public static final String ROOT_FILE_TREE = "fileTree"; + static final String ROOT_FAVORITES = "favorites"; + static final String ROOT_RECENT = "recent"; + static final String ROOT_BY_AUTHOR = "byAuthor"; + static final String ROOT_BY_TITLE = "byTitle"; + static final String ROOT_BY_SERIES = "bySeries"; + static final String ROOT_BY_TAG = "byTag"; + static final String ROOT_FILE_TREE = "fileTree"; public final IBookCollection Collection; @@ -74,16 +74,6 @@ public abstract class LibraryTree extends FBTree { } } - TitleTree getTitleSubTree(String title) { - final TitleTree temp = new TitleTree(Collection, title); - int position = Collections.binarySearch(subTrees(), temp); - if (position >= 0) { - return (TitleTree)subTrees().get(position); - } else { - return new TitleTree(this, title, - position - 1); - } - } - boolean createBookWithAuthorsSubTree(Book book) { final BookWithAuthorsTree temp = new BookWithAuthorsTree(Collection, book); int position = Collections.binarySearch(subTrees(), temp); diff --git a/src/org/geometerplus/fbreader/library/SearchResultsTree.java b/src/org/geometerplus/fbreader/library/SearchResultsTree.java index f3509fbbb..78cf9131b 100644 --- a/src/org/geometerplus/fbreader/library/SearchResultsTree.java +++ b/src/org/geometerplus/fbreader/library/SearchResultsTree.java @@ -19,7 +19,7 @@ package org.geometerplus.fbreader.library; -class SearchResultsTree extends FirstLevelTree { +public class SearchResultsTree extends FirstLevelTree { private final String myPattern; SearchResultsTree(RootTree root, String id, String pattern) { diff --git a/src/org/geometerplus/fbreader/library/TitleListTree.java b/src/org/geometerplus/fbreader/library/TitleListTree.java new file mode 100644 index 000000000..ff6f6da41 --- /dev/null +++ b/src/org/geometerplus/fbreader/library/TitleListTree.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2009-2013 Geometer Plus + * + * 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.fbreader.library; + +import java.util.*; + +import org.geometerplus.fbreader.book.Book; +import org.geometerplus.fbreader.book.BookEvent; + +public class TitleListTree extends FirstLevelTree { + private boolean myDoGroupByFirstLetter; + + TitleListTree(RootTree root) { + super(root, ROOT_BY_TITLE); + } + + @Override + public Status getOpeningStatus() { + return Status.ALWAYS_RELOAD_BEFORE_OPENING; + } + + @Override + public void waitForOpening() { + clear(); + + myDoGroupByFirstLetter = false; + final TreeSet letterSet = new TreeSet(); + final List titles = Collection.titles(); + if (titles.size() > 10) { + for (String t : titles) { + final String letter = TitleTree.firstTitleLetter(t); + if (letter != null) { + letterSet.add(letter); + } + } + myDoGroupByFirstLetter = titles.size() > letterSet.size() * 5 / 4; + } + + if (myDoGroupByFirstLetter) { + for (String letter : letterSet) { + createTitleSubTree(letter); + } + } else { + for (Book b : Collection.books()) { + createBookWithAuthorsSubTree(b); + } + } + } + + @Override + public boolean onBookEvent(BookEvent event, Book book) { + switch (event) { + case Added: + if (myDoGroupByFirstLetter) { + final String letter = TitleTree.firstTitleLetter(book); + return letter != null && createTitleSubTree(letter); + } else { + return createBookWithAuthorsSubTree(book); + } + case Removed: + // TODO: implement + return false; + default: + case Updated: + // TODO: implement + return false; + } + } + + boolean createTitleSubTree(String title) { + final TitleTree temp = new TitleTree(Collection, title); + int position = Collections.binarySearch(subTrees(), temp); + if (position >= 0) { + return false; + } else { + new TitleTree(this, title, - position - 1); + return true; + } + } +} diff --git a/src/org/geometerplus/fbreader/library/TitleTree.java b/src/org/geometerplus/fbreader/library/TitleTree.java index 143ebc511..a38ddc8fd 100644 --- a/src/org/geometerplus/fbreader/library/TitleTree.java +++ b/src/org/geometerplus/fbreader/library/TitleTree.java @@ -27,7 +27,10 @@ public final class TitleTree extends LibraryTree { if (book == null) { return null; } - String title = book.getTitle(); + return firstTitleLetter(book.getTitle()); + } + + static String firstTitleLetter(String title) { if (title == null) { return null; }