mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 21:29:54 +01:00
Introduce client layer events for forums
The forum UI depended on sync layer events such as MessageStateChangedEvent. Now, the forum client broadcasts its own high-level event (`ForumPostReceivedEvent`) with the information the UI needs (`ForumPostHeader`). Closes #310
This commit is contained in:
@@ -21,18 +21,17 @@ import org.briarproject.api.db.DbException;
|
|||||||
import org.briarproject.api.event.Event;
|
import org.briarproject.api.event.Event;
|
||||||
import org.briarproject.api.event.EventListener;
|
import org.briarproject.api.event.EventListener;
|
||||||
import org.briarproject.api.event.ForumInvitationReceivedEvent;
|
import org.briarproject.api.event.ForumInvitationReceivedEvent;
|
||||||
|
import org.briarproject.api.event.ForumPostReceivedEvent;
|
||||||
import org.briarproject.api.event.IntroductionRequestReceivedEvent;
|
import org.briarproject.api.event.IntroductionRequestReceivedEvent;
|
||||||
import org.briarproject.api.event.IntroductionResponseReceivedEvent;
|
import org.briarproject.api.event.IntroductionResponseReceivedEvent;
|
||||||
import org.briarproject.api.event.IntroductionSucceededEvent;
|
import org.briarproject.api.event.IntroductionSucceededEvent;
|
||||||
import org.briarproject.api.event.MessageStateChangedEvent;
|
import org.briarproject.api.event.PrivateMessageReceivedEvent;
|
||||||
import org.briarproject.api.event.SettingsUpdatedEvent;
|
import org.briarproject.api.event.SettingsUpdatedEvent;
|
||||||
import org.briarproject.api.forum.ForumManager;
|
|
||||||
import org.briarproject.api.lifecycle.Service;
|
import org.briarproject.api.lifecycle.Service;
|
||||||
import org.briarproject.api.lifecycle.ServiceException;
|
import org.briarproject.api.lifecycle.ServiceException;
|
||||||
import org.briarproject.api.messaging.MessagingManager;
|
import org.briarproject.api.messaging.MessagingManager;
|
||||||
import org.briarproject.api.settings.Settings;
|
import org.briarproject.api.settings.Settings;
|
||||||
import org.briarproject.api.settings.SettingsManager;
|
import org.briarproject.api.settings.SettingsManager;
|
||||||
import org.briarproject.api.sync.ClientId;
|
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
import org.briarproject.util.StringUtils;
|
import org.briarproject.util.StringUtils;
|
||||||
|
|
||||||
@@ -59,7 +58,6 @@ import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
|||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.android.BriarActivity.GROUP_ID;
|
import static org.briarproject.android.BriarActivity.GROUP_ID;
|
||||||
import static org.briarproject.android.fragment.SettingsFragment.SETTINGS_NAMESPACE;
|
import static org.briarproject.android.fragment.SettingsFragment.SETTINGS_NAMESPACE;
|
||||||
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
|
||||||
|
|
||||||
class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||||
Service, EventListener {
|
Service, EventListener {
|
||||||
@@ -78,7 +76,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
private final SettingsManager settingsManager;
|
private final SettingsManager settingsManager;
|
||||||
private final MessagingManager messagingManager;
|
private final MessagingManager messagingManager;
|
||||||
private final ForumManager forumManager;
|
|
||||||
private final AndroidExecutor androidExecutor;
|
private final AndroidExecutor androidExecutor;
|
||||||
private final Context appContext;
|
private final Context appContext;
|
||||||
|
|
||||||
@@ -94,14 +91,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
private volatile Settings settings = new Settings();
|
private volatile Settings settings = new Settings();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public AndroidNotificationManagerImpl(@DatabaseExecutor Executor dbExecutor,
|
AndroidNotificationManagerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||||
SettingsManager settingsManager, MessagingManager messagingManager,
|
SettingsManager settingsManager, MessagingManager messagingManager,
|
||||||
ForumManager forumManager, AndroidExecutor androidExecutor,
|
AndroidExecutor androidExecutor, Application app) {
|
||||||
Application app) {
|
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.settingsManager = settingsManager;
|
this.settingsManager = settingsManager;
|
||||||
this.messagingManager = messagingManager;
|
this.messagingManager = messagingManager;
|
||||||
this.forumManager = forumManager;
|
|
||||||
this.androidExecutor = androidExecutor;
|
this.androidExecutor = androidExecutor;
|
||||||
appContext = app.getApplicationContext();
|
appContext = app.getApplicationContext();
|
||||||
}
|
}
|
||||||
@@ -157,15 +152,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
if (e instanceof SettingsUpdatedEvent) {
|
if (e instanceof SettingsUpdatedEvent) {
|
||||||
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
|
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
|
||||||
if (s.getNamespace().equals(SETTINGS_NAMESPACE)) loadSettings();
|
if (s.getNamespace().equals(SETTINGS_NAMESPACE)) loadSettings();
|
||||||
} else if (e instanceof MessageStateChangedEvent) {
|
} else if (e instanceof PrivateMessageReceivedEvent) {
|
||||||
MessageStateChangedEvent m = (MessageStateChangedEvent) e;
|
PrivateMessageReceivedEvent m = (PrivateMessageReceivedEvent) e;
|
||||||
if (!m.isLocal() && m.getState() == DELIVERED) {
|
showPrivateMessageNotification(m.getGroupId());
|
||||||
ClientId c = m.getClientId();
|
} else if (e instanceof ForumPostReceivedEvent) {
|
||||||
if (c.equals(messagingManager.getClientId()))
|
ForumPostReceivedEvent m = (ForumPostReceivedEvent) e;
|
||||||
showPrivateMessageNotification(m.getMessage().getGroupId());
|
showForumPostNotification(m.getGroupId());
|
||||||
else if (c.equals(forumManager.getClientId()))
|
|
||||||
showForumPostNotification(m.getMessage().getGroupId());
|
|
||||||
}
|
|
||||||
} else if (e instanceof IntroductionRequestReceivedEvent) {
|
} else if (e instanceof IntroductionRequestReceivedEvent) {
|
||||||
ContactId c = ((IntroductionRequestReceivedEvent) e).getContactId();
|
ContactId c = ((IntroductionRequestReceivedEvent) e).getContactId();
|
||||||
showNotificationForPrivateConversation(c);
|
showNotificationForPrivateConversation(c);
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ public class ForumActivity extends BriarActivity implements
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(ForumActivity.class.getName());
|
Logger.getLogger(ForumActivity.class.getName());
|
||||||
|
|
||||||
public static final String MIN_TIMESTAMP = "briar.MIN_TIMESTAMP";
|
|
||||||
static final String FORUM_NAME = "briar.FORUM_NAME";
|
static final String FORUM_NAME = "briar.FORUM_NAME";
|
||||||
private static final int REQUEST_FORUM_SHARED = 3;
|
private static final int REQUEST_FORUM_SHARED = 3;
|
||||||
|
|
||||||
@@ -341,7 +340,6 @@ public class ForumActivity extends BriarActivity implements
|
|||||||
final View chevron, replyButton;
|
final View chevron, replyButton;
|
||||||
final ViewGroup cell;
|
final ViewGroup cell;
|
||||||
final View topDivider;
|
final View topDivider;
|
||||||
public ValueAnimator highlightAnimator;
|
|
||||||
|
|
||||||
ForumViewHolder(View v) {
|
ForumViewHolder(View v) {
|
||||||
super(v);
|
super(v);
|
||||||
@@ -438,7 +436,7 @@ public class ForumActivity extends BriarActivity implements
|
|||||||
// TODO This loop doesn't really loop. @ernir please review!
|
// TODO This loop doesn't really loop. @ernir please review!
|
||||||
for (int i = visiblePos + 1; i < getItemCount(); i++) {
|
for (int i = visiblePos + 1; i < getItemCount(); i++) {
|
||||||
ForumEntry entry = getVisibleEntry(i);
|
ForumEntry entry = getVisibleEntry(i);
|
||||||
if (entry.getLevel() <= levelLimit)
|
if (entry != null && entry.getLevel() <= levelLimit)
|
||||||
break;
|
break;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -486,7 +484,7 @@ public class ForumActivity extends BriarActivity implements
|
|||||||
|
|
||||||
for (int i = pos + 1; i < getItemCount(); i++) {
|
for (int i = pos + 1; i < getItemCount(); i++) {
|
||||||
ForumEntry entry = getVisibleEntry(i);
|
ForumEntry entry = getVisibleEntry(i);
|
||||||
if (entry.getLevel() > levelLimit) {
|
if (entry != null && entry.getLevel() > levelLimit) {
|
||||||
indexList.add(i);
|
indexList.add(i);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@@ -613,6 +611,8 @@ public class ForumActivity extends BriarActivity implements
|
|||||||
public void onBindViewHolder(
|
public void onBindViewHolder(
|
||||||
final ForumViewHolder ui, final int position) {
|
final ForumViewHolder ui, final int position) {
|
||||||
final ForumEntry data = getVisibleEntry(position);
|
final ForumEntry data = getVisibleEntry(position);
|
||||||
|
if (data == null) return;
|
||||||
|
|
||||||
if (!data.isRead()) {
|
if (!data.isRead()) {
|
||||||
data.setRead(true);
|
data.setRead(true);
|
||||||
forumController.entryRead(data);
|
forumController.entryRead(data);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public interface ForumController extends ActivityLifecycleController {
|
|||||||
void createPost(byte[] body);
|
void createPost(byte[] body);
|
||||||
void createPost(byte[] body, MessageId parentId);
|
void createPost(byte[] body, MessageId parentId);
|
||||||
|
|
||||||
public interface ForumPostListener {
|
interface ForumPostListener {
|
||||||
void addLocalEntry(int index, ForumEntry entry);
|
void addLocalEntry(int index, ForumEntry entry);
|
||||||
void addForeignEntry(int index, ForumEntry entry);
|
void addForeignEntry(int index, ForumEntry entry);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,12 +13,13 @@ import org.briarproject.api.db.DbException;
|
|||||||
import org.briarproject.api.event.Event;
|
import org.briarproject.api.event.Event;
|
||||||
import org.briarproject.api.event.EventBus;
|
import org.briarproject.api.event.EventBus;
|
||||||
import org.briarproject.api.event.EventListener;
|
import org.briarproject.api.event.EventListener;
|
||||||
|
import org.briarproject.api.event.ForumPostReceivedEvent;
|
||||||
import org.briarproject.api.event.GroupRemovedEvent;
|
import org.briarproject.api.event.GroupRemovedEvent;
|
||||||
import org.briarproject.api.event.MessageStateChangedEvent;
|
|
||||||
import org.briarproject.api.forum.ForumManager;
|
import org.briarproject.api.forum.ForumManager;
|
||||||
import org.briarproject.api.forum.ForumPost;
|
import org.briarproject.api.forum.ForumPost;
|
||||||
import org.briarproject.api.forum.ForumPostFactory;
|
import org.briarproject.api.forum.ForumPostFactory;
|
||||||
import org.briarproject.api.forum.ForumPostHeader;
|
import org.briarproject.api.forum.ForumPostHeader;
|
||||||
|
import org.briarproject.api.identity.Author;
|
||||||
import org.briarproject.api.identity.IdentityManager;
|
import org.briarproject.api.identity.IdentityManager;
|
||||||
import org.briarproject.api.identity.LocalAuthor;
|
import org.briarproject.api.identity.LocalAuthor;
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
@@ -38,7 +39,7 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
import static org.briarproject.api.identity.Author.Status.VERIFIED;
|
||||||
|
|
||||||
public class ForumControllerImpl extends DbControllerImpl
|
public class ForumControllerImpl extends DbControllerImpl
|
||||||
implements ForumController, EventListener {
|
implements ForumController, EventListener {
|
||||||
@@ -52,7 +53,7 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
@CryptoExecutor
|
@CryptoExecutor
|
||||||
protected Executor cryptoExecutor;
|
protected Executor cryptoExecutor;
|
||||||
@Inject
|
@Inject
|
||||||
protected volatile ForumPostFactory forumPostFactory;
|
volatile ForumPostFactory forumPostFactory;
|
||||||
@Inject
|
@Inject
|
||||||
protected volatile CryptoComponent crypto;
|
protected volatile CryptoComponent crypto;
|
||||||
@Inject
|
@Inject
|
||||||
@@ -65,7 +66,6 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
protected ForumPersistentData data;
|
protected ForumPersistentData data;
|
||||||
|
|
||||||
private ForumPostListener listener;
|
private ForumPostListener listener;
|
||||||
private MessageId localAdd = null;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ForumControllerImpl() {
|
ForumControllerImpl() {
|
||||||
@@ -100,51 +100,13 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findSingleNewEntry() {
|
|
||||||
runOnDbThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
List<ForumEntry> oldEntries = getForumEntries();
|
|
||||||
data.clearHeaders();
|
|
||||||
try {
|
|
||||||
loadPosts();
|
|
||||||
List<ForumEntry> allEntries = getForumEntries();
|
|
||||||
int i = 0;
|
|
||||||
for (ForumEntry entry : allEntries) {
|
|
||||||
boolean isNew = true;
|
|
||||||
for (ForumEntry oldEntry : oldEntries) {
|
|
||||||
if (entry.getMessageId()
|
|
||||||
.equals(oldEntry.getMessageId())) {
|
|
||||||
isNew = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isNew) {
|
|
||||||
if (localAdd != null &&
|
|
||||||
entry.getMessageId().equals(localAdd)) {
|
|
||||||
addLocalEntry(i, entry);
|
|
||||||
} else {
|
|
||||||
addForeignEntry(i, entry);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
} catch (DbException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (e instanceof MessageStateChangedEvent) {
|
if (e instanceof ForumPostReceivedEvent) {
|
||||||
MessageStateChangedEvent m = (MessageStateChangedEvent) e;
|
ForumPostReceivedEvent pe = (ForumPostReceivedEvent) e;
|
||||||
if (m.getState() == DELIVERED &&
|
if (pe.getGroupId().equals(data.getGroupId())) {
|
||||||
m.getMessage().getGroupId().equals(data.getGroupId())) {
|
LOG.info("Forum Post received, adding...");
|
||||||
LOG.info("Message added, reloading");
|
addNewPost(pe.getForumPostHeader());
|
||||||
findSingleNewEntry();
|
|
||||||
}
|
}
|
||||||
} else if (e instanceof GroupRemovedEvent) {
|
} else if (e instanceof GroupRemovedEvent) {
|
||||||
GroupRemovedEvent s = (GroupRemovedEvent) e;
|
GroupRemovedEvent s = (GroupRemovedEvent) e;
|
||||||
@@ -160,6 +122,40 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addNewPost(final ForumPostHeader h) {
|
||||||
|
if (data == null) return;
|
||||||
|
runOnDbThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
data.addHeader(h);
|
||||||
|
data.clearForumEntries();
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] body = forumManager.getPostBody(h.getId());
|
||||||
|
data.addBody(h.getId(), body);
|
||||||
|
} catch (DbException e) {
|
||||||
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Author a = data.getLocalAuthor();
|
||||||
|
// FIXME we should not need to calculate the index here
|
||||||
|
// the index is essentially stored in two different locations
|
||||||
|
int i = 0;
|
||||||
|
for (ForumEntry entry : getForumEntries()) {
|
||||||
|
if (entry.getMessageId().equals(h.getId())) {
|
||||||
|
if (a != null && a.equals(h.getAuthor())) {
|
||||||
|
addLocalEntry(i, entry);
|
||||||
|
} else {
|
||||||
|
addForeignEntry(i, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void loadAuthor() throws DbException {
|
private void loadAuthor() throws DbException {
|
||||||
Collection<LocalAuthor> localAuthors =
|
Collection<LocalAuthor> localAuthors =
|
||||||
identityManager.getLocalAuthors();
|
identityManager.getLocalAuthors();
|
||||||
@@ -343,6 +339,7 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
storePost(p);
|
storePost(p);
|
||||||
|
addNewPost(p);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -369,7 +366,6 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
runOnDbThread(new Runnable() {
|
runOnDbThread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
localAdd = p.getMessage().getId();
|
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
forumManager.addLocalPost(p);
|
forumManager.addLocalPost(p);
|
||||||
long duration = System.currentTimeMillis() - now;
|
long duration = System.currentTimeMillis() - now;
|
||||||
@@ -384,4 +380,12 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addNewPost(final ForumPost p) {
|
||||||
|
ForumPostHeader h =
|
||||||
|
new ForumPostHeader(p.getMessage().getId(), p.getParent(),
|
||||||
|
p.getMessage().getTimestamp(), p.getAuthor(), VERIFIED,
|
||||||
|
p.getContentType(), false);
|
||||||
|
addNewPost(h);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class ForumEntry {
|
|||||||
private boolean isShowingDescendants = true;
|
private boolean isShowingDescendants = true;
|
||||||
private boolean isRead = true;
|
private boolean isRead = true;
|
||||||
|
|
||||||
public ForumEntry(ForumPostHeader h, String text, int level) {
|
ForumEntry(ForumPostHeader h, String text, int level) {
|
||||||
this(h.getId(), text, level, h.getTimestamp(), h.getAuthor().getName(),
|
this(h.getId(), text, level, h.getTimestamp(), h.getAuthor().getName(),
|
||||||
h.getAuthor().getId(), h.getAuthorStatus());
|
h.getAuthor().getId(), h.getAuthorStatus());
|
||||||
this.isRead = h.isRead();
|
this.isRead = h.isRead();
|
||||||
@@ -50,7 +50,7 @@ public class ForumEntry {
|
|||||||
return author;
|
return author;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthorId getAuthorId() {
|
AuthorId getAuthorId() {
|
||||||
return authorId;
|
return authorId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,15 +58,15 @@ public class ForumEntry {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isShowingDescendants() {
|
boolean isShowingDescendants() {
|
||||||
return isShowingDescendants;
|
return isShowingDescendants;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setShowingDescendants(boolean showingDescendants) {
|
void setShowingDescendants(boolean showingDescendants) {
|
||||||
this.isShowingDescendants = showingDescendants;
|
this.isShowingDescendants = showingDescendants;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessageId getMessageId() {
|
MessageId getMessageId() {
|
||||||
return messageId;
|
return messageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ public class ForumEntry {
|
|||||||
return isRead;
|
return isRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRead(boolean read) {
|
void setRead(boolean read) {
|
||||||
isRead = read;
|
isRead = read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,14 +22,13 @@ import org.briarproject.api.db.NoSuchGroupException;
|
|||||||
import org.briarproject.api.event.ContactRemovedEvent;
|
import org.briarproject.api.event.ContactRemovedEvent;
|
||||||
import org.briarproject.api.event.Event;
|
import org.briarproject.api.event.Event;
|
||||||
import org.briarproject.api.event.ForumInvitationReceivedEvent;
|
import org.briarproject.api.event.ForumInvitationReceivedEvent;
|
||||||
|
import org.briarproject.api.event.ForumPostReceivedEvent;
|
||||||
import org.briarproject.api.event.GroupAddedEvent;
|
import org.briarproject.api.event.GroupAddedEvent;
|
||||||
import org.briarproject.api.event.GroupRemovedEvent;
|
import org.briarproject.api.event.GroupRemovedEvent;
|
||||||
import org.briarproject.api.event.MessageStateChangedEvent;
|
|
||||||
import org.briarproject.api.forum.Forum;
|
import org.briarproject.api.forum.Forum;
|
||||||
import org.briarproject.api.forum.ForumManager;
|
import org.briarproject.api.forum.ForumManager;
|
||||||
import org.briarproject.api.forum.ForumPostHeader;
|
import org.briarproject.api.forum.ForumPostHeader;
|
||||||
import org.briarproject.api.forum.ForumSharingManager;
|
import org.briarproject.api.forum.ForumSharingManager;
|
||||||
import org.briarproject.api.sync.ClientId;
|
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -41,7 +40,6 @@ import javax.inject.Inject;
|
|||||||
import static android.support.design.widget.Snackbar.LENGTH_INDEFINITE;
|
import static android.support.design.widget.Snackbar.LENGTH_INDEFINITE;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
|
||||||
|
|
||||||
public class ForumListFragment extends BaseEventFragment implements
|
public class ForumListFragment extends BaseEventFragment implements
|
||||||
View.OnClickListener {
|
View.OnClickListener {
|
||||||
@@ -234,14 +232,10 @@ public class ForumListFragment extends BaseEventFragment implements
|
|||||||
LOG.info("Forum removed, removing from list");
|
LOG.info("Forum removed, removing from list");
|
||||||
removeForum(g.getGroup().getId());
|
removeForum(g.getGroup().getId());
|
||||||
}
|
}
|
||||||
} else if (e instanceof MessageStateChangedEvent) {
|
} else if (e instanceof ForumPostReceivedEvent) {
|
||||||
MessageStateChangedEvent m = (MessageStateChangedEvent) e;
|
ForumPostReceivedEvent m = (ForumPostReceivedEvent) e;
|
||||||
ClientId c = m.getClientId();
|
LOG.info("Forum post added, reloading");
|
||||||
if (m.getState() == DELIVERED &&
|
loadForumHeaders(m.getGroupId());
|
||||||
c.equals(forumManager.getClientId())) {
|
|
||||||
LOG.info("Forum post added, reloading");
|
|
||||||
loadForumHeaders(m.getMessage().getGroupId());
|
|
||||||
}
|
|
||||||
} else if (e instanceof ForumInvitationReceivedEvent) {
|
} else if (e instanceof ForumInvitationReceivedEvent) {
|
||||||
loadAvailableForums();
|
loadAvailableForums();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import java.util.Collection;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is a singleton that defines the data that should persist, i.e.
|
* This class is a singleton that defines the data that should persist, i.e.
|
||||||
@@ -21,7 +20,7 @@ import java.util.logging.Logger;
|
|||||||
*/
|
*/
|
||||||
public class ForumPersistentData {
|
public class ForumPersistentData {
|
||||||
|
|
||||||
protected volatile MessageTree<ForumPostHeader> tree =
|
private volatile MessageTree<ForumPostHeader> tree =
|
||||||
new MessageTreeImpl<>();
|
new MessageTreeImpl<>();
|
||||||
private volatile Map<MessageId, byte[]> bodyCache = new HashMap<>();
|
private volatile Map<MessageId, byte[]> bodyCache = new HashMap<>();
|
||||||
private volatile LocalAuthor localAuthor;
|
private volatile LocalAuthor localAuthor;
|
||||||
@@ -29,36 +28,36 @@ public class ForumPersistentData {
|
|||||||
private volatile GroupId groupId;
|
private volatile GroupId groupId;
|
||||||
private List<ForumEntry> forumEntries;
|
private List<ForumEntry> forumEntries;
|
||||||
|
|
||||||
private static final Logger LOG =
|
void clearAll() {
|
||||||
Logger.getLogger(ForumControllerImpl.class.getName());
|
clearForumEntries();
|
||||||
|
tree.clear();
|
||||||
|
|
||||||
public void clearAll() {
|
|
||||||
clearHeaders();
|
|
||||||
bodyCache.clear();
|
bodyCache.clear();
|
||||||
localAuthor = null;
|
localAuthor = null;
|
||||||
forum = null;
|
forum = null;
|
||||||
groupId = null;
|
groupId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearHeaders() {
|
void clearForumEntries() {
|
||||||
tree.clear();
|
|
||||||
forumEntries = null;
|
forumEntries = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addHeaders(Collection<ForumPostHeader> headers) {
|
void addHeaders(Collection<ForumPostHeader> headers) {
|
||||||
tree.add(headers);
|
tree.add(headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addHeader(ForumPostHeader header) {
|
||||||
|
tree.add(header);
|
||||||
|
}
|
||||||
|
|
||||||
public Collection<ForumPostHeader> getHeaders() {
|
public Collection<ForumPostHeader> getHeaders() {
|
||||||
return tree.depthFirstOrder();
|
return tree.depthFirstOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addBody(MessageId messageId, byte[] body) {
|
void addBody(MessageId messageId, byte[] body) {
|
||||||
bodyCache.put(messageId, body);
|
bodyCache.put(messageId, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getBody(MessageId messageId) {
|
byte[] getBody(MessageId messageId) {
|
||||||
return bodyCache.get(messageId);
|
return bodyCache.get(messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,11 +86,11 @@ public class ForumPersistentData {
|
|||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ForumEntry> getForumEntries() {
|
List<ForumEntry> getForumEntries() {
|
||||||
return forumEntries;
|
return forumEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setForumEntries(List<ForumEntry> forumEntries) {
|
void setForumEntries(List<ForumEntry> forumEntries) {
|
||||||
this.forumEntries = forumEntries;
|
this.forumEntries = forumEntries;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ import java.util.Comparator;
|
|||||||
public interface MessageTree<T extends MessageTree.MessageNode> {
|
public interface MessageTree<T extends MessageTree.MessageNode> {
|
||||||
|
|
||||||
void add(Collection<T> nodes);
|
void add(Collection<T> nodes);
|
||||||
|
void add(T node);
|
||||||
|
void setComparator(Comparator<T> comparator);
|
||||||
void clear();
|
void clear();
|
||||||
Collection<T> depthFirstOrder();
|
Collection<T> depthFirstOrder();
|
||||||
void setComparator(Comparator<T> comparator);
|
|
||||||
|
|
||||||
interface MessageNode {
|
interface MessageNode {
|
||||||
MessageId getId();
|
MessageId getId();
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package org.briarproject.api.event;
|
||||||
|
|
||||||
|
import org.briarproject.api.forum.ForumPostHeader;
|
||||||
|
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||||
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event that is broadcast when a new forum post was received.
|
||||||
|
*/
|
||||||
|
public class ForumPostReceivedEvent extends Event {
|
||||||
|
|
||||||
|
private final ForumPostHeader forumPostHeader;
|
||||||
|
private final GroupId groupId;
|
||||||
|
|
||||||
|
public ForumPostReceivedEvent(ForumPostHeader forumPostHeader,
|
||||||
|
GroupId groupId) {
|
||||||
|
|
||||||
|
this.forumPostHeader = forumPostHeader;
|
||||||
|
this.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ForumPostHeader getForumPostHeader() {
|
||||||
|
return forumPostHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupId getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,8 +14,9 @@ import java.util.Map;
|
|||||||
public class MessageTreeImpl<T extends MessageTree.MessageNode>
|
public class MessageTreeImpl<T extends MessageTree.MessageNode>
|
||||||
implements MessageTree<T> {
|
implements MessageTree<T> {
|
||||||
|
|
||||||
Map<MessageId, List<T>> nodeMap = new HashMap<MessageId, List<T>>();
|
private final Map<MessageId, List<T>> nodeMap = new HashMap<MessageId, List<T>>();
|
||||||
List<T> roots = new ArrayList<T>();
|
private final List<T> roots = new ArrayList<T>();
|
||||||
|
private final List<List<T>> unsortedLists = new ArrayList<List<T>>();
|
||||||
|
|
||||||
private Comparator<T> comparator = new Comparator<T>() {
|
private Comparator<T> comparator = new Comparator<T>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -38,26 +39,44 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
|
|||||||
}
|
}
|
||||||
// parse the nodes for dependencies
|
// parse the nodes for dependencies
|
||||||
for (T node : nodes) {
|
for (T node : nodes) {
|
||||||
if (node.getParentId() == null) {
|
parseNode(node);
|
||||||
roots.add(node);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// retrieve the parent's children
|
|
||||||
List<T> pChildren = nodeMap.get(node.getParentId());
|
|
||||||
pChildren.add(node);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sortAll();
|
sortUnsorted();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sortAll() {
|
@Override
|
||||||
Collections.sort(roots, comparator);
|
public void add(T node) {
|
||||||
// Sort all the sub-lists
|
add(Collections.singletonList(node));
|
||||||
for (Map.Entry<MessageId, List<T>> entry: nodeMap.entrySet()) {
|
}
|
||||||
Collections.sort(entry.getValue(), comparator);
|
|
||||||
|
private void markAsUnsorted(List<T> list) {
|
||||||
|
if (!unsortedLists.contains(list))
|
||||||
|
unsortedLists.add(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseNode(T node) {
|
||||||
|
if (node.getParentId() == null) {
|
||||||
|
roots.add(node);
|
||||||
|
markAsUnsorted(roots);
|
||||||
|
} else {
|
||||||
|
// retrieve the parent's children
|
||||||
|
List<T> pChildren = nodeMap.get(node.getParentId());
|
||||||
|
pChildren.add(node);
|
||||||
|
markAsUnsorted(pChildren);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sortUnsorted() {
|
||||||
|
// leave unsorted if there is no comparator
|
||||||
|
if (comparator != null) {
|
||||||
|
for (List<T> list : unsortedLists) {
|
||||||
|
Collections.sort(list, comparator);
|
||||||
|
}
|
||||||
|
unsortedLists.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void traverse(List<T> list, T node) {
|
private void traverse(List<T> list, T node) {
|
||||||
list.add(node);
|
list.add(node);
|
||||||
for (T child : nodeMap.get(node.getId())) {
|
for (T child : nodeMap.get(node.getId())) {
|
||||||
@@ -65,6 +84,16 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setComparator(Comparator<T> comparator) {
|
||||||
|
this.comparator = comparator;
|
||||||
|
// Sort all lists with the new comparator
|
||||||
|
Collections.sort(roots, comparator);
|
||||||
|
for (Map.Entry<MessageId, List<T>> entry: nodeMap.entrySet()) {
|
||||||
|
Collections.sort(entry.getValue(), comparator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<T> depthFirstOrder() {
|
public Collection<T> depthFirstOrder() {
|
||||||
List<T> orderedList = new ArrayList<T>();
|
List<T> orderedList = new ArrayList<T>();
|
||||||
@@ -74,9 +103,4 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
|
|||||||
return Collections.unmodifiableList(orderedList);
|
return Collections.unmodifiableList(orderedList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setComparator(Comparator<T> comparator) {
|
|
||||||
this.comparator = comparator;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import org.briarproject.api.FormatException;
|
|||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
import org.briarproject.api.data.BdfDictionary;
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
import org.briarproject.api.data.BdfList;
|
import org.briarproject.api.data.BdfList;
|
||||||
|
import org.briarproject.api.data.MetadataParser;
|
||||||
import org.briarproject.api.db.DatabaseComponent;
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.db.Transaction;
|
import org.briarproject.api.db.Transaction;
|
||||||
|
import org.briarproject.api.event.ForumPostReceivedEvent;
|
||||||
import org.briarproject.api.forum.Forum;
|
import org.briarproject.api.forum.Forum;
|
||||||
import org.briarproject.api.forum.ForumFactory;
|
import org.briarproject.api.forum.ForumFactory;
|
||||||
import org.briarproject.api.forum.ForumManager;
|
import org.briarproject.api.forum.ForumManager;
|
||||||
@@ -19,7 +21,9 @@ import org.briarproject.api.identity.IdentityManager;
|
|||||||
import org.briarproject.api.sync.ClientId;
|
import org.briarproject.api.sync.ClientId;
|
||||||
import org.briarproject.api.sync.Group;
|
import org.briarproject.api.sync.Group;
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
import org.briarproject.api.sync.Message;
|
||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
|
import org.briarproject.clients.BdfIncomingMessageHook;
|
||||||
import org.briarproject.util.StringUtils;
|
import org.briarproject.util.StringUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -44,7 +48,7 @@ import static org.briarproject.api.forum.ForumConstants.KEY_READ;
|
|||||||
import static org.briarproject.api.forum.ForumConstants.KEY_TIMESTAMP;
|
import static org.briarproject.api.forum.ForumConstants.KEY_TIMESTAMP;
|
||||||
import static org.briarproject.api.identity.Author.Status.ANONYMOUS;
|
import static org.briarproject.api.identity.Author.Status.ANONYMOUS;
|
||||||
|
|
||||||
class ForumManagerImpl implements ForumManager {
|
class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(ForumManagerImpl.class.getName());
|
Logger.getLogger(ForumManagerImpl.class.getName());
|
||||||
@@ -55,21 +59,31 @@ class ForumManagerImpl implements ForumManager {
|
|||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final IdentityManager identityManager;
|
private final IdentityManager identityManager;
|
||||||
private final ClientHelper clientHelper;
|
|
||||||
private final ForumFactory forumFactory;
|
private final ForumFactory forumFactory;
|
||||||
private final List<RemoveForumHook> removeHooks;
|
private final List<RemoveForumHook> removeHooks;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ForumManagerImpl(DatabaseComponent db, IdentityManager identityManager,
|
ForumManagerImpl(DatabaseComponent db, IdentityManager identityManager,
|
||||||
ClientHelper clientHelper, ForumFactory forumFactory) {
|
ClientHelper clientHelper, MetadataParser metadataParser,
|
||||||
|
ForumFactory forumFactory) {
|
||||||
|
|
||||||
|
super(clientHelper, metadataParser);
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.identityManager = identityManager;
|
this.identityManager = identityManager;
|
||||||
this.clientHelper = clientHelper;
|
|
||||||
this.forumFactory = forumFactory;
|
this.forumFactory = forumFactory;
|
||||||
removeHooks = new CopyOnWriteArrayList<RemoveForumHook>();
|
removeHooks = new CopyOnWriteArrayList<RemoveForumHook>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void incomingMessage(Transaction txn, Message m, BdfList body,
|
||||||
|
BdfDictionary meta) throws DbException, FormatException {
|
||||||
|
|
||||||
|
ForumPostHeader post = getForumPostHeader(txn, m.getId(), meta);
|
||||||
|
ForumPostReceivedEvent event =
|
||||||
|
new ForumPostReceivedEvent(post, m.getGroupId());
|
||||||
|
txn.attach(event);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientId getClientId() {
|
public ClientId getClientId() {
|
||||||
return CLIENT_ID;
|
return CLIENT_ID;
|
||||||
@@ -193,25 +207,7 @@ class ForumManagerImpl implements ForumManager {
|
|||||||
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
|
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
|
||||||
try {
|
try {
|
||||||
BdfDictionary meta = entry.getValue();
|
BdfDictionary meta = entry.getValue();
|
||||||
long timestamp = meta.getLong(KEY_TIMESTAMP);
|
headers.add(getForumPostHeader(entry.getKey(), meta));
|
||||||
Author author = null;
|
|
||||||
Status authorStatus = ANONYMOUS;
|
|
||||||
MessageId parentId = null;
|
|
||||||
if (meta.containsKey(KEY_PARENT))
|
|
||||||
parentId = new MessageId(meta.getRaw(KEY_PARENT));
|
|
||||||
BdfDictionary d1 = meta.getDictionary(KEY_AUTHOR, null);
|
|
||||||
if (d1 != null) {
|
|
||||||
AuthorId authorId = new AuthorId(d1.getRaw(KEY_ID));
|
|
||||||
String name = d1.getString(KEY_NAME);
|
|
||||||
byte[] publicKey = d1.getRaw(KEY_PUBLIC_NAME);
|
|
||||||
author = new Author(authorId, name, publicKey);
|
|
||||||
authorStatus =
|
|
||||||
identityManager.getAuthorStatus(author.getId());
|
|
||||||
}
|
|
||||||
String contentType = meta.getString(KEY_CONTENT_TYPE);
|
|
||||||
boolean read = meta.getBoolean(KEY_READ);
|
|
||||||
headers.add(new ForumPostHeader(entry.getKey(), parentId,
|
|
||||||
timestamp, author, authorStatus, contentType, read));
|
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
@@ -242,4 +238,39 @@ class ForumManagerImpl implements ForumManager {
|
|||||||
return new Forum(g, forum.getString(0), forum.getRaw(1));
|
return new Forum(g, forum.getString(0), forum.getRaw(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ForumPostHeader getForumPostHeader(Transaction txn, MessageId id,
|
||||||
|
BdfDictionary meta) throws DbException, FormatException {
|
||||||
|
|
||||||
|
long timestamp = meta.getLong(KEY_TIMESTAMP);
|
||||||
|
Author author = null;
|
||||||
|
Status authorStatus = ANONYMOUS;
|
||||||
|
MessageId parentId = null;
|
||||||
|
if (meta.containsKey(KEY_PARENT))
|
||||||
|
parentId = new MessageId(meta.getRaw(KEY_PARENT));
|
||||||
|
BdfDictionary d1 = meta.getDictionary(KEY_AUTHOR, null);
|
||||||
|
if (d1 != null) {
|
||||||
|
AuthorId authorId = new AuthorId(d1.getRaw(KEY_ID));
|
||||||
|
String name = d1.getString(KEY_NAME);
|
||||||
|
byte[] publicKey = d1.getRaw(KEY_PUBLIC_NAME);
|
||||||
|
author = new Author(authorId, name, publicKey);
|
||||||
|
if (txn == null) {
|
||||||
|
authorStatus = identityManager.getAuthorStatus(author.getId());
|
||||||
|
} else {
|
||||||
|
authorStatus =
|
||||||
|
identityManager.getAuthorStatus(txn, author.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String contentType = meta.getString(KEY_CONTENT_TYPE);
|
||||||
|
boolean read = meta.getBoolean(KEY_READ);
|
||||||
|
|
||||||
|
return new ForumPostHeader(id, parentId, timestamp, author,
|
||||||
|
authorStatus, contentType, read);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ForumPostHeader getForumPostHeader(MessageId id,
|
||||||
|
BdfDictionary meta) throws DbException, FormatException {
|
||||||
|
|
||||||
|
return getForumPostHeader(null, id, meta);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,13 @@ public class ForumModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
ForumManager provideForumManager(ForumManagerImpl forumManager) {
|
ForumManager provideForumManager(ForumManagerImpl forumManager,
|
||||||
|
ValidationManager validationManager) {
|
||||||
|
|
||||||
|
validationManager
|
||||||
|
.registerIncomingMessageHook(forumManager.getClientId(),
|
||||||
|
forumManager);
|
||||||
|
|
||||||
return forumManager;
|
return forumManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user