1
0
Fork 0
mirror of https://github.com/geometer/FBReaderJ.git synced 2025-10-05 10:49:24 +02:00

sync tree in library + fixed filters

This commit is contained in:
Nikolay Pultsin 2014-07-23 10:23:10 +01:00
parent f0e58a87af
commit 30ed81658a
11 changed files with 236 additions and 15 deletions

View file

@ -26,6 +26,21 @@
<node name="found" value="Found"> <node name="found" value="Found">
<node name="summary" value="Search results for: %s"/> <node name="summary" value="Search results for: %s"/>
</node> </node>
<node name="sync" value="Synchronization">
<node name="summary" value="Books by synchronization status"/>
<node name="sync-success" value="Synchronized">
<node name="summary" value="Books uploaded to the server"/>
</node>
<node name="sync-failure" value="Synchronization failed">
<node name="summary" value="Long tap this item to select action"/>
</node>
<node name="sync-deleted" value="Deleted from server">
<node name="summary" value="Long tap this item to select action"/>
</node>
<node name="sync-tosync" value="Not synchronized yet">
<node name="summary" value="Books to be uploaded to the server"/>
</node>
</node>
<node name="fileTree" value="File tree"> <node name="fileTree" value="File tree">
<node name="summary" value="Browse file system"/> <node name="summary" value="Browse file system"/>
</node> </node>

View file

@ -97,7 +97,7 @@ class LibraryTreeAdapter extends TreeAdapter {
return R.drawable.ic_list_library_book; return R.drawable.ic_list_library_book;
} else if (tree instanceof FavoritesTree) { } else if (tree instanceof FavoritesTree) {
return R.drawable.ic_list_library_favorites; return R.drawable.ic_list_library_favorites;
} else if (tree instanceof RecentBooksTree) { } else if (tree instanceof RecentBooksTree || tree instanceof SyncTree) {
return R.drawable.ic_list_library_recent; return R.drawable.ic_list_library_recent;
} else if (tree instanceof AuthorListTree) { } else if (tree instanceof AuthorListTree) {
return R.drawable.ic_list_library_authors; return R.drawable.ic_list_library_authors;

View file

@ -37,11 +37,25 @@ import org.geometerplus.android.fbreader.network.auth.ServiceNetworkContext;
public class SynchroniserService extends Service implements IBookCollection.Listener, Runnable { public class SynchroniserService extends Service implements IBookCollection.Listener, Runnable {
enum SyncStatus { enum SyncStatus {
AlreadyUploaded, AlreadyUploaded(Book.SYNCHRONIZED_LABEL),
Uploaded, Uploaded(Book.SYNCHRONIZED_LABEL),
ToBeDeleted, ToBeDeleted(Book.SYNC_DELETED_LABEL),
Failure, Failure(Book.SYNC_FAILURE_LABEL),
HashNotComputed FailedPreviuousTime(null),
HashNotComputed(null);
private static final List<String> AllLabels = Arrays.asList(
Book.SYNCHRONIZED_LABEL,
Book.SYNC_FAILURE_LABEL,
Book.SYNC_DELETED_LABEL,
Book.SYNC_TOSYNC_LABEL
);
public final String Label;
SyncStatus(String label) {
Label = label;
}
} }
private final ZLNetworkContext myNetworkContext = new ServiceNetworkContext(this); private final ZLNetworkContext myNetworkContext = new ServiceNetworkContext(this);
@ -110,6 +124,16 @@ public class SynchroniserService extends Service implements IBookCollection.List
myProcessed.add(book); myProcessed.add(book);
++count; ++count;
final SyncStatus status = uploadBookToServer(book); final SyncStatus status = uploadBookToServer(book);
if (status.Label != null) {
for (String label : SyncStatus.AllLabels) {
if (status.Label.equals(label)) {
book.addLabel(label);
} else {
book.removeLabel(label);
}
}
myCollection.saveBook(book);
}
final Integer sc = statusCounts.get(status); final Integer sc = statusCounts.get(status);
statusCounts.put(status, sc != null ? sc + 1 : 1); statusCounts.put(status, sc != null ? sc + 1 : 1);
} }
@ -190,12 +214,12 @@ public class SynchroniserService extends Service implements IBookCollection.List
final String hash = myCollection.getHash(book); final String hash = myCollection.getHash(book);
if (hash == null) { if (hash == null) {
return SyncStatus.HashNotComputed; return SyncStatus.HashNotComputed;
} } else if (myActualHashesFromServer.contains(hash)) {
if (myActualHashesFromServer.contains(hash)) {
return SyncStatus.AlreadyUploaded; return SyncStatus.AlreadyUploaded;
} } else if (myDeletedHashesFromServer.contains(hash)) {
if (myDeletedHashesFromServer.contains(hash)) {
return SyncStatus.ToBeDeleted; return SyncStatus.ToBeDeleted;
} else if (book.labels().contains(Book.SYNC_FAILURE_LABEL)) {
return SyncStatus.FailedPreviuousTime;
} }
final Map<String,Object> result = new HashMap<String,Object>(); final Map<String,Object> result = new HashMap<String,Object>();
final PostRequest verificationRequest = final PostRequest verificationRequest =

View file

@ -34,6 +34,10 @@ import org.geometerplus.fbreader.sort.TitledEntity;
public class Book extends TitledEntity { public class Book extends TitledEntity {
public static final String FAVORITE_LABEL = "favorite"; public static final String FAVORITE_LABEL = "favorite";
public static final String READ_LABEL = "read"; public static final String READ_LABEL = "read";
public static final String SYNCHRONIZED_LABEL = "sync-success";
public static final String SYNC_FAILURE_LABEL = "sync-failure";
public static final String SYNC_DELETED_LABEL = "sync-deleted";
public static final String SYNC_TOSYNC_LABEL = "sync-tosync";
public final ZLFile File; public final ZLFile File;

View file

@ -140,4 +140,16 @@ public abstract class Filter {
return First.matches(book) || Second.matches(book); return First.matches(book) || Second.matches(book);
} }
} }
public final static class Not extends Filter {
public final Filter Base;
public Not(Filter base) {
Base = base;
}
public boolean matches(Book book) {
return !Base.matches(book);
}
}
} }

View file

@ -52,6 +52,10 @@ class XMLSerializer extends AbstractSerializer {
appendTag(buffer, "filter", true, appendTag(buffer, "filter", true,
"type", "empty" "type", "empty"
); );
} else if (filter instanceof Filter.Not) {
appendTag(buffer, "not", false);
serialize(buffer, ((Filter.Not)filter).Base);
closeTag(buffer, "not");
} else if (filter instanceof Filter.And) { } else if (filter instanceof Filter.And) {
appendTag(buffer, "and", false); appendTag(buffer, "and", false);
serialize(buffer, ((Filter.And)filter).First); serialize(buffer, ((Filter.And)filter).First);
@ -686,6 +690,7 @@ class XMLSerializer extends AbstractSerializer {
private static final class BookQueryDeserializer extends DefaultHandler { private static final class BookQueryDeserializer extends DefaultHandler {
private static enum State { private static enum State {
READ_QUERY, READ_QUERY,
READ_FILTER_NOT,
READ_FILTER_AND, READ_FILTER_AND,
READ_FILTER_OR, READ_FILTER_OR,
READ_FILTER_SIMPLE READ_FILTER_SIMPLE
@ -714,6 +719,12 @@ class XMLSerializer extends AbstractSerializer {
} }
} }
private void setFilterToStack() {
if (!myFilterStack.isEmpty() && myFilterStack.getLast() == null) {
myFilterStack.set(myFilterStack.size() - 1, myFilter);
}
}
@Override @Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (myStateStack.isEmpty()) { if (myStateStack.isEmpty()) {
@ -759,10 +770,10 @@ class XMLSerializer extends AbstractSerializer {
// to keep a door to add new filters in a future // to keep a door to add new filters in a future
myFilter = new Filter.Empty(); myFilter = new Filter.Empty();
} }
if (!myFilterStack.isEmpty() && myFilterStack.getLast() == null) {
myFilterStack.set(myFilterStack.size() - 1, myFilter);
}
myStateStack.add(State.READ_FILTER_SIMPLE); myStateStack.add(State.READ_FILTER_SIMPLE);
} else if ("not".equals(localName)) {
myFilterStack.add(null);
myStateStack.add(State.READ_FILTER_NOT);
} else if ("and".equals(localName)) { } else if ("and".equals(localName)) {
myFilterStack.add(null); myFilterStack.add(null);
myStateStack.add(State.READ_FILTER_AND); myStateStack.add(State.READ_FILTER_AND);
@ -784,6 +795,9 @@ class XMLSerializer extends AbstractSerializer {
switch (myStateStack.removeLast()) { switch (myStateStack.removeLast()) {
case READ_QUERY: case READ_QUERY:
break; break;
case READ_FILTER_NOT:
myFilter = new Filter.Not(myFilterStack.removeLast());
break;
case READ_FILTER_AND: case READ_FILTER_AND:
myFilter = new Filter.And(myFilterStack.removeLast(), myFilter); myFilter = new Filter.And(myFilterStack.removeLast(), myFilter);
break; break;
@ -793,6 +807,7 @@ class XMLSerializer extends AbstractSerializer {
case READ_FILTER_SIMPLE: case READ_FILTER_SIMPLE:
break; break;
} }
setFilterToStack();
} }
} }

View file

@ -26,7 +26,7 @@ import org.geometerplus.fbreader.Paths;
public class FileFirstLevelTree extends FirstLevelTree { public class FileFirstLevelTree extends FirstLevelTree {
FileFirstLevelTree(RootTree root) { FileFirstLevelTree(RootTree root) {
super(root, ROOT_FILE_TREE); super(root, ROOT_FILE);
} }
@Override @Override

View file

@ -40,7 +40,8 @@ public abstract class LibraryTree extends FBTree {
static final String ROOT_BY_TITLE = "byTitle"; static final String ROOT_BY_TITLE = "byTitle";
static final String ROOT_BY_SERIES = "bySeries"; static final String ROOT_BY_SERIES = "bySeries";
static final String ROOT_BY_TAG = "byTag"; static final String ROOT_BY_TAG = "byTag";
static final String ROOT_FILE_TREE = "fileTree"; static final String ROOT_SYNC = "sync";
static final String ROOT_FILE = "fileTree";
protected LibraryTree(IBookCollection collection) { protected LibraryTree(IBookCollection collection) {
super(); super();

View file

@ -31,6 +31,7 @@ public class RootTree extends LibraryTree {
new TitleListTree(this); new TitleListTree(this);
new SeriesListTree(this); new SeriesListTree(this);
new TagListTree(this); new TagListTree(this);
new SyncTree(this);
new FileFirstLevelTree(this); new FileFirstLevelTree(this);
} }

View file

@ -0,0 +1,71 @@
/*
* Copyright (C) 2009-2014 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.fbreader.library;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.fbreader.book.Book;
import org.geometerplus.fbreader.book.Filter;
public class SyncLabelTree extends FilteredTree {
private final String myLabel;
private final ZLResource myResource;
SyncLabelTree(SyncTree parent, String label, Filter filter, ZLResource resource) {
super(parent, filter, -1);
myLabel = label;
myResource = resource;
}
@Override
public String getName() {
return myResource.getValue();
}
@Override
public String getTreeTitle() {
return getSummary();
}
@Override
public String getSummary() {
return myResource.getResource("summary").getValue();
}
@Override
protected String getStringId() {
return "@SyncLabelTree " + myLabel;
}
@Override
public boolean isSelectable() {
return false;
}
@Override
public Status getOpeningStatus() {
return Status.ALWAYS_RELOAD_BEFORE_OPENING;
}
@Override
protected boolean createSubtree(Book book) {
return createBookWithAuthorsSubtree(book);
}
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (C) 2009-2014 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.fbreader.library;
import java.util.Arrays;
import java.util.List;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.fbreader.book.Book;
import org.geometerplus.fbreader.book.Filter;
public class SyncTree extends FirstLevelTree {
private final List<String> myLabels = Arrays.asList(
Book.SYNCHRONIZED_LABEL,
Book.SYNC_FAILURE_LABEL,
Book.SYNC_DELETED_LABEL
);
SyncTree(RootTree root) {
super(root, ROOT_SYNC);
}
@Override
public String getTreeTitle() {
return getName();
}
@Override
public Status getOpeningStatus() {
return Status.ALWAYS_RELOAD_BEFORE_OPENING;
}
@Override
public void waitForOpening() {
clear();
final ZLResource baseResource = resource().getResource(ROOT_SYNC);
Filter others = null;
for (String label : myLabels) {
final Filter filter = new Filter.ByLabel(label);
if (Collection.hasBooks(filter)) {
new SyncLabelTree(this, label, filter, baseResource.getResource(label));
}
if (others == null) {
others = new Filter.Not(filter);
} else {
others = new Filter.And(others, new Filter.Not(filter));
}
}
if (Collection.hasBooks(others)) {
new SyncLabelTree(
this,
Book.SYNC_TOSYNC_LABEL,
others,
baseResource.getResource(Book.SYNC_TOSYNC_LABEL)
);
}
}
}