Merge branch '498-implement-ui-for-sharing-blogs' into 'master'

UI for sharing blogs

Not posting any screenshots, because the UI is the same as for forums.

This does not yet offer the possibility to unsubscribe from blogs again. Should be done in a different MR as this one is big enough already.

Closes #498,  #497

See merge request !263
This commit is contained in:
Torsten Grote
2016-08-03 21:47:02 +00:00
70 changed files with 2185 additions and 648 deletions

View File

@@ -0,0 +1,658 @@
package org.briarproject;
import net.jodah.concurrentunit.Waiter;
import org.briarproject.api.blogs.Blog;
import org.briarproject.api.blogs.BlogInvitationRequest;
import org.briarproject.api.blogs.BlogInvitationResponse;
import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogPostFactory;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyPair;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DbException;
import org.briarproject.api.event.BlogInvitationReceivedEvent;
import org.briarproject.api.event.BlogInvitationResponseReceivedEvent;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sharing.InvitationMessage;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.SyncSession;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.api.sync.ValidationManager.State;
import org.briarproject.api.system.Clock;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.transport.TransportModule;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import javax.inject.Inject;
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class BlogSharingIntegrationTest extends BriarTestCase {
private LifecycleManager lifecycleManager0, lifecycleManager1,
lifecycleManager2;
private SyncSessionFactory sync0, sync1, sync2;
private BlogManager blogManager0, blogManager1;
private ContactManager contactManager0, contactManager1, contactManager2;
private Contact contact1, contact2, contact01, contact02;
private ContactId contactId1, contactId2, contactId01, contactId02;
private IdentityManager identityManager0, identityManager1,
identityManager2;
private LocalAuthor author0, author1, author2;
private Blog blog0, blog1, blog2;
private SharerListener listener0, listener2;
private InviteeListener listener1;
@Inject
Clock clock;
@Inject
AuthorFactory authorFactory;
@Inject
BlogPostFactory blogPostFactory;
@Inject
CryptoComponent cryptoComponent;
// objects accessed from background threads need to be volatile
private volatile BlogSharingManager blogSharingManager0;
private volatile BlogSharingManager blogSharingManager1;
private volatile BlogSharingManager blogSharingManager2;
private volatile Waiter eventWaiter;
private volatile Waiter msgWaiter;
private final File testDir = TestUtils.getTestDirectory();
private final SecretKey master = TestUtils.getSecretKey();
private final int TIMEOUT = 15000;
private final String SHARER = "Sharer";
private final String INVITEE = "Invitee";
private final String CONTACT2 = "Contact2";
private static final Logger LOG =
Logger.getLogger(BlogSharingIntegrationTest.class.getName());
private BlogSharingIntegrationTestComponent t0, t1, t2;
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before
public void setUp() {
BlogSharingIntegrationTestComponent component =
DaggerBlogSharingIntegrationTestComponent.builder().build();
component.inject(this);
injectEagerSingletons(component);
assertTrue(testDir.mkdirs());
File t0Dir = new File(testDir, SHARER);
t0 = DaggerBlogSharingIntegrationTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
injectEagerSingletons(t0);
File t1Dir = new File(testDir, INVITEE);
t1 = DaggerBlogSharingIntegrationTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
injectEagerSingletons(t1);
File t2Dir = new File(testDir, CONTACT2);
t2 = DaggerBlogSharingIntegrationTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t2Dir)).build();
injectEagerSingletons(t2);
identityManager0 = t0.getIdentityManager();
identityManager1 = t1.getIdentityManager();
identityManager2 = t2.getIdentityManager();
contactManager0 = t0.getContactManager();
contactManager1 = t1.getContactManager();
contactManager2 = t2.getContactManager();
blogManager0 = t0.getBlogManager();
blogManager1 = t1.getBlogManager();
blogSharingManager0 = t0.getBlogSharingManager();
blogSharingManager1 = t1.getBlogSharingManager();
blogSharingManager2 = t2.getBlogSharingManager();
sync0 = t0.getSyncSessionFactory();
sync1 = t1.getSyncSessionFactory();
sync2 = t2.getSyncSessionFactory();
// initialize waiters fresh for each test
eventWaiter = new Waiter();
msgWaiter = new Waiter();
}
@Test
public void testPersonalBlogCannotBeSharedWithOwner() throws Exception {
startLifecycles();
defaultInit(true);
assertFalse(blogSharingManager0.canBeShared(blog1.getId(), contact1));
assertFalse(blogSharingManager0.canBeShared(blog2.getId(), contact2));
assertFalse(blogSharingManager1.canBeShared(blog0.getId(), contact01));
assertFalse(blogSharingManager2.canBeShared(blog0.getId(), contact02));
// create invitation
blogSharingManager0
.sendInvitation(blog1.getId(), contactId1, "Hi!");
// sync invitation
sync0To1();
// make sure the invitee ignored the request for their own blog
assertFalse(listener1.requestReceived);
stopLifecycles();
}
@Test
public void testSuccessfulSharing() throws Exception {
startLifecycles();
// initialize and let invitee accept all requests
defaultInit(true);
// send invitation
blogSharingManager0
.sendInvitation(blog2.getId(), contactId1, "Hi!");
// invitee has own blog and that of the sharer
assertEquals(2, blogManager1.getBlogs().size());
// sync first request message
sync0To1();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// sync response back
sync1To0();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// blog was added successfully
assertEquals(0, blogSharingManager0.getInvited().size());
assertEquals(3, blogManager1.getBlogs().size());
// invitee has one invitation message from sharer
List<InvitationMessage> list =
new ArrayList<>(blogSharingManager1
.getInvitationMessages(contactId01));
assertEquals(2, list.size());
// check other things are alright with the message
for (InvitationMessage m : list) {
if (m instanceof BlogInvitationRequest) {
BlogInvitationRequest invitation =
(BlogInvitationRequest) m;
assertFalse(invitation.isAvailable());
assertEquals(blog2.getAuthor().getName(),
invitation.getBlogAuthorName());
assertEquals(contactId1, invitation.getContactId());
assertEquals("Hi!", invitation.getMessage());
} else {
BlogInvitationResponse response =
(BlogInvitationResponse) m;
assertEquals(contactId01, response.getContactId());
assertTrue(response.wasAccepted());
assertTrue(response.isLocal());
}
}
// sharer has own invitation message and response
assertEquals(2,
blogSharingManager0.getInvitationMessages(contactId1)
.size());
// blog can not be shared again
assertFalse(blogSharingManager0.canBeShared(blog2.getId(), contact1));
assertFalse(blogSharingManager1.canBeShared(blog2.getId(), contact01));
stopLifecycles();
}
@Test
public void testDeclinedSharing() throws Exception {
startLifecycles();
// initialize and let invitee deny all requests
defaultInit(false);
// send invitation
blogSharingManager0
.sendInvitation(blog2.getId(), contactId1, null);
// sync first request message
sync0To1();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// sync response back
sync1To0();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// blog was not added
assertEquals(0, blogSharingManager0.getInvited().size());
assertEquals(2, blogManager1.getBlogs().size());
// blog is no longer available to invitee who declined
assertEquals(0, blogSharingManager1.getInvited().size());
// invitee has one invitation message from sharer and one response
List<InvitationMessage> list =
new ArrayList<>(blogSharingManager1
.getInvitationMessages(contactId01));
assertEquals(2, list.size());
// check things are alright with the message
for (InvitationMessage m : list) {
if (m instanceof BlogInvitationRequest) {
BlogInvitationRequest invitation =
(BlogInvitationRequest) m;
assertFalse(invitation.isAvailable());
assertEquals(blog2.getAuthor().getName(),
invitation.getBlogAuthorName());
assertEquals(contactId1, invitation.getContactId());
assertEquals(null, invitation.getMessage());
} else {
BlogInvitationResponse response =
(BlogInvitationResponse) m;
assertEquals(contactId01, response.getContactId());
assertFalse(response.wasAccepted());
assertTrue(response.isLocal());
}
}
// sharer has own invitation message and response
assertEquals(2,
blogSharingManager0.getInvitationMessages(contactId1)
.size());
// blog can be shared again
assertTrue(blogSharingManager0.canBeShared(blog2.getId(), contact1));
stopLifecycles();
}
@Test
public void testInviteeLeavesAfterFinished() throws Exception {
startLifecycles();
// initialize and let invitee accept all requests
defaultInit(true);
// send invitation
blogSharingManager0
.sendInvitation(blog2.getId(), contactId1, "Hi!");
// sync first request message
sync0To1();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// sync response back
sync1To0();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// blog was added successfully
assertEquals(0, blogSharingManager0.getInvited().size());
assertEquals(3, blogManager1.getBlogs().size());
assertTrue(blogManager1.getBlogs().contains(blog2));
// sharer shares blog with invitee
assertTrue(blogSharingManager0.getSharedWith(blog2.getId())
.contains(contact1));
// invitee gets blog shared by sharer
assertTrue(blogSharingManager1.getSharedBy(blog2.getId())
.contains(contact01));
// invitee un-subscribes from blog
blogManager1.removeBlog(blog2);
// send leave message to sharer
sync1To0();
// blog is gone
assertEquals(0, blogSharingManager0.getInvited().size());
assertEquals(2, blogManager1.getBlogs().size());
// sharer no longer shares blog with invitee
assertFalse(blogSharingManager0.getSharedWith(blog2.getId())
.contains(contact1));
// invitee no longer gets blog shared by sharer
assertFalse(blogSharingManager1.getSharedBy(blog2.getId())
.contains(contact01));
// blog can be shared again
assertTrue(blogSharingManager0.canBeShared(blog2.getId(), contact1));
assertTrue(blogSharingManager1.canBeShared(blog2.getId(), contact01));
stopLifecycles();
}
@Test
public void testInvitationForExistingBlog() throws Exception {
startLifecycles();
// initialize and let invitee accept all requests
defaultInit(true);
// 1 and 2 are adding each other
contactManager1.addContact(author2,
author1.getId(), master, clock.currentTimeMillis(), true,
true
);
contactManager2.addContact(author1,
author2.getId(), master, clock.currentTimeMillis(), true,
true
);
assertEquals(3, blogManager1.getBlogs().size());
// sharer sends invitation for 2's blog to 1
blogSharingManager0
.sendInvitation(blog2.getId(), contactId1, "Hi!");
// sync first request message
sync0To1();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// make sure blog2 is shared by 0
Collection<Contact> contacts =
blogSharingManager1.getSharedBy(blog2.getId());
assertEquals(1, contacts.size());
assertTrue(contacts.contains(contact01));
// make sure 1 knows that they have blog2 already
Collection<InvitationMessage> messages =
blogSharingManager1.getInvitationMessages(contactId01);
assertEquals(2, messages.size());
assertEquals(blog2, blogManager1.getBlog(blog2.getId()));
// sync response back
sync1To0();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// blog was not added, because it was there already
assertEquals(0, blogSharingManager0.getInvited().size());
assertEquals(3, blogManager1.getBlogs().size());
stopLifecycles();
}
@After
public void tearDown() throws InterruptedException {
TestUtils.deleteTestDirectory(testDir);
}
private class SharerListener implements EventListener {
volatile boolean requestReceived = false;
volatile boolean responseReceived = false;
public void eventOccurred(Event e) {
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
State s = event.getState();
ClientId c = event.getClientId();
if ((s == DELIVERED || s == INVALID) &&
c.equals(blogSharingManager0.getClientId()) &&
!event.isLocal()) {
LOG.info("TEST: Sharer received message in group " +
event.getMessage().getGroupId().hashCode());
msgWaiter.resume();
} else if (s == DELIVERED && !event.isLocal() &&
c.equals(blogManager0.getClientId())) {
LOG.info("TEST: Sharer received blog post");
msgWaiter.resume();
}
} else if (e instanceof BlogInvitationResponseReceivedEvent) {
BlogInvitationResponseReceivedEvent event =
(BlogInvitationResponseReceivedEvent) e;
eventWaiter.assertEquals(contactId1, event.getContactId());
responseReceived = true;
eventWaiter.resume();
}
// this is only needed for tests where a blog is re-shared
else if (e instanceof BlogInvitationReceivedEvent) {
BlogInvitationReceivedEvent event =
(BlogInvitationReceivedEvent) e;
eventWaiter.assertEquals(contactId1, event.getContactId());
requestReceived = true;
Blog b = event.getBlog();
try {
Contact c = contactManager0.getContact(contactId1);
blogSharingManager0.respondToInvitation(b, c, true);
} catch (DbException ex) {
eventWaiter.rethrow(ex);
} finally {
eventWaiter.resume();
}
}
}
}
private class InviteeListener implements EventListener {
volatile boolean requestReceived = false;
volatile boolean responseReceived = false;
private final boolean accept, answer;
InviteeListener(boolean accept, boolean answer) {
this.accept = accept;
this.answer = answer;
}
InviteeListener(boolean accept) {
this(accept, true);
}
public void eventOccurred(Event e) {
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
State s = event.getState();
ClientId c = event.getClientId();
if ((s == DELIVERED || s == INVALID) &&
c.equals(blogSharingManager0.getClientId()) &&
!event.isLocal()) {
LOG.info("TEST: Invitee received message in group " +
event.getMessage().getGroupId().hashCode());
msgWaiter.resume();
} else if (s == DELIVERED && !event.isLocal() &&
c.equals(blogManager0.getClientId())) {
LOG.info("TEST: Invitee received blog post");
msgWaiter.resume();
}
} else if (e instanceof BlogInvitationReceivedEvent) {
BlogInvitationReceivedEvent event =
(BlogInvitationReceivedEvent) e;
requestReceived = true;
if (!answer) return;
Blog b = event.getBlog();
try {
eventWaiter.assertEquals(1,
blogSharingManager1.getInvited().size());
Contact c =
contactManager1.getContact(event.getContactId());
blogSharingManager1.respondToInvitation(b, c, accept);
} catch (DbException ex) {
eventWaiter.rethrow(ex);
} finally {
eventWaiter.resume();
}
}
// this is only needed for tests where a blog is re-shared
else if (e instanceof BlogInvitationResponseReceivedEvent) {
BlogInvitationResponseReceivedEvent event =
(BlogInvitationResponseReceivedEvent) e;
eventWaiter.assertEquals(contactId01, event.getContactId());
responseReceived = true;
eventWaiter.resume();
}
}
}
private void startLifecycles() throws InterruptedException {
// Start the lifecycle manager and wait for it to finish
lifecycleManager0 = t0.getLifecycleManager();
lifecycleManager1 = t1.getLifecycleManager();
lifecycleManager2 = t2.getLifecycleManager();
lifecycleManager0.startServices();
lifecycleManager1.startServices();
lifecycleManager2.startServices();
lifecycleManager0.waitForStartup();
lifecycleManager1.waitForStartup();
lifecycleManager2.waitForStartup();
}
private void stopLifecycles() throws InterruptedException {
// Clean up
lifecycleManager0.stopServices();
lifecycleManager1.stopServices();
lifecycleManager2.stopServices();
lifecycleManager0.waitForShutdown();
lifecycleManager1.waitForShutdown();
lifecycleManager2.waitForShutdown();
}
private void defaultInit(boolean accept) throws DbException {
addDefaultIdentities();
addDefaultContacts();
getPersonalBlogOfSharer();
listenToEvents(accept);
}
private void addDefaultIdentities() throws DbException {
KeyPair keyPair = cryptoComponent.generateSignatureKeyPair();
author0 = authorFactory.createLocalAuthor(SHARER,
keyPair.getPublic().getEncoded(),
keyPair.getPrivate().getEncoded());
identityManager0.addLocalAuthor(author0);
keyPair = cryptoComponent.generateSignatureKeyPair();
author1 = authorFactory.createLocalAuthor(INVITEE,
keyPair.getPublic().getEncoded(),
keyPair.getPrivate().getEncoded());
identityManager1.addLocalAuthor(author1);
keyPair = cryptoComponent.generateSignatureKeyPair();
author2 = authorFactory.createLocalAuthor(CONTACT2,
keyPair.getPublic().getEncoded(),
keyPair.getPrivate().getEncoded());
identityManager2.addLocalAuthor(author2);
}
private void addDefaultContacts() throws DbException {
// sharer adds invitee as contact
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true
);
contact1 = contactManager0.getContact(contactId1);
// sharer adds second contact
contactId2 = contactManager0.addContact(author2,
author0.getId(), master, clock.currentTimeMillis(), true,
true
);
contact2 = contactManager0.getContact(contactId2);
// contacts add sharer back
contactId01 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true
);
contact01 = contactManager1.getContact(contactId01);
contactId02 = contactManager2.addContact(author0,
author2.getId(), master, clock.currentTimeMillis(), true,
true
);
contact02 = contactManager2.getContact(contactId02);
}
private void getPersonalBlogOfSharer() throws DbException {
blog0 = blogManager0.getPersonalBlog(author0);
blog1 = blogManager0.getPersonalBlog(author1);
blog2 = blogManager0.getPersonalBlog(author2);
}
private void listenToEvents(boolean accept) {
listener0 = new SharerListener();
t0.getEventBus().addListener(listener0);
listener1 = new InviteeListener(accept);
t1.getEventBus().addListener(listener1);
listener2 = new SharerListener();
t2.getEventBus().addListener(listener2);
}
private void sync0To1() throws IOException, TimeoutException {
deliverMessage(sync0, contactId01, sync1, contactId1,
"Sharer to Invitee");
}
private void sync1To0() throws IOException, TimeoutException {
deliverMessage(sync1, contactId1, sync0, contactId01,
"Invitee to Sharer");
}
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
SyncSessionFactory toSync, ContactId toId, String debug)
throws IOException, TimeoutException {
if (debug != null) LOG.info("TEST: Sending message from " + debug);
ByteArrayOutputStream out = new ByteArrayOutputStream();
// Create an outgoing sync session
SyncSession sessionFrom =
fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out);
// Write whatever needs to be written
sessionFrom.run();
out.close();
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
// Create an incoming sync session
SyncSession sessionTo = toSync.createIncomingSession(fromId, in);
// Read whatever needs to be read
sessionTo.run();
in.close();
// wait for message to actually arrive
msgWaiter.await(TIMEOUT, 1);
}
private void injectEagerSingletons(
BlogSharingIntegrationTestComponent component) {
component.inject(new LifecycleModule.EagerSingletons());
component.inject(new BlogsModule.EagerSingletons());
component.inject(new CryptoModule.EagerSingletons());
component.inject(new ContactModule.EagerSingletons());
component.inject(new TransportModule.EagerSingletons());
component.inject(new SharingModule.EagerSingletons());
component.inject(new SyncModule.EagerSingletons());
component.inject(new PropertiesModule.EagerSingletons());
}
}

View File

@@ -0,0 +1,100 @@
package org.briarproject;
import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.PrivateGroupFactory;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.clients.ClientsModule;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.data.DataModule;
import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule;
import org.briarproject.forum.ForumModule;
import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = {
TestDatabaseModule.class,
TestPluginsModule.class,
TestSeedProviderModule.class,
ClientsModule.class,
ContactModule.class,
CryptoModule.class,
DataModule.class,
DatabaseModule.class,
EventModule.class,
BlogsModule.class,
ForumModule.class,
IdentityModule.class,
LifecycleModule.class,
PropertiesModule.class,
SharingModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class
})
interface BlogSharingIntegrationTestComponent {
void inject(BlogSharingIntegrationTest testCase);
void inject(ContactModule.EagerSingletons init);
void inject(CryptoModule.EagerSingletons init);
void inject(BlogsModule.EagerSingletons init);
void inject(LifecycleModule.EagerSingletons init);
void inject(PropertiesModule.EagerSingletons init);
void inject(SharingModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);
LifecycleManager getLifecycleManager();
EventBus getEventBus();
IdentityManager getIdentityManager();
ContactManager getContactManager();
BlogSharingManager getBlogSharingManager();
BlogManager getBlogManager();
SyncSessionFactory getSyncSessionFactory();
/* the following methods are only needed to manually construct messages */
DatabaseComponent getDatabaseComponent();
PrivateGroupFactory getPrivateGroupFactory();
ClientHelper getClientHelper();
MessageQueueManager getMessageQueueManager();
}

View File

@@ -7,6 +7,7 @@ import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.SyncSessionFactory; import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.clients.ClientsModule; import org.briarproject.clients.ClientsModule;
import org.briarproject.contact.ContactModule; import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule; import org.briarproject.crypto.CryptoModule;
@@ -38,6 +39,7 @@ import dagger.Component;
DatabaseModule.class, DatabaseModule.class,
EventModule.class, EventModule.class,
ForumModule.class, ForumModule.class,
BlogsModule.class,
IdentityModule.class, IdentityModule.class,
LifecycleModule.class, LifecycleModule.class,
PropertiesModule.class, PropertiesModule.class,
@@ -46,7 +48,7 @@ import dagger.Component;
SystemModule.class, SystemModule.class,
TransportModule.class TransportModule.class
}) })
public interface ForumManagerTestComponent { interface ForumManagerTestComponent {
void inject(ForumManagerTest testCase); void inject(ForumManagerTest testCase);

View File

@@ -11,6 +11,7 @@ import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.SyncSessionFactory; import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.clients.ClientsModule; import org.briarproject.clients.ClientsModule;
import org.briarproject.contact.ContactModule; import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule; import org.briarproject.crypto.CryptoModule;
@@ -42,6 +43,7 @@ import dagger.Component;
DatabaseModule.class, DatabaseModule.class,
EventModule.class, EventModule.class,
ForumModule.class, ForumModule.class,
BlogsModule.class,
IdentityModule.class, IdentityModule.class,
LifecycleModule.class, LifecycleModule.class,
PropertiesModule.class, PropertiesModule.class,
@@ -50,7 +52,7 @@ import dagger.Component;
SystemModule.class, SystemModule.class,
TransportModule.class TransportModule.class
}) })
public interface ForumSharingIntegrationTestComponent { interface ForumSharingIntegrationTestComponent {
void inject(ForumSharingIntegrationTest testCase); void inject(ForumSharingIntegrationTest testCase);

View File

@@ -102,7 +102,7 @@
</activity> </activity>
<activity <activity
android:name=".android.forum.ForumInvitationsActivity" android:name=".android.sharing.InvitationsForumActivity"
android:label="@string/forum_invitations_title" android:label="@string/forum_invitations_title"
android:parentActivityName=".android.NavDrawerActivity"> android:parentActivityName=".android.NavDrawerActivity">
<meta-data <meta-data
@@ -111,6 +111,16 @@
/> />
</activity> </activity>
<activity
android:name=".android.sharing.InvitationsBlogActivity"
android:label="@string/blogs_sharing_invitations_title"
android:parentActivityName=".android.contact.ConversationActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.contact.ConversationActivity"
/>
</activity>
<activity <activity
android:name=".android.forum.CreateForumActivity" android:name=".android.forum.CreateForumActivity"
android:label="@string/create_forum_title" android:label="@string/create_forum_title"
@@ -133,8 +143,8 @@
</activity> </activity>
<activity <activity
android:name=".android.forum.ShareForumActivity" android:name=".android.sharing.ShareForumActivity"
android:label="@string/forums_share_toolbar_header" android:label="@string/activity_share_toolbar_header"
android:parentActivityName=".android.forum.ForumActivity"> android:parentActivityName=".android.forum.ForumActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
@@ -143,8 +153,18 @@
</activity> </activity>
<activity <activity
android:name=".android.forum.ForumSharingStatusActivity" android:name=".android.sharing.ShareBlogActivity"
android:label="@string/forum_sharing_status" android:label="@string/activity_share_toolbar_header"
android:parentActivityName=".android.blogs.BlogActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blogs.BlogActivity"
/>
</activity>
<activity
android:name=".android.sharing.SharingStatusForumActivity"
android:label="@string/sharing_status"
android:parentActivityName=".android.forum.ForumActivity"> android:parentActivityName=".android.forum.ForumActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
@@ -152,6 +172,16 @@
/> />
</activity> </activity>
<activity
android:name=".android.sharing.SharingStatusBlogActivity"
android:label="@string/sharing_status"
android:parentActivityName=".android.blogs.BlogActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blogs.BlogActivity"
/>
</activity>
<activity <activity
android:name=".android.blogs.CreateBlogActivity" android:name=".android.blogs.CreateBlogActivity"
android:label="@string/blogs_my_blogs_label" android:label="@string/blogs_my_blogs_label"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<org.briarproject.android.util.BriarRecyclerView <org.briarproject.android.util.BriarRecyclerView
android:id="@+id/availableForumsView" android:id="@+id/invitationsView"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent"/>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout <FrameLayout
android:id="@+id/shareForumContainer" android:id="@+id/shareContainer"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent"/>

View File

@@ -1,57 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
android:id="@+id/messageLayout"
layout="@layout/list_item_msg_out"/>
<RelativeLayout
android:id="@+id/introductionLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|end"
android:background="@drawable/notice_out"
android:layout_marginLeft="@dimen/message_bubble_margin_non_tail"
android:layout_marginRight="@dimen/message_bubble_margin_tail">
<TextView
android:id="@+id/introductionText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:textSize="@dimen/text_size_medium"
android:textStyle="italic"
android:textColor="@color/briar_text_secondary"
tools:text="@string/introduction_request_received"/>
<TextView
android:id="@+id/introductionTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/introductionText"
android:textColor="@color/private_message_date"
android:textSize="@dimen/text_size_tiny"
tools:text="Dec 24, 13:37"/>
<ImageView
android:id="@+id/introductionStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/introductionTime"
android:layout_toRightOf="@+id/introductionTime"
android:layout_alignBottom="@+id/introductionTime"
android:layout_marginLeft="@dimen/margin_medium"
tools:ignore="ContentDescription"
tools:src="@drawable/message_delivered"/>
</RelativeLayout>
</LinearLayout>

View File

@@ -37,13 +37,13 @@
android:layout_marginTop="@dimen/message_bubble_timestamp_margin" android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
android:layout_alignEnd="@+id/introductionText" android:layout_alignEnd="@+id/introductionText"
android:layout_alignRight="@+id/introductionText" android:layout_alignRight="@+id/introductionText"
android:layout_below="@+id/showForumsButton" android:layout_below="@+id/showInvitationsButton"
android:textColor="@color/private_message_date" android:textColor="@color/private_message_date"
android:textSize="@dimen/text_size_tiny" android:textSize="@dimen/text_size_tiny"
tools:text="Dec 24, 13:37"/> tools:text="Dec 24, 13:37"/>
<Button <Button
android:id="@+id/showForumsButton" android:id="@+id/showInvitationsButton"
style="@style/BriarButtonFlat.Positive" style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -51,7 +51,7 @@
android:layout_alignEnd="@+id/introductionText" android:layout_alignEnd="@+id/introductionText"
android:layout_alignRight="@+id/introductionText" android:layout_alignRight="@+id/introductionText"
android:layout_below="@+id/introductionText" android:layout_below="@+id/introductionText"
android:text="@string/forum_show_invitations"/> tools:text="@string/forum_show_invitations"/>
</RelativeLayout> </RelativeLayout>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_blog_share"
android:icon="@drawable/social_share_white"
android:title="@string/blogs_sharing_share"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_blog_sharing_status"
android:title="@string/sharing_status"
app:showAsAction="never"/>
</menu>

View File

@@ -17,7 +17,7 @@
<item <item
android:id="@+id/action_forum_sharing_status" android:id="@+id/action_forum_sharing_status"
android:title="@string/forum_sharing_status" android:title="@string/sharing_status"
app:showAsAction="never"/> app:showAsAction="never"/>
<item <item

View File

@@ -79,7 +79,7 @@
<string name="show_forums">Mostrar</string> <string name="show_forums">Mostrar</string>
<string name="forum_leave">Sair do fórum</string> <string name="forum_leave">Sair do fórum</string>
<string name="forum_left_toast">Saiu do fórum</string> <string name="forum_left_toast">Saiu do fórum</string>
<string name="forum_sharing_status">Status de compartilhamento</string> <string name="sharing_status">Status de compartilhamento</string>
<string name="no_posts">Sem Posts</string> <string name="no_posts">Sem Posts</string>
<plurals name="unread_posts"> <plurals name="unread_posts">
<item quantity="one">%d post não lido</item> <item quantity="one">%d post não lido</item>

View File

@@ -53,6 +53,7 @@
<string name="now">now</string> <string name="now">now</string>
<string name="contact_list_title">Contacts</string> <string name="contact_list_title">Contacts</string>
<string name="no_contacts">It seems that you are new here and have no contacts yet.\n\nTap the + icon at the top and follow the instructions to add some friends to your list.\n\nPlease remember: You can only add new contacts face-to-face to prevent anyone from impersonating you or reading your messages in the future.</string> <string name="no_contacts">It seems that you are new here and have no contacts yet.\n\nTap the + icon at the top and follow the instructions to add some friends to your list.\n\nPlease remember: You can only add new contacts face-to-face to prevent anyone from impersonating you or reading your messages in the future.</string>
<string name="no_contacts_selector">It seems that you are new here and have no contacts yet.\n\nPlease come back here after you added your first contact.</string>
<string name="add_contact_title">Add a Contact</string> <string name="add_contact_title">Add a Contact</string>
<string name="your_nickname">Choose the identity you want to use:</string> <string name="your_nickname">Choose the identity you want to use:</string>
<string name="face_to_face">You must be face-to-face with the person you want to add as a contact. This will prevent anyone from impersonating you or reading your messages in future.</string> <string name="face_to_face">You must be face-to-face with the person you want to add as a contact. This will prevent anyone from impersonating you or reading your messages in future.</string>
@@ -95,7 +96,7 @@
<string name="show_forums">Show</string> <string name="show_forums">Show</string>
<string name="forum_leave">Leave Forum</string> <string name="forum_leave">Leave Forum</string>
<string name="forum_left_toast">Left Forum</string> <string name="forum_left_toast">Left Forum</string>
<string name="forum_sharing_status">Sharing Status</string> <string name="sharing_status">Sharing Status</string>
<string name="no_posts">No posts</string> <string name="no_posts">No posts</string>
<plurals name="unread_posts"> <plurals name="unread_posts">
<item quantity="one">%d unread post</item> <item quantity="one">%d unread post</item>
@@ -113,6 +114,7 @@
<string name="forum_share_button">Share Forum</string> <string name="forum_share_button">Share Forum</string>
<string name="forum_shared_snackbar">Forum shared with chosen contacts</string> <string name="forum_shared_snackbar">Forum shared with chosen contacts</string>
<string name="forum_share_message">You may compose an optional invitation message that will be sent to the selected contacts.</string> <string name="forum_share_message">You may compose an optional invitation message that will be sent to the selected contacts.</string>
<string name="forum_share_error">There was an error sharing this forum.</string>
<string name="forum_invitation_received">%1$s has shared the forum \"%2$s\" with you.</string> <string name="forum_invitation_received">%1$s has shared the forum \"%2$s\" with you.</string>
<string name="forum_invitation_sent">You have shared the forum \"%1$s\" with %2$s.</string> <string name="forum_invitation_sent">You have shared the forum \"%1$s\" with %2$s.</string>
<string name="forum_show_invitations">Show Forum Invitations</string> <string name="forum_show_invitations">Show Forum Invitations</string>
@@ -265,7 +267,7 @@
<string name="settings_toolbar_header">Settings</string> <string name="settings_toolbar_header">Settings</string>
<string name="contacts_toolbar_header">Contacts</string> <string name="contacts_toolbar_header">Contacts</string>
<string name="forums_toolbar_header">Forums</string> <string name="forums_toolbar_header">Forums</string>
<string name="forums_share_toolbar_header">Choose Contacts</string> <string name="activity_share_toolbar_header">Choose Contacts</string>
<!-- Progress titles --> <!-- Progress titles -->
<string name="progress_title_logout">Signing out of Briar..</string> <string name="progress_title_logout">Signing out of Briar..</string>
<string name="progress_title_please_wait">Please wait..</string> <string name="progress_title_please_wait">Please wait..</string>
@@ -304,6 +306,21 @@
<string name="blogs_delete_blog_cancel">Keep</string> <string name="blogs_delete_blog_cancel">Keep</string>
<string name="blogs_blog_deleted">Blog Deleted</string> <string name="blogs_blog_deleted">Blog Deleted</string>
<string name="blogs_remove_blog">Remove Blog</string> <string name="blogs_remove_blog">Remove Blog</string>
<string name="blogs_sharing_share">Share Blog</string>
<string name="blogs_sharing_error">There was an error sharing this blog.</string>
<string name="blogs_sharing_button">Share Blog</string>
<string name="blogs_sharing_snackbar">Blog shared with chosen contacts</string>
<string name="blogs_sharing_response_accepted_sent">You accepted the blog invitation from %s.</string>
<string name="blogs_sharing_response_declined_sent">You declined the blog invitation from %s.</string>
<string name="blogs_sharing_response_accepted_received">%s accepted the blog invitation.</string>
<string name="blogs_sharing_response_declined_received">%s declined the blog invitation.</string>
<string name="blogs_sharing_invitation_received">%1$s has shared the personal blog of %2$s with you.</string>
<string name="blogs_sharing_invitation_sent">You have shared the personal blog of %1$s with %2$s.</string>
<string name="blogs_sharing_show_invitations">Show Blog Invitations</string>
<string name="blogs_sharing_invitations_title">Blog Invitations</string>
<string name="blogs_sharing_exists">You are subscribed to this blog already. Accepting again can lead to faster blog post delivery.</string>
<string name="blogs_sharing_joined_toast">Subscribed to Blog</string>
<string name="blogs_sharing_declined_toast">Blog Invitation Declined</string>
<string name="blogs_blog_list">Blog List</string> <string name="blogs_blog_list">Blog List</string>
<string name="blogs_available_blogs">Available Blogs</string> <string name="blogs_available_blogs">Available Blogs</string>

View File

@@ -15,14 +15,9 @@ import org.briarproject.android.blogs.RssFeedManageActivity;
import org.briarproject.android.blogs.WriteBlogPostActivity; import org.briarproject.android.blogs.WriteBlogPostActivity;
import org.briarproject.android.contact.ContactListFragment; import org.briarproject.android.contact.ContactListFragment;
import org.briarproject.android.contact.ConversationActivity; import org.briarproject.android.contact.ConversationActivity;
import org.briarproject.android.forum.ForumInvitationsActivity;
import org.briarproject.android.forum.ContactSelectorFragment;
import org.briarproject.android.forum.CreateForumActivity; import org.briarproject.android.forum.CreateForumActivity;
import org.briarproject.android.forum.ForumActivity; import org.briarproject.android.forum.ForumActivity;
import org.briarproject.android.forum.ForumListFragment; import org.briarproject.android.forum.ForumListFragment;
import org.briarproject.android.forum.ForumSharingStatusActivity;
import org.briarproject.android.forum.ShareForumActivity;
import org.briarproject.android.forum.ShareForumMessageFragment;
import org.briarproject.android.identity.CreateIdentityActivity; import org.briarproject.android.identity.CreateIdentityActivity;
import org.briarproject.android.introduction.ContactChooserFragment; import org.briarproject.android.introduction.ContactChooserFragment;
import org.briarproject.android.introduction.IntroductionActivity; import org.briarproject.android.introduction.IntroductionActivity;
@@ -33,6 +28,15 @@ import org.briarproject.android.keyagreement.KeyAgreementActivity;
import org.briarproject.android.keyagreement.ShowQrCodeFragment; import org.briarproject.android.keyagreement.ShowQrCodeFragment;
import org.briarproject.android.panic.PanicPreferencesActivity; import org.briarproject.android.panic.PanicPreferencesActivity;
import org.briarproject.android.panic.PanicResponderActivity; import org.briarproject.android.panic.PanicResponderActivity;
import org.briarproject.android.sharing.ContactSelectorFragment;
import org.briarproject.android.sharing.InvitationsBlogActivity;
import org.briarproject.android.sharing.InvitationsForumActivity;
import org.briarproject.android.sharing.ShareBlogActivity;
import org.briarproject.android.sharing.ShareBlogMessageFragment;
import org.briarproject.android.sharing.ShareForumActivity;
import org.briarproject.android.sharing.ShareForumMessageFragment;
import org.briarproject.android.sharing.SharingStatusBlogActivity;
import org.briarproject.android.sharing.SharingStatusForumActivity;
import dagger.Component; import dagger.Component;
@@ -63,13 +67,19 @@ public interface ActivityComponent {
void inject(CreateIdentityActivity activity); void inject(CreateIdentityActivity activity);
void inject(ForumInvitationsActivity activity); void inject(InvitationsForumActivity activity);
void inject(InvitationsBlogActivity activity);
void inject(CreateForumActivity activity); void inject(CreateForumActivity activity);
void inject(ShareForumActivity activity); void inject(ShareForumActivity activity);
void inject(ForumSharingStatusActivity activity); void inject(ShareBlogActivity activity);
void inject(SharingStatusForumActivity activity);
void inject(SharingStatusBlogActivity activity);
void inject(ForumActivity activity); void inject(ForumActivity activity);
@@ -105,6 +115,7 @@ public interface ActivityComponent {
void inject(ContactChooserFragment fragment); void inject(ContactChooserFragment fragment);
void inject(ContactSelectorFragment fragment); void inject(ContactSelectorFragment fragment);
void inject(ShareForumMessageFragment fragment); void inject(ShareForumMessageFragment fragment);
void inject(ShareBlogMessageFragment fragment);
void inject(IntroductionMessageFragment fragment); void inject(IntroductionMessageFragment fragment);
} }

View File

@@ -8,6 +8,7 @@ import org.briarproject.android.api.ReferenceManager;
import org.briarproject.android.report.BriarReportSender; import org.briarproject.android.report.BriarReportSender;
import org.briarproject.api.blogs.BlogManager; import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogPostFactory; import org.briarproject.api.blogs.BlogPostFactory;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.contact.ContactExchangeTask; import org.briarproject.api.contact.ContactExchangeTask;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoComponent;
@@ -93,6 +94,8 @@ public interface AndroidComponent extends CoreEagerSingletons {
ForumSharingManager forumSharingManager(); ForumSharingManager forumSharingManager();
BlogSharingManager blogSharingManager();
ForumPostFactory forumPostFactory(); ForumPostFactory forumPostFactory();
BlogManager blogManager(); BlogManager blogManager();

View File

@@ -21,11 +21,12 @@ import org.briarproject.api.db.DbException;
import org.briarproject.api.event.BlogPostAddedEvent; import org.briarproject.api.event.BlogPostAddedEvent;
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.ForumPostReceivedEvent; 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.InvitationReceivedEvent;
import org.briarproject.api.event.InvitationResponseReceivedEvent;
import org.briarproject.api.event.PrivateMessageReceivedEvent; import org.briarproject.api.event.PrivateMessageReceivedEvent;
import org.briarproject.api.event.SettingsUpdatedEvent; import org.briarproject.api.event.SettingsUpdatedEvent;
import org.briarproject.api.lifecycle.Service; import org.briarproject.api.lifecycle.Service;
@@ -174,8 +175,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} else if (e instanceof IntroductionSucceededEvent) { } else if (e instanceof IntroductionSucceededEvent) {
Contact c = ((IntroductionSucceededEvent) e).getContact(); Contact c = ((IntroductionSucceededEvent) e).getContact();
showIntroductionSucceededNotification(c); showIntroductionSucceededNotification(c);
} else if (e instanceof ForumInvitationReceivedEvent) { } else if (e instanceof InvitationReceivedEvent) {
ContactId c = ((ForumInvitationReceivedEvent) e).getContactId(); ContactId c = ((InvitationReceivedEvent) e).getContactId();
showNotificationForPrivateConversation(c);
} else if (e instanceof InvitationResponseReceivedEvent) {
ContactId c = ((InvitationResponseReceivedEvent) e).getContactId();
showNotificationForPrivateConversation(c); showNotificationForPrivateConversation(c);
} }
} }

View File

@@ -32,6 +32,7 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
OnBlogPostClickListener, BaseFragmentListener { OnBlogPostClickListener, BaseFragmentListener {
static final int REQUEST_WRITE_POST = 1; static final int REQUEST_WRITE_POST = 1;
static final int REQUEST_SHARE = 2;
static final String BLOG_NAME = "briar.BLOG_NAME"; static final String BLOG_NAME = "briar.BLOG_NAME";
static final String IS_MY_BLOG = "briar.IS_MY_BLOG"; static final String IS_MY_BLOG = "briar.IS_MY_BLOG";
static final String IS_NEW_BLOG = "briar.IS_NEW_BLOG"; static final String IS_NEW_BLOG = "briar.IS_NEW_BLOG";
@@ -185,6 +186,7 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, protected void onActivityResult(int requestCode, int resultCode,
Intent data) { Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// The BlogPostAddedEvent arrives when the controller is not listening, // The BlogPostAddedEvent arrives when the controller is not listening,
// so we need to manually reload the blog posts :( // so we need to manually reload the blog posts :(

View File

@@ -23,6 +23,8 @@ import org.briarproject.android.blogs.BlogController.BlogPostListener;
import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener; import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
import org.briarproject.android.controller.handler.UiResultHandler; import org.briarproject.android.controller.handler.UiResultHandler;
import org.briarproject.android.fragment.BaseFragment; import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.sharing.ShareBlogActivity;
import org.briarproject.android.sharing.SharingStatusBlogActivity;
import org.briarproject.android.util.BriarRecyclerView; import org.briarproject.android.util.BriarRecyclerView;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
@@ -30,6 +32,9 @@ import java.util.Collection;
import javax.inject.Inject; import javax.inject.Inject;
import static android.app.Activity.RESULT_OK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static android.support.design.widget.Snackbar.LENGTH_LONG; import static android.support.design.widget.Snackbar.LENGTH_LONG;
import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation; import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
import static android.widget.Toast.LENGTH_SHORT; import static android.widget.Toast.LENGTH_SHORT;
@@ -37,6 +42,7 @@ import static org.briarproject.android.BriarActivity.GROUP_ID;
import static org.briarproject.android.blogs.BlogActivity.BLOG_NAME; import static org.briarproject.android.blogs.BlogActivity.BLOG_NAME;
import static org.briarproject.android.blogs.BlogActivity.IS_MY_BLOG; import static org.briarproject.android.blogs.BlogActivity.IS_MY_BLOG;
import static org.briarproject.android.blogs.BlogActivity.IS_NEW_BLOG; import static org.briarproject.android.blogs.BlogActivity.IS_NEW_BLOG;
import static org.briarproject.android.blogs.BlogActivity.REQUEST_SHARE;
import static org.briarproject.android.blogs.BlogActivity.REQUEST_WRITE_POST; import static org.briarproject.android.blogs.BlogActivity.REQUEST_WRITE_POST;
public class BlogFragment extends BaseFragment implements BlogPostListener { public class BlogFragment extends BaseFragment implements BlogPostListener {
@@ -136,12 +142,18 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (myBlog) { if (myBlog) {
inflater.inflate(R.menu.blogs_my_blog_actions, menu); inflater.inflate(R.menu.blogs_my_blog_actions, menu);
} else {
inflater.inflate(R.menu.blogs_blog_actions, menu);
} }
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
} }
@Override @Override
public boolean onOptionsItemSelected(final MenuItem item) { public boolean onOptionsItemSelected(final MenuItem item) {
ActivityOptionsCompat options =
makeCustomAnimation(getActivity(),
android.R.anim.slide_in_left,
android.R.anim.slide_out_right);
switch (item.getItemId()) { switch (item.getItemId()) {
case android.R.id.home: case android.R.id.home:
getActivity().onBackPressed(); getActivity().onBackPressed();
@@ -151,18 +163,36 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
new Intent(getActivity(), WriteBlogPostActivity.class); new Intent(getActivity(), WriteBlogPostActivity.class);
i.putExtra(GROUP_ID, groupId.getBytes()); i.putExtra(GROUP_ID, groupId.getBytes());
i.putExtra(BLOG_NAME, blogName); i.putExtra(BLOG_NAME, blogName);
ActivityOptionsCompat options =
makeCustomAnimation(getActivity(),
android.R.anim.slide_in_left,
android.R.anim.slide_out_right);
ActivityCompat.startActivityForResult(getActivity(), i, ActivityCompat.startActivityForResult(getActivity(), i,
REQUEST_WRITE_POST, options.toBundle()); REQUEST_WRITE_POST, options.toBundle());
return true; return true;
case R.id.action_blog_share:
Intent i2 = new Intent(getActivity(), ShareBlogActivity.class);
i2.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
i2.putExtra(GROUP_ID, groupId.getBytes());
startActivityForResult(i2, REQUEST_SHARE, options.toBundle());
return true;
case R.id.action_blog_sharing_status:
Intent i3 = new Intent(getActivity(),
SharingStatusBlogActivity.class);
i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
i3.putExtra(GROUP_ID, groupId.getBytes());
startActivity(i3, options.toBundle());
return true;
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
} }
@Override
public void onActivityResult(int request, int result, Intent data) {
super.onActivityResult(request, result, data);
if (request == REQUEST_SHARE && result == RESULT_OK) {
displaySnackbar(R.string.blogs_sharing_snackbar);
}
}
@Override @Override
public String getUniqueTag() { public String getUniqueTag() {
return TAG; return TAG;
@@ -202,6 +232,13 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
loadData(true); loadData(true);
} }
private void displaySnackbar(int stringId) {
Snackbar snackbar =
Snackbar.make(list, stringId, Snackbar.LENGTH_SHORT);
snackbar.getView().setBackgroundResource(R.color.briar_primary);
snackbar.show();
}
private void showDeleteDialog() { private void showDeleteDialog() {
DialogInterface.OnClickListener okListener = DialogInterface.OnClickListener okListener =
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {

View File

@@ -20,6 +20,7 @@ import org.briarproject.android.ActivityComponent;
import org.briarproject.android.fragment.BaseFragment; import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.keyagreement.KeyAgreementActivity; import org.briarproject.android.keyagreement.KeyAgreementActivity;
import org.briarproject.android.util.BriarRecyclerView; import org.briarproject.android.util.BriarRecyclerView;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
@@ -33,13 +34,11 @@ import org.briarproject.api.event.ContactStatusChangedEvent;
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.ForumInvitationReceivedEvent;
import org.briarproject.api.event.ForumInvitationResponseReceivedEvent;
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.InvitationReceivedEvent;
import org.briarproject.api.event.InvitationResponseReceivedEvent;
import org.briarproject.api.event.PrivateMessageReceivedEvent; import org.briarproject.api.event.PrivateMessageReceivedEvent;
import org.briarproject.api.forum.ForumInvitationRequest;
import org.briarproject.api.forum.ForumInvitationResponse;
import org.briarproject.api.forum.ForumSharingManager; import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
@@ -91,6 +90,8 @@ public class ContactListFragment extends BaseFragment implements EventListener {
protected volatile IntroductionManager introductionManager; protected volatile IntroductionManager introductionManager;
@Inject @Inject
protected volatile ForumSharingManager forumSharingManager; protected volatile ForumSharingManager forumSharingManager;
@Inject
protected volatile BlogSharingManager blogSharingManager;
public static ContactListFragment newInstance() { public static ContactListFragment newInstance() {
@@ -277,14 +278,14 @@ public class ContactListFragment extends BaseFragment implements EventListener {
(IntroductionResponseReceivedEvent) e; (IntroductionResponseReceivedEvent) e;
IntroductionResponse ir = m.getIntroductionResponse(); IntroductionResponse ir = m.getIntroductionResponse();
updateItem(m.getContactId(), ConversationItem.from(ir)); updateItem(m.getContactId(), ConversationItem.from(ir));
} else if (e instanceof ForumInvitationReceivedEvent) { } else if (e instanceof InvitationReceivedEvent) {
LOG.info("Forum Invitation received, reloading conversation..."); LOG.info("Invitation received, reloading conversation...");
ForumInvitationReceivedEvent m = (ForumInvitationReceivedEvent) e; InvitationReceivedEvent m = (InvitationReceivedEvent) e;
reloadConversation(m.getContactId()); reloadConversation(m.getContactId());
} else if (e instanceof ForumInvitationResponseReceivedEvent) { } else if (e instanceof InvitationResponseReceivedEvent) {
LOG.info("Forum Invitation Response received, reloading ..."); LOG.info("Invitation Response received, reloading ...");
ForumInvitationResponseReceivedEvent m = InvitationResponseReceivedEvent m =
(ForumInvitationResponseReceivedEvent) e; (InvitationResponseReceivedEvent) e;
reloadConversation(m.getContactId()); reloadConversation(m.getContactId());
} }
} }
@@ -404,14 +405,19 @@ public class ContactListFragment extends BaseFragment implements EventListener {
LOG.info("Loading introduction messages took " + duration + " ms"); LOG.info("Loading introduction messages took " + duration + " ms");
now = System.currentTimeMillis(); now = System.currentTimeMillis();
Collection<InvitationMessage> invitations = Collection<InvitationMessage> forumInvitations =
forumSharingManager.getInvitationMessages(id); forumSharingManager.getInvitationMessages(id);
for (InvitationMessage i : invitations) { for (InvitationMessage i : forumInvitations) {
messages.add(ConversationItem.from(i));
}
Collection<InvitationMessage> blogInvitations =
blogSharingManager.getInvitationMessages(id);
for (InvitationMessage i : blogInvitations) {
messages.add(ConversationItem.from(i)); messages.add(ConversationItem.from(i));
} }
duration = System.currentTimeMillis() - now; duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Loading forum invitations took " + duration + " ms"); LOG.info("Loading invitations took " + duration + " ms");
return messages; return messages;
} }

View File

@@ -30,6 +30,7 @@ import org.briarproject.android.api.AndroidNotificationManager;
import org.briarproject.android.introduction.IntroductionActivity; import org.briarproject.android.introduction.IntroductionActivity;
import org.briarproject.android.util.BriarRecyclerView; import org.briarproject.android.util.BriarRecyclerView;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
@@ -44,15 +45,13 @@ import org.briarproject.api.event.ContactRemovedEvent;
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.ForumInvitationReceivedEvent;
import org.briarproject.api.event.ForumInvitationResponseReceivedEvent;
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.InvitationReceivedEvent;
import org.briarproject.api.event.InvitationResponseReceivedEvent;
import org.briarproject.api.event.MessagesAckedEvent; import org.briarproject.api.event.MessagesAckedEvent;
import org.briarproject.api.event.MessagesSentEvent; import org.briarproject.api.event.MessagesSentEvent;
import org.briarproject.api.event.PrivateMessageReceivedEvent; import org.briarproject.api.event.PrivateMessageReceivedEvent;
import org.briarproject.api.forum.ForumInvitationRequest;
import org.briarproject.api.forum.ForumInvitationResponse;
import org.briarproject.api.forum.ForumSharingManager; import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.introduction.IntroductionManager; import org.briarproject.api.introduction.IntroductionManager;
import org.briarproject.api.introduction.IntroductionMessage; import org.briarproject.api.introduction.IntroductionMessage;
@@ -64,6 +63,8 @@ import org.briarproject.api.messaging.PrivateMessageFactory;
import org.briarproject.api.messaging.PrivateMessageHeader; import org.briarproject.api.messaging.PrivateMessageHeader;
import org.briarproject.api.plugins.ConnectionRegistry; import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.sharing.InvitationMessage; import org.briarproject.api.sharing.InvitationMessage;
import org.briarproject.api.sharing.InvitationRequest;
import org.briarproject.api.sharing.InvitationResponse;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.briarproject.util.StringUtils; import org.briarproject.util.StringUtils;
@@ -128,6 +129,8 @@ public class ConversationActivity extends BriarActivity
protected volatile IntroductionManager introductionManager; protected volatile IntroductionManager introductionManager;
@Inject @Inject
protected volatile ForumSharingManager forumSharingManager; protected volatile ForumSharingManager forumSharingManager;
@Inject
protected volatile BlogSharingManager blogSharingManager;
private volatile GroupId groupId = null; private volatile GroupId groupId = null;
private volatile ContactId contactId = null; private volatile ContactId contactId = null;
@@ -334,9 +337,16 @@ public class ConversationActivity extends BriarActivity
Collection<IntroductionMessage> introductions = Collection<IntroductionMessage> introductions =
introductionManager introductionManager
.getIntroductionMessages(contactId); .getIntroductionMessages(contactId);
Collection<InvitationMessage> invitations = Collection<InvitationMessage> forumInvitations =
forumSharingManager forumSharingManager
.getInvitationMessages(contactId); .getInvitationMessages(contactId);
Collection<InvitationMessage> blogInvitations =
blogSharingManager
.getInvitationMessages(contactId);
List<InvitationMessage> invitations = new ArrayList<>(
forumInvitations.size() + blogInvitations.size());
invitations.addAll(forumInvitations);
invitations.addAll(blogInvitations);
long duration = System.currentTimeMillis() - now; long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Loading headers took " + duration + " ms"); LOG.info("Loading headers took " + duration + " ms");
@@ -388,13 +398,13 @@ public class ConversationActivity extends BriarActivity
items.add(item); items.add(item);
} }
for (InvitationMessage i : invitations) { for (InvitationMessage i : invitations) {
if (i instanceof ForumInvitationRequest) { if (i instanceof InvitationRequest) {
ForumInvitationRequest r = InvitationRequest r =
(ForumInvitationRequest) i; (InvitationRequest) i;
items.add(ConversationItem.from(r)); items.add(ConversationItem.from(r));
} else if (i instanceof ForumInvitationResponse) { } else if (i instanceof InvitationResponse) {
ForumInvitationResponse r = InvitationResponse r =
(ForumInvitationResponse) i; (InvitationResponse) i;
items.add(ConversationItem items.add(ConversationItem
.from(ConversationActivity.this, .from(ConversationActivity.this,
contactName, r)); contactName, r));
@@ -541,6 +551,7 @@ public class ConversationActivity extends BriarActivity
IntroductionRequestReceivedEvent event = IntroductionRequestReceivedEvent event =
(IntroductionRequestReceivedEvent) e; (IntroductionRequestReceivedEvent) e;
if (event.getContactId().equals(contactId)) { if (event.getContactId().equals(contactId)) {
LOG.info("Introduction request received, adding...");
IntroductionRequest ir = event.getIntroductionRequest(); IntroductionRequest ir = event.getIntroductionRequest();
ConversationItem item = new ConversationIntroductionInItem(ir); ConversationItem item = new ConversationIntroductionInItem(ir);
addConversationItem(item); addConversationItem(item);
@@ -549,21 +560,24 @@ public class ConversationActivity extends BriarActivity
IntroductionResponseReceivedEvent event = IntroductionResponseReceivedEvent event =
(IntroductionResponseReceivedEvent) e; (IntroductionResponseReceivedEvent) e;
if (event.getContactId().equals(contactId)) { if (event.getContactId().equals(contactId)) {
LOG.info("Introduction response received, adding...");
IntroductionResponse ir = event.getIntroductionResponse(); IntroductionResponse ir = event.getIntroductionResponse();
ConversationItem item = ConversationItem item =
ConversationItem.from(this, contactName, ir); ConversationItem.from(this, contactName, ir);
addConversationItem(item); addConversationItem(item);
} }
} else if (e instanceof ForumInvitationReceivedEvent) { } else if (e instanceof InvitationReceivedEvent) {
ForumInvitationReceivedEvent event = InvitationReceivedEvent event =
(ForumInvitationReceivedEvent) e; (InvitationReceivedEvent) e;
if (event.getContactId().equals(contactId)) { if (event.getContactId().equals(contactId)) {
LOG.info("Invitation received, reloading...");
loadMessages(); loadMessages();
} }
} else if (e instanceof ForumInvitationResponseReceivedEvent) { } else if (e instanceof InvitationResponseReceivedEvent) {
ForumInvitationResponseReceivedEvent event = InvitationResponseReceivedEvent event =
(ForumInvitationResponseReceivedEvent) e; (InvitationResponseReceivedEvent) e;
if (event.getContactId().equals(contactId)) { if (event.getContactId().equals(contactId)) {
LOG.info("Invitation response received, reloading...");
loadMessages(); loadMessages();
} }
} }

View File

@@ -13,18 +13,25 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.forum.ForumInvitationsActivity; import org.briarproject.android.sharing.InvitationsBlogActivity;
import org.briarproject.android.sharing.InvitationsForumActivity;
import org.briarproject.android.util.AndroidUtils; import org.briarproject.android.util.AndroidUtils;
import org.briarproject.api.blogs.BlogInvitationRequest;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.forum.ForumInvitationRequest; import org.briarproject.api.forum.ForumInvitationRequest;
import org.briarproject.api.introduction.IntroductionRequest; import org.briarproject.api.introduction.IntroductionRequest;
import org.briarproject.api.messaging.PrivateMessageHeader; import org.briarproject.api.messaging.PrivateMessageHeader;
import org.briarproject.api.sharing.InvitationRequest;
import org.briarproject.util.StringUtils; import org.briarproject.util.StringUtils;
import java.util.List; import java.util.List;
import static android.support.v7.util.SortedList.INVALID_POSITION; import static android.support.v7.util.SortedList.INVALID_POSITION;
import static android.support.v7.widget.RecyclerView.ViewHolder; import static android.support.v7.widget.RecyclerView.ViewHolder;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static org.briarproject.android.contact.ConversationItem.BLOG_INVITATION_IN;
import static org.briarproject.android.contact.ConversationItem.BLOG_INVITATION_OUT;
import static org.briarproject.android.contact.ConversationItem.FORUM_INVITATION_IN; import static org.briarproject.android.contact.ConversationItem.FORUM_INVITATION_IN;
import static org.briarproject.android.contact.ConversationItem.FORUM_INVITATION_OUT; import static org.briarproject.android.contact.ConversationItem.FORUM_INVITATION_OUT;
import static org.briarproject.android.contact.ConversationItem.INTRODUCTION_IN; import static org.briarproject.android.contact.ConversationItem.INTRODUCTION_IN;
@@ -46,13 +53,13 @@ class ConversationAdapter extends RecyclerView.Adapter {
private IntroductionHandler intro; private IntroductionHandler intro;
private String contactName; private String contactName;
public ConversationAdapter(Context context, ConversationAdapter(Context context,
IntroductionHandler introductionHandler) { IntroductionHandler introductionHandler) {
ctx = context; ctx = context;
intro = introductionHandler; intro = introductionHandler;
} }
public void setContactName(String contactName) { void setContactName(String contactName) {
this.contactName = contactName; this.contactName = contactName;
notifyDataSetChanged(); notifyDataSetChanged();
} }
@@ -77,7 +84,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
return new IntroductionHolder(v, type); return new IntroductionHolder(v, type);
} else if (type == INTRODUCTION_OUT) { } else if (type == INTRODUCTION_OUT) {
v = LayoutInflater.from(viewGroup.getContext()).inflate( v = LayoutInflater.from(viewGroup.getContext()).inflate(
R.layout.list_item_introduction_out, viewGroup, false); R.layout.list_item_msg_notice_out, viewGroup, false);
return new IntroductionHolder(v, type); return new IntroductionHolder(v, type);
} else if (type == NOTICE_IN) { } else if (type == NOTICE_IN) {
v = LayoutInflater.from(viewGroup.getContext()).inflate( v = LayoutInflater.from(viewGroup.getContext()).inflate(
@@ -87,13 +94,15 @@ class ConversationAdapter extends RecyclerView.Adapter {
v = LayoutInflater.from(viewGroup.getContext()).inflate( v = LayoutInflater.from(viewGroup.getContext()).inflate(
R.layout.list_item_notice_out, viewGroup, false); R.layout.list_item_notice_out, viewGroup, false);
return new NoticeHolder(v, type); return new NoticeHolder(v, type);
} else if (type == FORUM_INVITATION_IN) { } else if (type == FORUM_INVITATION_IN || type == BLOG_INVITATION_IN) {
v = LayoutInflater.from(viewGroup.getContext()).inflate( v = LayoutInflater.from(viewGroup.getContext()).inflate(
R.layout.list_item_forum_invitation_in, viewGroup, false); R.layout.list_item_shareable_invitation_in, viewGroup,
false);
return new InvitationHolder(v, type); return new InvitationHolder(v, type);
} else if (type == FORUM_INVITATION_OUT) { } else if (type == FORUM_INVITATION_OUT ||
type == BLOG_INVITATION_OUT) {
v = LayoutInflater.from(viewGroup.getContext()).inflate( v = LayoutInflater.from(viewGroup.getContext()).inflate(
R.layout.list_item_forum_invitation_out, viewGroup, false); R.layout.list_item_msg_notice_out, viewGroup, false);
return new InvitationHolder(v, type); return new InvitationHolder(v, type);
} }
// incoming message (non-local) // incoming message (non-local)
@@ -119,12 +128,12 @@ class ConversationAdapter extends RecyclerView.Adapter {
bindNotice((NoticeHolder) ui, (ConversationNoticeOutItem) item); bindNotice((NoticeHolder) ui, (ConversationNoticeOutItem) item);
} else if (item instanceof ConversationNoticeInItem) { } else if (item instanceof ConversationNoticeInItem) {
bindNotice((NoticeHolder) ui, (ConversationNoticeInItem) item); bindNotice((NoticeHolder) ui, (ConversationNoticeInItem) item);
} else if (item instanceof ConversationForumInvitationOutItem) { } else if (item instanceof ConversationShareableInvitationOutItem) {
bindInvitation((InvitationHolder) ui, bindInvitation((InvitationHolder) ui,
(ConversationForumInvitationOutItem) item); (ConversationShareableInvitationOutItem) item);
} else if (item instanceof ConversationForumInvitationInItem) { } else if (item instanceof ConversationShareableInvitationInItem) {
bindInvitation((InvitationHolder) ui, bindInvitation((InvitationHolder) ui,
(ConversationForumInvitationInItem) item); (ConversationShareableInvitationInItem) item);
} else { } else {
throw new IllegalArgumentException("Unhandled Conversation Item"); throw new IllegalArgumentException("Unhandled Conversation Item");
} }
@@ -180,9 +189,9 @@ class ConversationAdapter extends RecyclerView.Adapter {
String message = ir.getMessage(); String message = ir.getMessage();
if (StringUtils.isNullOrEmpty(message)) { if (StringUtils.isNullOrEmpty(message)) {
ui.messageLayout.setVisibility(View.GONE); ui.messageLayout.setVisibility(GONE);
} else { } else {
ui.messageLayout.setVisibility(View.VISIBLE); ui.messageLayout.setVisibility(VISIBLE);
ui.message.body.setText(StringUtils.trim(message)); ui.message.body.setText(StringUtils.trim(message));
ui.message.date ui.message.date
.setText(AndroidUtils.formatDate(ctx, item.getTime())); .setText(AndroidUtils.formatDate(ctx, item.getTime()));
@@ -213,8 +222,8 @@ class ConversationAdapter extends RecyclerView.Adapter {
ui.text.setText(ctx.getString( ui.text.setText(ctx.getString(
R.string.introduction_request_answered_received, R.string.introduction_request_answered_received,
contactName, ir.getName())); contactName, ir.getName()));
ui.acceptButton.setVisibility(View.GONE); ui.acceptButton.setVisibility(GONE);
ui.declineButton.setVisibility(View.GONE); ui.declineButton.setVisibility(GONE);
} }
// Incoming Introduction Request (Not Answered) // Incoming Introduction Request (Not Answered)
else { else {
@@ -230,12 +239,12 @@ class ConversationAdapter extends RecyclerView.Adapter {
if (item.getIntroductionRequest().doesIntroduceOtherIdentity()) { if (item.getIntroductionRequest().doesIntroduceOtherIdentity()) {
// don't allow accept when one of our identities is introduced // don't allow accept when one of our identities is introduced
ui.acceptButton.setVisibility(View.GONE); ui.acceptButton.setVisibility(GONE);
ui.text.setText(ctx.getString( ui.text.setText(ctx.getString(
R.string.introduction_request_for_our_identity_received, R.string.introduction_request_for_our_identity_received,
contactName, ir.getName())); contactName, ir.getName()));
} else { } else {
ui.acceptButton.setVisibility(View.VISIBLE); ui.acceptButton.setVisibility(VISIBLE);
ui.acceptButton.setOnClickListener(new View.OnClickListener() { ui.acceptButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@@ -245,7 +254,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
} }
}); });
} }
ui.declineButton.setVisibility(View.VISIBLE); ui.declineButton.setVisibility(VISIBLE);
ui.declineButton.setOnClickListener(new View.OnClickListener() { ui.declineButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@@ -276,26 +285,38 @@ class ConversationAdapter extends RecyclerView.Adapter {
} }
private void bindInvitation(InvitationHolder ui, private void bindInvitation(InvitationHolder ui,
final ConversationForumInvitationItem item) { final ConversationShareableInvitationItem item) {
ForumInvitationRequest fim = item.getForumInvitationMessage(); final InvitationRequest ir = item.getInvitationRequest();
String name = "";
int receivedRes = 0, sentRes = 0, buttonRes = 0;
if (ir instanceof ForumInvitationRequest) {
name = ((ForumInvitationRequest) ir).getForumName();
receivedRes = R.string.forum_invitation_received;
sentRes = R.string.forum_invitation_sent;
buttonRes = R.string.forum_show_invitations;
} else if (ir instanceof BlogInvitationRequest) {
name = ((BlogInvitationRequest) ir).getBlogAuthorName();
receivedRes = R.string.blogs_sharing_invitation_received;
sentRes = R.string.blogs_sharing_invitation_sent;
buttonRes = R.string.blogs_sharing_show_invitations;
}
String message = fim.getMessage(); String message = ir.getMessage();
if (StringUtils.isNullOrEmpty(message)) { if (StringUtils.isNullOrEmpty(message)) {
ui.messageLayout.setVisibility(View.GONE); ui.messageLayout.setVisibility(GONE);
} else { } else {
ui.messageLayout.setVisibility(View.VISIBLE); ui.messageLayout.setVisibility(VISIBLE);
ui.message.body.setText(StringUtils.trim(message)); ui.message.body.setText(StringUtils.trim(message));
ui.message.date ui.message.date
.setText(AndroidUtils.formatDate(ctx, item.getTime())); .setText(AndroidUtils.formatDate(ctx, item.getTime()));
} }
// Outgoing Invitation // Outgoing Invitation
if (item instanceof ConversationForumInvitationOutItem) { if (item instanceof ConversationShareableInvitationOutItem) {
ui.text.setText(ctx.getString(R.string.forum_invitation_sent, ui.text.setText(ctx.getString(sentRes, name, contactName));
fim.getForumName(), contactName)); ConversationShareableInvitationOutItem i =
ConversationForumInvitationOutItem i = (ConversationShareableInvitationOutItem) item;
(ConversationForumInvitationOutItem) item;
if (i.isSeen()) { if (i.isSeen()) {
ui.status.setImageResource(R.drawable.message_delivered); ui.status.setImageResource(R.drawable.message_delivered);
ui.message.status.setImageResource( ui.message.status.setImageResource(
@@ -312,22 +333,24 @@ class ConversationAdapter extends RecyclerView.Adapter {
} }
// Incoming Invitation // Incoming Invitation
else { else {
ui.text.setText(ctx.getString(R.string.forum_invitation_received, ui.text.setText(ctx.getString(receivedRes, contactName, name));
contactName, fim.getForumName()));
if (fim.isAvailable()) { if (ir.isAvailable()) {
ui.showForumsButton.setVisibility(View.VISIBLE); final Class c = ir instanceof ForumInvitationRequest ?
ui.showForumsButton InvitationsForumActivity.class :
InvitationsBlogActivity.class;
ui.showInvitationsButton.setText(ctx.getString(buttonRes));
ui.showInvitationsButton.setVisibility(VISIBLE);
ui.showInvitationsButton
.setOnClickListener(new View.OnClickListener() { .setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Intent intent = new Intent(ctx, Intent i = new Intent(ctx, c);
ForumInvitationsActivity.class); ctx.startActivity(i);
ctx.startActivity(intent);
} }
}); });
} else { } else {
ui.showForumsButton.setVisibility(View.GONE); ui.showInvitationsButton.setVisibility(GONE);
} }
} }
ui.date.setText(AndroidUtils.formatDate(ctx, item.getTime())); ui.date.setText(AndroidUtils.formatDate(ctx, item.getTime()));
@@ -345,7 +368,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
return items.get(position); return items.get(position);
} }
public ConversationItem getLastItem() { ConversationItem getLastItem() {
if (items.size() > 0) { if (items.size() > 0) {
return items.get(items.size() - 1); return items.get(items.size() - 1);
} else { } else {
@@ -353,7 +376,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
} }
} }
public SparseArray<IncomingItem> getIncomingMessages() { SparseArray<IncomingItem> getIncomingMessages() {
SparseArray<IncomingItem> messages = new SparseArray<>(); SparseArray<IncomingItem> messages = new SparseArray<>();
for (int i = 0; i < items.size(); i++) { for (int i = 0; i < items.size(); i++) {
@@ -365,7 +388,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
return messages; return messages;
} }
public SparseArray<OutgoingItem> getOutgoingMessages() { SparseArray<OutgoingItem> getOutgoingMessages() {
SparseArray<OutgoingItem> messages = new SparseArray<>(); SparseArray<OutgoingItem> messages = new SparseArray<>();
for (int i = 0; i < items.size(); i++) { for (int i = 0; i < items.size(); i++) {
@@ -377,7 +400,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
return messages; return messages;
} }
public SparseArray<ConversationMessageItem> getPrivateMessages() { SparseArray<ConversationMessageItem> getPrivateMessages() {
SparseArray<ConversationMessageItem> messages = new SparseArray<>(); SparseArray<ConversationMessageItem> messages = new SparseArray<>();
for (int i = 0; i < items.size(); i++) { for (int i = 0; i < items.size(); i++) {
@@ -408,7 +431,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
public TextView date; public TextView date;
public ImageView status; public ImageView status;
public MessageHolder(View v, int type) { MessageHolder(View v, int type) {
super(v); super(v);
layout = (ViewGroup) v.findViewById(R.id.msgLayout); layout = (ViewGroup) v.findViewById(R.id.msgLayout);
@@ -432,7 +455,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
private final TextView date; private final TextView date;
private final ImageView status; private final ImageView status;
public IntroductionHolder(View v, int type) { IntroductionHolder(View v, int type) {
super(v); super(v);
messageLayout = v.findViewById(R.id.messageLayout); messageLayout = v.findViewById(R.id.messageLayout);
@@ -457,7 +480,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
private final TextView date; private final TextView date;
private final ImageView status; private final ImageView status;
public NoticeHolder(View v, int type) { NoticeHolder(View v, int type) {
super(v); super(v);
text = (TextView) v.findViewById(R.id.noticeText); text = (TextView) v.findViewById(R.id.noticeText);
@@ -476,21 +499,21 @@ class ConversationAdapter extends RecyclerView.Adapter {
private final View messageLayout; private final View messageLayout;
private final MessageHolder message; private final MessageHolder message;
private final TextView text; private final TextView text;
private final Button showForumsButton; private final Button showInvitationsButton;
private final TextView date; private final TextView date;
private final ImageView status; private final ImageView status;
public InvitationHolder(View v, int type) { InvitationHolder(View v, int type) {
super(v); super(v);
messageLayout = v.findViewById(R.id.messageLayout); messageLayout = v.findViewById(R.id.messageLayout);
message = new MessageHolder(messageLayout, message = new MessageHolder(messageLayout,
type == FORUM_INVITATION_IN ? MSG_IN : MSG_OUT); type == FORUM_INVITATION_IN ? MSG_IN : MSG_OUT);
text = (TextView) v.findViewById(R.id.introductionText); text = (TextView) v.findViewById(R.id.introductionText);
showForumsButton = (Button) v.findViewById(R.id.showForumsButton); showInvitationsButton = (Button) v.findViewById(R.id.showInvitationsButton);
date = (TextView) v.findViewById(R.id.introductionTime); date = (TextView) v.findViewById(R.id.introductionTime);
if (type == FORUM_INVITATION_OUT) { if (type == FORUM_INVITATION_OUT || type == BLOG_INVITATION_OUT) {
status = (ImageView) v.findViewById(R.id.introductionStatus); status = (ImageView) v.findViewById(R.id.introductionStatus);
} else { } else {
status = null; status = null;
@@ -543,7 +566,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
} }
} }
public interface IntroductionHandler { interface IntroductionHandler {
void respondToIntroduction(SessionId sessionId, boolean accept); void respondToIntroduction(SessionId sessionId, boolean accept);
} }
} }

View File

@@ -1,32 +0,0 @@
package org.briarproject.android.contact;
import org.briarproject.api.forum.ForumInvitationRequest;
// This class is not thread-safe
public class ConversationForumInvitationInItem
extends ConversationForumInvitationItem
implements ConversationItem.IncomingItem {
private boolean read;
public ConversationForumInvitationInItem(ForumInvitationRequest fim) {
super(fim);
this.read = fim.isRead();
}
@Override
int getType() {
return FORUM_INVITATION_IN;
}
@Override
public boolean isRead() {
return read;
}
@Override
public void setRead(boolean read) {
this.read = read;
}
}

View File

@@ -1,18 +0,0 @@
package org.briarproject.android.contact;
import org.briarproject.api.forum.ForumInvitationRequest;
abstract class ConversationForumInvitationItem extends ConversationItem {
private final ForumInvitationRequest fim;
public ConversationForumInvitationItem(ForumInvitationRequest fim) {
super(fim.getId(), fim.getTimestamp());
this.fim = fim;
}
public ForumInvitationRequest getForumInvitationMessage() {
return fim;
}
}

View File

@@ -3,13 +3,15 @@ package org.briarproject.android.contact;
import android.content.Context; import android.content.Context;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.api.forum.ForumInvitationRequest; import org.briarproject.api.blogs.BlogInvitationResponse;
import org.briarproject.api.forum.ForumInvitationResponse; import org.briarproject.api.forum.ForumInvitationResponse;
import org.briarproject.api.introduction.IntroductionMessage; import org.briarproject.api.introduction.IntroductionMessage;
import org.briarproject.api.introduction.IntroductionRequest; import org.briarproject.api.introduction.IntroductionRequest;
import org.briarproject.api.introduction.IntroductionResponse; import org.briarproject.api.introduction.IntroductionResponse;
import org.briarproject.api.messaging.PrivateMessageHeader; import org.briarproject.api.messaging.PrivateMessageHeader;
import org.briarproject.api.sharing.InvitationMessage; import org.briarproject.api.sharing.InvitationMessage;
import org.briarproject.api.sharing.InvitationRequest;
import org.briarproject.api.sharing.InvitationResponse;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
// This class is not thread-safe // This class is not thread-safe
@@ -25,6 +27,8 @@ public abstract class ConversationItem {
final static int NOTICE_OUT = 6; final static int NOTICE_OUT = 6;
final static int FORUM_INVITATION_IN = 7; final static int FORUM_INVITATION_IN = 7;
final static int FORUM_INVITATION_OUT = 8; final static int FORUM_INVITATION_OUT = 8;
final static int BLOG_INVITATION_IN = 9;
final static int BLOG_INVITATION_OUT = 10;
private MessageId id; private MessageId id;
private long time; private long time;
@@ -97,15 +101,27 @@ public abstract class ConversationItem {
} }
} }
public static ConversationItem from(ForumInvitationRequest fim) { public static ConversationItem from(InvitationRequest fim) {
if (fim.isLocal()) { if (fim.isLocal()) {
return new ConversationForumInvitationOutItem(fim); return new ConversationShareableInvitationOutItem(fim);
} else { } else {
return new ConversationForumInvitationInItem(fim); return new ConversationShareableInvitationInItem(fim);
} }
} }
public static ConversationItem from(Context ctx, String contactName, public static ConversationItem from(Context ctx, String contactName,
InvitationResponse ir) {
if (ir instanceof ForumInvitationResponse) {
return from(ctx, contactName, (ForumInvitationResponse) ir);
} else if (ir instanceof BlogInvitationResponse) {
return from(ctx, contactName, (BlogInvitationResponse) ir);
} else {
throw new IllegalArgumentException("Unknown Invitation Response.");
}
}
private static ConversationItem from(Context ctx, String contactName,
ForumInvitationResponse fir) { ForumInvitationResponse fir) {
if (fir.isLocal()) { if (fir.isLocal()) {
@@ -137,6 +153,38 @@ public abstract class ConversationItem {
} }
} }
private static ConversationItem from(Context ctx, String contactName,
BlogInvitationResponse fir) {
if (fir.isLocal()) {
String text;
if (fir.wasAccepted()) {
text = ctx.getString(
R.string.blogs_sharing_response_accepted_sent,
contactName);
} else {
text = ctx.getString(
R.string.blogs_sharing_response_declined_sent,
contactName);
}
return new ConversationNoticeOutItem(fir.getId(), text,
fir.getTimestamp(), fir.isSent(), fir.isSeen());
} else {
String text;
if (fir.wasAccepted()) {
text = ctx.getString(
R.string.blogs_sharing_response_accepted_received,
contactName);
} else {
text = ctx.getString(
R.string.blogs_sharing_response_declined_received,
contactName);
}
return new ConversationNoticeInItem(fir.getId(), text,
fir.getTimestamp(), fir.isRead());
}
}
/** /**
* This method should not be used to get user-facing objects, * This method should not be used to get user-facing objects,
* Its purpose is only to provide data for the contact list. * Its purpose is only to provide data for the contact list.

View File

@@ -0,0 +1,43 @@
package org.briarproject.android.contact;
import org.briarproject.api.blogs.BlogInvitationRequest;
import org.briarproject.api.forum.ForumInvitationRequest;
import org.briarproject.api.sharing.InvitationRequest;
// This class is not thread-safe
class ConversationShareableInvitationInItem
extends ConversationShareableInvitationItem
implements ConversationItem.IncomingItem {
private final int type;
private boolean read;
ConversationShareableInvitationInItem(InvitationRequest ir) {
super(ir);
if (ir instanceof ForumInvitationRequest) {
this.type = FORUM_INVITATION_IN;
} else if (ir instanceof BlogInvitationRequest) {
this.type = BLOG_INVITATION_IN;
} else {
throw new IllegalArgumentException("Unknown Invitation Type.");
}
this.read = ir.isRead();
}
@Override
int getType() {
return type;
}
@Override
public boolean isRead() {
return read;
}
@Override
public void setRead(boolean read) {
this.read = read;
}
}

View File

@@ -0,0 +1,18 @@
package org.briarproject.android.contact;
import org.briarproject.api.sharing.InvitationRequest;
abstract class ConversationShareableInvitationItem extends ConversationItem {
private final InvitationRequest fim;
ConversationShareableInvitationItem(InvitationRequest fim) {
super(fim.getId(), fim.getTimestamp());
this.fim = fim;
}
InvitationRequest getInvitationRequest() {
return fim;
}
}

View File

@@ -1,6 +1,8 @@
package org.briarproject.android.contact; package org.briarproject.android.contact;
import org.briarproject.api.blogs.BlogInvitationRequest;
import org.briarproject.api.forum.ForumInvitationRequest; import org.briarproject.api.forum.ForumInvitationRequest;
import org.briarproject.api.sharing.InvitationRequest;
/** /**
* This class is needed and can not be replaced by an ConversationNoticeOutItem, * This class is needed and can not be replaced by an ConversationNoticeOutItem,
@@ -9,21 +11,31 @@ import org.briarproject.api.forum.ForumInvitationRequest;
* <p/> * <p/>
* This class is not thread-safe * This class is not thread-safe
*/ */
public class ConversationForumInvitationOutItem class ConversationShareableInvitationOutItem
extends ConversationForumInvitationItem extends ConversationShareableInvitationItem
implements ConversationItem.OutgoingItem { implements ConversationItem.OutgoingItem {
private final int type;
private boolean sent, seen; private boolean sent, seen;
public ConversationForumInvitationOutItem(ForumInvitationRequest fim) { ConversationShareableInvitationOutItem(InvitationRequest ir) {
super(fim); super(ir);
this.sent = fim.isSent();
this.seen = fim.isSeen(); if (ir instanceof ForumInvitationRequest) {
this.type = FORUM_INVITATION_OUT;
} else if (ir instanceof BlogInvitationRequest) {
this.type = BLOG_INVITATION_OUT;
} else {
throw new IllegalArgumentException("Unknown Invitation Type.");
}
this.sent = ir.isSent();
this.seen = ir.isSeen();
} }
@Override @Override
int getType() { int getType() {
return FORUM_INVITATION_OUT; return type;
} }
@Override @Override

View File

@@ -33,6 +33,8 @@ import org.briarproject.android.ActivityComponent;
import org.briarproject.android.BriarActivity; import org.briarproject.android.BriarActivity;
import org.briarproject.android.api.AndroidNotificationManager; import org.briarproject.android.api.AndroidNotificationManager;
import org.briarproject.android.controller.handler.UiResultHandler; import org.briarproject.android.controller.handler.UiResultHandler;
import org.briarproject.android.sharing.ShareForumActivity;
import org.briarproject.android.sharing.SharingStatusForumActivity;
import org.briarproject.android.util.AndroidUtils; import org.briarproject.android.util.AndroidUtils;
import org.briarproject.android.util.BriarRecyclerView; import org.briarproject.android.util.BriarRecyclerView;
import org.briarproject.android.util.TrustIndicatorView; import org.briarproject.android.util.TrustIndicatorView;
@@ -228,7 +230,7 @@ public class ForumActivity extends BriarActivity implements
options.toBundle()); options.toBundle());
return true; return true;
case R.id.action_forum_sharing_status: case R.id.action_forum_sharing_status:
Intent i3 = new Intent(this, ForumSharingStatusActivity.class); Intent i3 = new Intent(this, SharingStatusForumActivity.class);
i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP); i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
i3.putExtra(GROUP_ID, groupId.getBytes()); i3.putExtra(GROUP_ID, groupId.getBytes());
ActivityCompat.startActivity(this, i3, options.toBundle()); ActivityCompat.startActivity(this, i3, options.toBundle());

View File

@@ -1,188 +0,0 @@
package org.briarproject.android.forum;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.util.BriarRecyclerView;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.NoSuchGroupException;
import org.briarproject.api.event.ContactRemovedEvent;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.ForumInvitationReceivedEvent;
import org.briarproject.api.event.GroupAddedEvent;
import org.briarproject.api.event.GroupRemovedEvent;
import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumSharingManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.logging.Logger;
import javax.inject.Inject;
import static android.widget.Toast.LENGTH_SHORT;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.android.forum.ForumInvitationAdapter.AvailableForumClickListener;
public class ForumInvitationsActivity extends BriarActivity
implements EventListener, AvailableForumClickListener {
private static final Logger LOG =
Logger.getLogger(ForumInvitationsActivity.class.getName());
private ForumInvitationAdapter adapter;
// Fields that are accessed from background threads must be volatile
@Inject
protected volatile ForumManager forumManager;
@Inject
protected volatile ForumSharingManager forumSharingManager;
@Inject
protected volatile EventBus eventBus;
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.activity_available_forums);
adapter = new ForumInvitationAdapter(this, this);
BriarRecyclerView list =
(BriarRecyclerView) findViewById(R.id.availableForumsView);
list.setLayoutManager(new LinearLayoutManager(this));
list.setAdapter(adapter);
}
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void onResume() {
super.onResume();
eventBus.addListener(this);
loadForums(false);
}
@Override
public void onPause() {
super.onPause();
eventBus.removeListener(this);
adapter.clear();
}
private void loadForums(final boolean clear) {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
Collection<ForumInvitationItem> forums = new ArrayList<>();
long now = System.currentTimeMillis();
for (Forum f : forumSharingManager.getInvited()) {
boolean subscribed;
try {
forumManager.getForum(f.getId());
subscribed = true;
} catch (NoSuchGroupException e) {
subscribed = false;
}
Collection<Contact> c =
forumSharingManager.getSharedBy(f.getId());
forums.add(
new ForumInvitationItem(f, subscribed, c));
}
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Load took " + duration + " ms");
displayForums(forums, clear);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
private void displayForums(final Collection<ForumInvitationItem> forums,
final boolean clear) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (forums.isEmpty()) {
LOG.info("No forums available, finishing");
finish();
} else {
if (clear) adapter.clear();
adapter.addAll(forums);
}
}
});
}
@Override
public void eventOccurred(Event e) {
if (e instanceof ContactRemovedEvent) {
LOG.info("Contact removed, reloading");
loadForums(true);
} else if (e instanceof GroupAddedEvent) {
GroupAddedEvent g = (GroupAddedEvent) e;
if (g.getGroup().getClientId().equals(forumManager.getClientId())) {
LOG.info("Forum added, reloading");
loadForums(false);
}
} else if (e instanceof GroupRemovedEvent) {
GroupRemovedEvent g = (GroupRemovedEvent) e;
if (g.getGroup().getClientId().equals(forumManager.getClientId())) {
LOG.info("Forum removed, reloading");
loadForums(true);
}
} else if (e instanceof ForumInvitationReceivedEvent) {
LOG.info("Available forums updated, reloading");
loadForums(false);
}
}
@Override
public void onItemClick(ForumInvitationItem item, boolean accept) {
respondToInvitation(item, accept);
// show toast
int res = R.string.forum_declined_toast;
if (accept) res = R.string.forum_joined_toast;
Toast.makeText(this, res, LENGTH_SHORT).show();
// remove item and finish if it was the last
adapter.remove(item);
if (adapter.getItemCount() == 0) {
supportFinishAfterTransition();
}
}
private void respondToInvitation(final ForumInvitationItem item,
final boolean accept) {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
Forum f = item.getForum();
for (Contact c : item.getContacts()) {
forumSharingManager.respondToInvitation(f, c, accept);
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
}

View File

@@ -16,6 +16,7 @@ import android.view.ViewGroup;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.ActivityComponent; import org.briarproject.android.ActivityComponent;
import org.briarproject.android.fragment.BaseEventFragment; import org.briarproject.android.fragment.BaseEventFragment;
import org.briarproject.android.sharing.InvitationsForumActivity;
import org.briarproject.android.util.BriarRecyclerView; import org.briarproject.android.util.BriarRecyclerView;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.db.NoSuchGroupException; import org.briarproject.api.db.NoSuchGroupException;
@@ -285,6 +286,7 @@ public class ForumListFragment extends BaseEventFragment implements
@Override @Override
public void onClick(View view) { public void onClick(View view) {
// snackbar click // snackbar click
startActivity(new Intent(getContext(), ForumInvitationsActivity.class)); Intent i = new Intent(getContext(), InvitationsForumActivity.class);
startActivity(i);
} }
} }

View File

@@ -0,0 +1,37 @@
package org.briarproject.android.sharing;
import android.content.Context;
import org.briarproject.R;
import org.briarproject.api.blogs.Blog;
class BlogInvitationAdapter extends InvitationAdapter {
BlogInvitationAdapter(Context ctx, AvailableForumClickListener listener) {
super(ctx, listener);
}
@Override
public void onBindViewHolder(InvitationsViewHolder ui, int position) {
super.onBindViewHolder(ui, position);
InvitationItem item = getItem(position);
Blog blog = (Blog) item.getShareable();
ui.avatar.setAuthorAvatar(blog.getAuthor());
ui.name.setText(ctx.getString(R.string.blogs_personal_blog,
blog.getAuthor().getName()));
if (item.isSubscribed()) {
ui.subscribed.setText(ctx.getString(R.string.blogs_sharing_exists,
blog.getAuthor().getName()));
}
}
int compareInvitations(InvitationItem o1, InvitationItem o2) {
return String.CASE_INSENSITIVE_ORDER
.compare(((Blog) o1.getShareable()).getAuthor().getName(),
((Blog) o2.getShareable()).getAuthor().getName());
}
}

View File

@@ -1,4 +1,4 @@
package org.briarproject.android.forum; package org.briarproject.android.sharing;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;

View File

@@ -1,4 +1,4 @@
package org.briarproject.android.forum; package org.briarproject.android.sharing;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
@@ -37,8 +37,8 @@ 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.android.forum.ShareForumActivity.CONTACTS; import static org.briarproject.android.sharing.ShareActivity.CONTACTS;
import static org.briarproject.android.forum.ShareForumActivity.getContactsFromIds; import static org.briarproject.android.sharing.ShareActivity.getContactsFromIds;
import static org.briarproject.api.sharing.SharingConstants.GROUP_ID; import static org.briarproject.api.sharing.SharingConstants.GROUP_ID;
public class ContactSelectorFragment extends BaseFragment implements public class ContactSelectorFragment extends BaseFragment implements
@@ -49,7 +49,7 @@ public class ContactSelectorFragment extends BaseFragment implements
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(ContactSelectorFragment.class.getName()); Logger.getLogger(ContactSelectorFragment.class.getName());
private ShareForumActivity shareForumActivity; private ShareActivity shareActivity;
private Menu menu; private Menu menu;
private BriarRecyclerView list; private BriarRecyclerView list;
private ContactSelectorAdapter adapter; private ContactSelectorAdapter adapter;
@@ -63,7 +63,7 @@ public class ContactSelectorFragment extends BaseFragment implements
@Inject @Inject
protected volatile ForumSharingManager forumSharingManager; protected volatile ForumSharingManager forumSharingManager;
protected volatile GroupId groupId; private volatile GroupId groupId;
public static ContactSelectorFragment newInstance(GroupId groupId) { public static ContactSelectorFragment newInstance(GroupId groupId) {
@@ -83,10 +83,10 @@ public class ContactSelectorFragment extends BaseFragment implements
public void onAttach(Context context) { public void onAttach(Context context) {
super.onAttach(context); super.onAttach(context);
try { try {
shareForumActivity = (ShareForumActivity) context; shareActivity = (ShareActivity) context;
} catch (ClassCastException e) { } catch (ClassCastException e) {
throw new InstantiationError( throw new InstantiationError(
"This fragment is only meant to be attached to the ShareForumActivity"); "This fragment is only meant to be attached to a subclass of ShareActivity");
} }
} }
@@ -95,7 +95,8 @@ public class ContactSelectorFragment extends BaseFragment implements
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setHasOptionsMenu(true); setHasOptionsMenu(true);
groupId = new GroupId(getArguments().getByteArray(GROUP_ID)); Bundle args = getArguments();
groupId = new GroupId(args.getByteArray(GROUP_ID));
if (groupId == null) throw new IllegalStateException("No GroupId"); if (groupId == null) throw new IllegalStateException("No GroupId");
} }
@@ -115,13 +116,13 @@ public class ContactSelectorFragment extends BaseFragment implements
list = (BriarRecyclerView) contentView.findViewById(R.id.contactList); list = (BriarRecyclerView) contentView.findViewById(R.id.contactList);
list.setLayoutManager(new LinearLayoutManager(getActivity())); list.setLayoutManager(new LinearLayoutManager(getActivity()));
list.setAdapter(adapter); list.setAdapter(adapter);
list.setEmptyText(getString(R.string.no_contacts)); list.setEmptyText(getString(R.string.no_contacts_selector));
// restore selected contacts if available // restore selected contacts if available
if (savedInstanceState != null) { if (savedInstanceState != null) {
ArrayList<Integer> intContacts = ArrayList<Integer> intContacts =
savedInstanceState.getIntegerArrayList(CONTACTS); savedInstanceState.getIntegerArrayList(CONTACTS);
selectedContacts = ShareForumActivity.getContactsFromIntegers( selectedContacts = ShareActivity.getContactsFromIntegers(
intContacts); intContacts);
} }
@@ -160,11 +161,11 @@ public class ContactSelectorFragment extends BaseFragment implements
// Handle presses on the action bar items // Handle presses on the action bar items
switch (item.getItemId()) { switch (item.getItemId()) {
case android.R.id.home: case android.R.id.home:
shareForumActivity.onBackPressed(); shareActivity.onBackPressed();
return true; return true;
case R.id.action_share_forum: case R.id.action_share_forum:
selectedContacts = adapter.getSelectedContactIds(); selectedContacts = adapter.getSelectedContactIds();
shareForumActivity.showMessageScreen(groupId, selectedContacts); shareActivity.showMessageScreen(groupId, selectedContacts);
return true; return true;
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
@@ -185,7 +186,7 @@ public class ContactSelectorFragment extends BaseFragment implements
} }
private void loadContacts(final Collection<ContactId> selection) { private void loadContacts(final Collection<ContactId> selection) {
shareForumActivity.runOnDbThread(new Runnable() { shareActivity.runOnDbThread(new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
@@ -199,8 +200,7 @@ public class ContactSelectorFragment extends BaseFragment implements
boolean selected = selection != null && boolean selected = selection != null &&
selection.contains(c.getId()); selection.contains(c.getId());
// do we have already some sharing with that contact? // do we have already some sharing with that contact?
boolean disabled = boolean disabled = shareActivity.isDisabled(groupId, c);
!forumSharingManager.canBeShared(groupId, c);
contacts.add(new SelectableContactListItem(c, contacts.add(new SelectableContactListItem(c,
localAuthor, groupId, selected, disabled)); localAuthor, groupId, selected, disabled));
} }
@@ -218,7 +218,7 @@ public class ContactSelectorFragment extends BaseFragment implements
} }
private void displayContacts(final List<ContactListItem> contacts) { private void displayContacts(final List<ContactListItem> contacts) {
shareForumActivity.runOnUiThread(new Runnable() { shareActivity.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
if (!contacts.isEmpty()) adapter.addAll(contacts); if (!contacts.isEmpty()) adapter.addAll(contacts);

View File

@@ -0,0 +1,31 @@
package org.briarproject.android.sharing;
import android.content.Context;
import org.briarproject.api.forum.Forum;
class ForumInvitationAdapter extends InvitationAdapter {
ForumInvitationAdapter(Context ctx, AvailableForumClickListener listener) {
super(ctx, listener);
}
@Override
public void onBindViewHolder(InvitationsViewHolder ui, int position) {
super.onBindViewHolder(ui, position);
InvitationItem item = getItem(position);
Forum forum = (Forum) item.getShareable();
ui.avatar.setText(forum.getName().substring(0, 1));
ui.avatar.setBackgroundBytes(item.getShareable().getId().getBytes());
ui.name.setText(forum.getName());
}
int compareInvitations(InvitationItem o1, InvitationItem o2) {
return String.CASE_INSENSITIVE_ORDER
.compare(((Forum) o1.getShareable()).getName(),
((Forum) o2.getShareable()).getName());
}
}

View File

@@ -1,4 +1,4 @@
package org.briarproject.android.forum; package org.briarproject.android.sharing;
import android.content.Context; import android.content.Context;
import android.support.v7.util.SortedList; import android.support.v7.util.SortedList;
@@ -12,7 +12,6 @@ import android.widget.TextView;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.util.TextAvatarView; import org.briarproject.android.util.TextAvatarView;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.forum.ForumSharingMessage;
import org.briarproject.util.StringUtils; import org.briarproject.util.StringUtils;
import java.util.ArrayList; import java.util.ArrayList;
@@ -21,37 +20,32 @@ import java.util.Collection;
import static android.view.View.GONE; import static android.view.View.GONE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
class ForumInvitationAdapter extends abstract class InvitationAdapter extends
RecyclerView.Adapter<ForumInvitationAdapter.AvailableForumViewHolder> { RecyclerView.Adapter<InvitationAdapter.InvitationsViewHolder> {
private final Context ctx; protected final Context ctx;
private final AvailableForumClickListener listener; private final AvailableForumClickListener listener;
private final SortedList<ForumInvitationItem> forums = private final SortedList<InvitationItem> invitations =
new SortedList<>(ForumInvitationItem.class, new SortedList<>(InvitationItem.class,
new SortedListCallBacks()); new SortedListCallBacks());
ForumInvitationAdapter(Context ctx, AvailableForumClickListener listener) { InvitationAdapter(Context ctx, AvailableForumClickListener listener) {
this.ctx = ctx; this.ctx = ctx;
this.listener = listener; this.listener = listener;
} }
@Override @Override
public AvailableForumViewHolder onCreateViewHolder(ViewGroup parent, public InvitationsViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) { int viewType) {
View v = LayoutInflater.from(ctx) View v = LayoutInflater.from(ctx)
.inflate(R.layout.list_item_available_forum, parent, false); .inflate(R.layout.list_item_invitations, parent, false);
return new AvailableForumViewHolder(v); return new InvitationsViewHolder(v);
} }
@Override @Override
public void onBindViewHolder(AvailableForumViewHolder ui, int position) { public void onBindViewHolder(InvitationsViewHolder ui, int position) {
final ForumInvitationItem item = getItem(position); final InvitationItem item = getItem(position);
ui.avatar.setText(item.getForum().getName().substring(0, 1));
ui.avatar.setBackgroundBytes(item.getForum().getId().getBytes());
ui.name.setText(item.getForum().getName());
Collection<String> names = new ArrayList<>(); Collection<String> names = new ArrayList<>();
for (Contact c : item.getContacts()) names.add(c.getAuthor().getName()); for (Contact c : item.getContacts()) names.add(c.getAuthor().getName());
@@ -80,40 +74,39 @@ class ForumInvitationAdapter extends
@Override @Override
public int getItemCount() { public int getItemCount() {
return forums.size(); return invitations.size();
} }
public ForumInvitationItem getItem(int position) { public InvitationItem getItem(int position) {
return forums.get(position); return invitations.get(position);
} }
public void add(ForumInvitationItem item) { public void add(InvitationItem item) {
forums.add(item); invitations.add(item);
} }
public void addAll(Collection<ForumInvitationItem> list) { public void addAll(Collection<InvitationItem> list) {
forums.addAll(list); invitations.addAll(list);
} }
public void remove(ForumInvitationItem item) { public void remove(InvitationItem item) {
forums.remove(item); invitations.remove(item);
} }
public void clear() { public void clear() {
forums.clear(); invitations.clear();
} }
static class AvailableForumViewHolder static class InvitationsViewHolder extends RecyclerView.ViewHolder {
extends RecyclerView.ViewHolder {
private final TextAvatarView avatar; final TextAvatarView avatar;
private final TextView name; final TextView name;
private final TextView sharedBy; final TextView sharedBy;
private final TextView subscribed; final TextView subscribed;
private final Button accept; final Button accept;
private final Button decline; final Button decline;
AvailableForumViewHolder(View v) { InvitationsViewHolder(View v) {
super(v); super(v);
avatar = (TextAvatarView) v.findViewById(R.id.avatarView); avatar = (TextAvatarView) v.findViewById(R.id.avatarView);
@@ -125,15 +118,15 @@ class ForumInvitationAdapter extends
} }
} }
abstract int compareInvitations(InvitationItem o1, InvitationItem o2);
private class SortedListCallBacks private class SortedListCallBacks
extends SortedList.Callback<ForumInvitationItem> { extends SortedList.Callback<InvitationItem> {
@Override @Override
public int compare(ForumInvitationItem o1, public int compare(InvitationItem o1,
ForumInvitationItem o2) { InvitationItem o2) {
return String.CASE_INSENSITIVE_ORDER return compareInvitations(o1, o2);
.compare(o1.getForum().getName(),
o2.getForum().getName());
} }
@Override @Override
@@ -157,21 +150,21 @@ class ForumInvitationAdapter extends
} }
@Override @Override
public boolean areContentsTheSame(ForumInvitationItem oldItem, public boolean areContentsTheSame(InvitationItem oldItem,
ForumInvitationItem newItem) { InvitationItem newItem) {
return oldItem.isSubscribed() == newItem.isSubscribed() && return oldItem.isSubscribed() == newItem.isSubscribed() &&
oldItem.getContacts().equals(newItem.getContacts()); oldItem.getContacts().equals(newItem.getContacts());
} }
@Override @Override
public boolean areItemsTheSame(ForumInvitationItem oldItem, public boolean areItemsTheSame(InvitationItem oldItem,
ForumInvitationItem newItem) { InvitationItem newItem) {
return oldItem.getForum().equals(newItem.getForum()); return oldItem.getShareable().equals(newItem.getShareable());
} }
} }
interface AvailableForumClickListener { interface AvailableForumClickListener {
void onItemClick(ForumInvitationItem item, boolean accept); void onItemClick(InvitationItem item, boolean accept);
} }
} }

View File

@@ -1,29 +1,29 @@
package org.briarproject.android.forum; package org.briarproject.android.sharing;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.forum.Forum; import org.briarproject.api.sharing.Shareable;
import java.util.Collection; import java.util.Collection;
class ForumInvitationItem { class InvitationItem {
private final Forum forum; private final Shareable shareable;
private final boolean subscribed; private final boolean subscribed;
private final Collection<Contact> contacts; private final Collection<Contact> contacts;
ForumInvitationItem(Forum forum, boolean subscribed, InvitationItem(Shareable shareable, boolean subscribed,
Collection<Contact> contacts) { Collection<Contact> contacts) {
this.forum = forum; this.shareable = shareable;
this.subscribed = subscribed; this.subscribed = subscribed;
this.contacts = contacts; this.contacts = contacts;
} }
Forum getForum() { Shareable getShareable() {
return forum; return shareable;
} }
public boolean isSubscribed() { boolean isSubscribed() {
return subscribed; return subscribed;
} }

View File

@@ -0,0 +1,117 @@
package org.briarproject.android.sharing;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.util.BriarRecyclerView;
import org.briarproject.api.event.ContactRemovedEvent;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import java.util.Collection;
import java.util.logging.Logger;
import javax.inject.Inject;
import static android.widget.Toast.LENGTH_SHORT;
import static org.briarproject.android.sharing.InvitationAdapter.AvailableForumClickListener;
abstract class InvitationsActivity extends BriarActivity
implements EventListener, AvailableForumClickListener {
protected static final Logger LOG =
Logger.getLogger(InvitationsActivity.class.getName());
private InvitationAdapter adapter;
@Inject
protected EventBus eventBus;
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.activity_invitations);
adapter = getAdapter(this, this);
BriarRecyclerView list =
(BriarRecyclerView) findViewById(R.id.invitationsView);
if (list != null) {
list.setLayoutManager(new LinearLayoutManager(this));
list.setAdapter(adapter);
}
}
@Override
public void onResume() {
super.onResume();
eventBus.addListener(this);
loadInvitations(false);
}
@Override
public void onPause() {
super.onPause();
eventBus.removeListener(this);
adapter.clear();
}
@Override
public void eventOccurred(Event e) {
if (e instanceof ContactRemovedEvent) {
LOG.info("Contact removed, reloading...");
loadInvitations(true);
}
}
@Override
public void onItemClick(InvitationItem item, boolean accept) {
respondToInvitation(item, accept);
// show toast
int res = getDeclineRes();
if (accept) res = getAcceptRes();
Toast.makeText(this, res, LENGTH_SHORT).show();
// remove item and finish if it was the last
adapter.remove(item);
if (adapter.getItemCount() == 0) {
supportFinishAfterTransition();
}
}
abstract protected InvitationAdapter getAdapter(Context ctx,
AvailableForumClickListener listener);
abstract protected void loadInvitations(boolean clear);
abstract protected void respondToInvitation(final InvitationItem item,
final boolean accept);
abstract protected int getAcceptRes();
abstract protected int getDeclineRes();
protected void displayInvitations(
final Collection<InvitationItem> invitations, final boolean clear) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (invitations.isEmpty()) {
LOG.info("No more invitations available, finishing");
finish();
} else {
if (clear) adapter.clear();
adapter.addAll(invitations);
}
}
});
}
}

View File

@@ -0,0 +1,127 @@
package org.briarproject.android.sharing;
import android.content.Context;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.api.blogs.Blog;
import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.NoSuchGroupException;
import org.briarproject.api.event.BlogInvitationReceivedEvent;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.GroupAddedEvent;
import org.briarproject.api.event.GroupRemovedEvent;
import org.briarproject.api.sync.ClientId;
import java.util.ArrayList;
import java.util.Collection;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.android.sharing.InvitationAdapter.AvailableForumClickListener;
public class InvitationsBlogActivity extends InvitationsActivity {
// Fields that are accessed from background threads must be volatile
@Inject
protected volatile BlogManager blogManager;
@Inject
protected volatile BlogSharingManager blogSharingManager;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void eventOccurred(Event e) {
super.eventOccurred(e);
if (e instanceof GroupAddedEvent) {
GroupAddedEvent g = (GroupAddedEvent) e;
ClientId cId = g.getGroup().getClientId();
if (cId.equals(blogManager.getClientId())) {
LOG.info("Blog added, reloading");
loadInvitations(false);
}
} else if (e instanceof GroupRemovedEvent) {
GroupRemovedEvent g = (GroupRemovedEvent) e;
ClientId cId = g.getGroup().getClientId();
if (cId.equals(blogManager.getClientId())) {
LOG.info("Blog removed, reloading");
loadInvitations(false);
}
} else if (e instanceof BlogInvitationReceivedEvent) {
LOG.info("Blog invitation received, reloading");
loadInvitations(false);
}
}
protected InvitationAdapter getAdapter(Context ctx,
AvailableForumClickListener listener) {
return new BlogInvitationAdapter(ctx, listener);
}
protected void loadInvitations(final boolean clear) {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
Collection<InvitationItem> invitations = new ArrayList<>();
long now = System.currentTimeMillis();
for (Blog b : blogSharingManager.getInvited()) {
boolean subscribed;
try {
blogManager.getBlog(b.getId());
subscribed = true;
} catch (NoSuchGroupException e) {
subscribed = false;
}
Collection<Contact> c =
blogSharingManager.getSharedBy(b.getId());
invitations.add(
new InvitationItem(b, subscribed, c));
}
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Load took " + duration + " ms");
displayInvitations(invitations, clear);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
protected void respondToInvitation(final InvitationItem item,
final boolean accept) {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
Blog b = (Blog) item.getShareable();
for (Contact c : item.getContacts()) {
blogSharingManager.respondToInvitation(b, c, accept);
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
protected int getAcceptRes() {
return R.string.blogs_sharing_joined_toast;
}
protected int getDeclineRes() {
return R.string.blogs_sharing_declined_toast;
}
}

View File

@@ -0,0 +1,127 @@
package org.briarproject.android.sharing;
import android.content.Context;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.NoSuchGroupException;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.ForumInvitationReceivedEvent;
import org.briarproject.api.event.GroupAddedEvent;
import org.briarproject.api.event.GroupRemovedEvent;
import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.sync.ClientId;
import java.util.ArrayList;
import java.util.Collection;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.android.sharing.InvitationAdapter.AvailableForumClickListener;
public class InvitationsForumActivity extends InvitationsActivity {
// Fields that are accessed from background threads must be volatile
@Inject
protected volatile ForumManager forumManager;
@Inject
protected volatile ForumSharingManager forumSharingManager;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void eventOccurred(Event e) {
super.eventOccurred(e);
if (e instanceof GroupAddedEvent) {
GroupAddedEvent g = (GroupAddedEvent) e;
ClientId cId = g.getGroup().getClientId();
if (cId.equals(forumManager.getClientId())) {
LOG.info("Forum added, reloading");
loadInvitations(false);
}
} else if (e instanceof GroupRemovedEvent) {
GroupRemovedEvent g = (GroupRemovedEvent) e;
ClientId cId = g.getGroup().getClientId();
if (cId.equals(forumManager.getClientId())) {
LOG.info("Forum removed, reloading");
loadInvitations(false);
}
} else if (e instanceof ForumInvitationReceivedEvent) {
LOG.info("Forum invitation received, reloading");
loadInvitations(false);
}
}
protected InvitationAdapter getAdapter(Context ctx,
AvailableForumClickListener listener) {
return new ForumInvitationAdapter(ctx, listener);
}
protected void loadInvitations(final boolean clear) {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
Collection<InvitationItem> forums = new ArrayList<>();
long now = System.currentTimeMillis();
for (Forum f : forumSharingManager.getInvited()) {
boolean subscribed;
try {
forumManager.getForum(f.getId());
subscribed = true;
} catch (NoSuchGroupException e) {
subscribed = false;
}
Collection<Contact> c =
forumSharingManager.getSharedBy(f.getId());
forums.add(
new InvitationItem(f, subscribed, c));
}
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Load took " + duration + " ms");
displayInvitations(forums, clear);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
protected void respondToInvitation(final InvitationItem item,
final boolean accept) {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
Forum f = (Forum) item.getShareable();
for (Contact c : item.getContacts()) {
forumSharingManager.respondToInvitation(f, c, accept);
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
protected int getAcceptRes() {
return R.string.forum_joined_toast;
}
protected int getDeclineRes() {
return R.string.forum_declined_toast;
}
}

View File

@@ -1,4 +1,4 @@
package org.briarproject.android.forum; package org.briarproject.android.sharing;
import org.briarproject.android.contact.ContactListItem; import org.briarproject.android.contact.ContactListItem;
import org.briarproject.android.contact.ConversationItem; import org.briarproject.android.contact.ConversationItem;

View File

@@ -1,31 +1,31 @@
package org.briarproject.android.forum; package org.briarproject.android.sharing;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.BriarActivity; import org.briarproject.android.BriarActivity;
import org.briarproject.android.fragment.BaseFragment; import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
// TODO extend the BriarFragmentActivity ? public abstract class ShareActivity extends BriarActivity implements
public class ShareForumActivity extends BriarActivity implements
BaseFragment.BaseFragmentListener { BaseFragment.BaseFragmentListener {
public final static String CONTACTS = "contacts"; final static String CONTACTS = "contacts";
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_share_forum); setContentView(R.layout.activity_share);
Intent i = getIntent(); Intent i = getIntent();
byte[] b = i.getByteArrayExtra(GROUP_ID); byte[] b = i.getByteArrayExtra(GROUP_ID);
@@ -36,34 +36,32 @@ public class ShareForumActivity extends BriarActivity implements
ContactSelectorFragment contactSelectorFragment = ContactSelectorFragment contactSelectorFragment =
ContactSelectorFragment.newInstance(groupId); ContactSelectorFragment.newInstance(groupId);
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.add(R.id.shareForumContainer, contactSelectorFragment) .add(R.id.shareContainer, contactSelectorFragment)
.commit(); .commit();
} }
} }
@Override abstract ShareMessageFragment getMessageFragment(GroupId groupId,
public void injectActivity(ActivityComponent component) { Collection<ContactId> contacts);
component.inject(this);
}
public void showMessageScreen(GroupId groupId, abstract boolean isDisabled(GroupId groupId, Contact c) throws DbException;
Collection<ContactId> contacts) {
ShareForumMessageFragment messageFragment = void showMessageScreen(GroupId groupId, Collection<ContactId> contacts) {
ShareForumMessageFragment.newInstance(groupId, contacts); ShareMessageFragment messageFragment =
getMessageFragment(groupId, contacts);
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.setCustomAnimations(android.R.anim.fade_in, .setCustomAnimations(android.R.anim.fade_in,
android.R.anim.fade_out, android.R.anim.fade_out,
android.R.anim.slide_in_left, android.R.anim.slide_in_left,
android.R.anim.slide_out_right) android.R.anim.slide_out_right)
.replace(R.id.shareForumContainer, messageFragment, .replace(R.id.shareContainer, messageFragment,
ContactSelectorFragment.TAG) ContactSelectorFragment.TAG)
.addToBackStack(null) .addToBackStack(null)
.commit(); .commit();
} }
public static ArrayList<Integer> getContactsFromIds( static ArrayList<Integer> getContactsFromIds(
Collection<ContactId> contacts) { Collection<ContactId> contacts) {
// transform ContactIds to Integers so they can be added to a bundle // transform ContactIds to Integers so they can be added to a bundle
@@ -74,13 +72,13 @@ public class ShareForumActivity extends BriarActivity implements
return intContacts; return intContacts;
} }
public void sharingSuccessful(View v) { void sharingSuccessful(View v) {
setResult(RESULT_OK); setResult(RESULT_OK);
hideSoftKeyboard(v); hideSoftKeyboard(v);
supportFinishAfterTransition(); supportFinishAfterTransition();
} }
protected static Collection<ContactId> getContactsFromIntegers( static Collection<ContactId> getContactsFromIntegers(
ArrayList<Integer> intContacts) { ArrayList<Integer> intContacts) {
// turn contact integers from a bundle back to ContactIds // turn contact integers from a bundle back to ContactIds

View File

@@ -0,0 +1,35 @@
package org.briarproject.android.sharing;
import org.briarproject.android.ActivityComponent;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException;
import org.briarproject.api.sync.GroupId;
import java.util.Collection;
import javax.inject.Inject;
public class ShareBlogActivity extends ShareActivity {
@Inject
volatile BlogSharingManager blogSharingManager;
ShareMessageFragment getMessageFragment(GroupId groupId,
Collection<ContactId> contacts) {
return ShareBlogMessageFragment.newInstance(groupId, contacts);
}
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
/**
* This must only be called from a DbThread
*/
boolean isDisabled(GroupId groupId, Contact c) throws DbException {
return !blogSharingManager.canBeShared(groupId, c);
}
}

View File

@@ -0,0 +1,81 @@
package org.briarproject.android.sharing;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException;
import org.briarproject.api.sync.GroupId;
import java.util.Collection;
import javax.inject.Inject;
import static android.widget.Toast.LENGTH_SHORT;
import static java.util.logging.Level.WARNING;
public class ShareBlogMessageFragment extends ShareMessageFragment {
public final static String TAG = ShareBlogMessageFragment.class.getName();
// Fields that are accessed from background threads must be volatile
@Inject
protected volatile BlogSharingManager blogSharingManager;
public static ShareBlogMessageFragment newInstance(GroupId groupId,
Collection<ContactId> contacts) {
ShareBlogMessageFragment fragment = new ShareBlogMessageFragment();
fragment.setArguments(getArguments(groupId, contacts));
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setTitle(R.string.blogs_sharing_share);
View v = super.onCreateView(inflater, container, savedInstanceState);
ui.button.setText(getString(R.string.blogs_sharing_button));
return v;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
protected void share(final String msg) {
listener.runOnDbThread(new Runnable() {
@Override
public void run() {
try {
for (ContactId c : getContacts()) {
blogSharingManager.sendInvitation(getGroupId(), c, msg);
}
} catch (DbException e) {
sharingError();
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
protected void sharingError() {
runOnUiThread(new Runnable() {
@Override
public void run() {
int res = R.string.blogs_sharing_error;
Toast.makeText(getContext(), res, LENGTH_SHORT).show();
}
});
}
}

View File

@@ -0,0 +1,34 @@
package org.briarproject.android.sharing;
import org.briarproject.android.ActivityComponent;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.sync.GroupId;
import java.util.Collection;
import javax.inject.Inject;
public class ShareForumActivity extends ShareActivity {
@Inject
volatile ForumSharingManager forumSharingManager;
ShareMessageFragment getMessageFragment(GroupId groupId,
Collection<ContactId> contacts) {
return ShareForumMessageFragment.newInstance(groupId, contacts);
}
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
/**
* This must only be called from a DbThread
*/
boolean isDisabled(GroupId groupId, Contact c) throws DbException {
return !forumSharingManager.canBeShared(groupId, c);
}
}

View File

@@ -0,0 +1,79 @@
package org.briarproject.android.sharing;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.sync.GroupId;
import java.util.Collection;
import javax.inject.Inject;
import static android.widget.Toast.LENGTH_SHORT;
import static java.util.logging.Level.WARNING;
public class ShareForumMessageFragment extends ShareMessageFragment {
public final static String TAG = ShareForumMessageFragment.class.getName();
// Fields that are accessed from background threads must be volatile
@Inject
protected volatile ForumSharingManager forumSharingManager;
public static ShareForumMessageFragment newInstance(GroupId groupId,
Collection<ContactId> contacts) {
ShareForumMessageFragment fragment = new ShareForumMessageFragment();
fragment.setArguments(getArguments(groupId, contacts));
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setTitle(R.string.forum_share_button);
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
protected void share(final String msg) {
listener.runOnDbThread(new Runnable() {
@Override
public void run() {
try {
for (ContactId c : getContacts()) {
forumSharingManager.
sendInvitation(getGroupId(), c, msg);
}
} catch (DbException e) {
sharingError();
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
protected void sharingError() {
runOnUiThread(new Runnable() {
@Override
public void run() {
int res = R.string.forum_share_error;
Toast.makeText(getContext(), res, LENGTH_SHORT).show();
}
});
}
}

View File

@@ -1,21 +1,18 @@
package org.briarproject.android.forum; package org.briarproject.android.sharing;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.Toast;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.fragment.BaseFragment; import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException;
import org.briarproject.api.forum.ForumSharingManager; import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
@@ -25,42 +22,41 @@ import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import static android.widget.Toast.LENGTH_SHORT; import static org.briarproject.android.sharing.ShareActivity.CONTACTS;
import static java.util.logging.Level.WARNING; import static org.briarproject.android.sharing.ShareActivity.getContactsFromIds;
import static org.briarproject.android.forum.ShareForumActivity.CONTACTS;
import static org.briarproject.android.forum.ShareForumActivity.getContactsFromIds;
import static org.briarproject.api.sharing.SharingConstants.GROUP_ID; import static org.briarproject.api.sharing.SharingConstants.GROUP_ID;
public class ShareForumMessageFragment extends BaseFragment { abstract class ShareMessageFragment extends BaseFragment {
public final static String TAG = "IntroductionMessageFragment"; public final static String TAG = ShareMessageFragment.class.getName();
private static final Logger LOG = protected static final Logger LOG = Logger.getLogger(TAG);
Logger.getLogger(ShareForumMessageFragment.class.getName());
private ShareForumActivity shareForumActivity; protected ViewHolder ui;
private ViewHolder ui; private ShareActivity shareActivity;
// Fields that are accessed from background threads must be volatile // Fields that are accessed from background threads must be volatile
@Inject @Inject
protected volatile ForumSharingManager forumSharingManager; protected volatile ForumSharingManager forumSharingManager;
@Inject
protected volatile BlogSharingManager blogSharingManager;
private volatile GroupId groupId; private volatile GroupId groupId;
private volatile Collection<ContactId> contacts; private volatile Collection<ContactId> contacts;
public static ShareForumMessageFragment newInstance(GroupId groupId, Collection<ContactId> contacts) { protected static Bundle getArguments(GroupId groupId,
Collection<ContactId> contacts) {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putByteArray(GROUP_ID, groupId.getBytes()); args.putByteArray(GROUP_ID, groupId.getBytes());
args.putIntegerArrayList(CONTACTS, getContactsFromIds(contacts)); args.putIntegerArrayList(CONTACTS, getContactsFromIds(contacts));
ShareForumMessageFragment fragment = new ShareForumMessageFragment(); return args;
fragment.setArguments(args);
return fragment;
} }
@Override @Override
public void onAttach(Context context) { public void onAttach(Context context) {
super.onAttach(context); super.onAttach(context);
try { try {
shareForumActivity = (ShareForumActivity) context; shareActivity = (ShareActivity) context;
} catch (ClassCastException e) { } catch (ClassCastException e) {
throw new InstantiationError( throw new InstantiationError(
"This fragment is only meant to be attached to the ShareForumActivity"); "This fragment is only meant to be attached to the ShareForumActivity");
@@ -71,17 +67,18 @@ public class ShareForumMessageFragment extends BaseFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
// change toolbar text // allow for "up" button to act as back button
ActionBar actionBar = shareForumActivity.getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(R.string.forum_share_button);
}
// allow for home button to act as back button
setHasOptionsMenu(true); setHasOptionsMenu(true);
// get groupID and contactIDs from fragment arguments
groupId = new GroupId(getArguments().getByteArray(GROUP_ID));
ArrayList<Integer> intContacts =
getArguments().getIntegerArrayList(CONTACTS);
if (intContacts == null) throw new IllegalArgumentException();
contacts = ShareActivity.getContactsFromIntegers(intContacts);
// inflate view // inflate view
View v = inflater.inflate(R.layout.share_forum_message, container, View v = inflater.inflate(R.layout.fragment_share_message, container,
false); false);
ui = new ViewHolder(v); ui = new ViewHolder(v);
ui.button.setOnClickListener(new View.OnClickListener() { ui.button.setOnClickListener(new View.OnClickListener() {
@@ -91,13 +88,6 @@ public class ShareForumMessageFragment extends BaseFragment {
} }
}); });
// get groupID and contactIDs from fragment arguments
groupId = new GroupId(getArguments().getByteArray(GROUP_ID));
ArrayList<Integer> intContacts =
getArguments().getIntegerArrayList(CONTACTS);
if (intContacts == null) throw new IllegalArgumentException();
contacts = ShareForumActivity.getContactsFromIntegers(intContacts);
return v; return v;
} }
@@ -105,7 +95,7 @@ public class ShareForumMessageFragment extends BaseFragment {
public boolean onOptionsItemSelected(final MenuItem item) { public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case android.R.id.home: case android.R.id.home:
shareForumActivity.onBackPressed(); shareActivity.onBackPressed();
return true; return true;
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
@@ -117,54 +107,40 @@ public class ShareForumMessageFragment extends BaseFragment {
return TAG; return TAG;
} }
@Override protected void setTitle(int res) {
public void injectFragment(ActivityComponent component) { shareActivity.setTitle(res);
component.inject(this);
} }
public void onButtonClick() { private void onButtonClick() {
// disable button to prevent accidental double invitations // disable button to prevent accidental double invitations
ui.button.setEnabled(false); ui.button.setEnabled(false);
String msg = ui.message.getText().toString(); String msg = ui.message.getText().toString();
shareForum(msg); share(msg);
// don't wait for the introduction to be made before finishing activity // don't wait for the invitation to be made before finishing activity
shareForumActivity.sharingSuccessful(ui.message); shareActivity.sharingSuccessful(ui.message);
} }
private void shareForum(final String msg) { abstract void share(final String msg);
shareForumActivity.runOnDbThread(new Runnable() {
@Override abstract void sharingError();
public void run() {
try { protected Collection<ContactId> getContacts() {
for (ContactId c : contacts) { return contacts;
forumSharingManager.sendInvitation(groupId, c,
msg);
}
} catch (DbException e) {
sharingError();
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
} }
private void sharingError() { protected GroupId getGroupId() {
shareForumActivity.runOnUiThread(new Runnable() { return groupId;
@Override
public void run() {
Toast.makeText(shareForumActivity,
R.string.introduction_error, LENGTH_SHORT).show();
}
});
} }
private static class ViewHolder { protected void runOnUiThread(Runnable runnable) {
listener.runOnUiThread(runnable);
}
private final EditText message; protected static class ViewHolder {
private final Button button; protected final EditText message;
protected final Button button;
ViewHolder(View v) { ViewHolder(View v) {
message = (EditText) v.findViewById(R.id.invitationMessageView); message = (EditText) v.findViewById(R.id.invitationMessageView);

View File

@@ -1,4 +1,4 @@
package org.briarproject.android.forum; package org.briarproject.android.sharing;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
@@ -6,13 +6,11 @@ import android.support.v7.widget.LinearLayoutManager;
import android.view.MenuItem; import android.view.MenuItem;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.BriarActivity; import org.briarproject.android.BriarActivity;
import org.briarproject.android.contact.ContactListItem; import org.briarproject.android.contact.ContactListItem;
import org.briarproject.android.util.BriarRecyclerView; import org.briarproject.android.util.BriarRecyclerView;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.forum.ForumSharingManager;
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;
@@ -26,26 +24,24 @@ import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
public class ForumSharingStatusActivity extends BriarActivity { abstract class SharingStatusActivity extends BriarActivity {
private GroupId groupId; private GroupId groupId;
private BriarRecyclerView sharedByList, sharedWithList; private BriarRecyclerView sharedByList, sharedWithList;
private ForumSharingStatusAdapter sharedByAdapter, sharedWithAdapter; private SharingStatusAdapter sharedByAdapter, sharedWithAdapter;
// Fields that are accessed from background threads must be volatile // Fields that are accessed from background threads must be volatile
@Inject @Inject
protected volatile ForumSharingManager forumSharingManager;
@Inject
protected volatile IdentityManager identityManager; protected volatile IdentityManager identityManager;
public final static String TAG = "ForumSharingStatusActivity"; public final static String TAG = SharingStatusActivity.class.getName();
private static final Logger LOG = Logger.getLogger(TAG); private static final Logger LOG = Logger.getLogger(TAG);
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_forum_sharing_status); setContentView(R.layout.activity_sharing_status);
Intent i = getIntent(); Intent i = getIntent();
byte[] b = i.getByteArrayExtra(GROUP_ID); byte[] b = i.getByteArrayExtra(GROUP_ID);
@@ -53,13 +49,13 @@ public class ForumSharingStatusActivity extends BriarActivity {
groupId = new GroupId(b); groupId = new GroupId(b);
sharedByList = (BriarRecyclerView) findViewById(R.id.sharedByView); sharedByList = (BriarRecyclerView) findViewById(R.id.sharedByView);
sharedByAdapter = new ForumSharingStatusAdapter(this); sharedByAdapter = new SharingStatusAdapter(this);
sharedByList.setLayoutManager(new LinearLayoutManager(this)); sharedByList.setLayoutManager(new LinearLayoutManager(this));
sharedByList.setAdapter(sharedByAdapter); sharedByList.setAdapter(sharedByAdapter);
sharedByList.setEmptyText(getString(R.string.nobody)); sharedByList.setEmptyText(getString(R.string.nobody));
sharedWithList = (BriarRecyclerView) findViewById(R.id.sharedWithView); sharedWithList = (BriarRecyclerView) findViewById(R.id.sharedWithView);
sharedWithAdapter = new ForumSharingStatusAdapter(this); sharedWithAdapter = new SharingStatusAdapter(this);
sharedWithList.setLayoutManager(new LinearLayoutManager(this)); sharedWithList.setLayoutManager(new LinearLayoutManager(this));
sharedWithList.setAdapter(sharedWithAdapter); sharedWithList.setAdapter(sharedWithAdapter);
sharedWithList.setEmptyText(getString(R.string.nobody)); sharedWithList.setEmptyText(getString(R.string.nobody));
@@ -85,9 +81,18 @@ public class ForumSharingStatusActivity extends BriarActivity {
} }
} }
@Override /**
public void injectActivity(ActivityComponent component) { * This must only be called from the DbThread
component.inject(this); */
abstract protected Collection<Contact> getSharedWith() throws DbException;
/**
* This must only be called from the DbThread
*/
abstract protected Collection<Contact> getSharedBy() throws DbException;
protected GroupId getGroupId() {
return groupId;
} }
private void loadSharedBy() { private void loadSharedBy() {
@@ -96,9 +101,7 @@ public class ForumSharingStatusActivity extends BriarActivity {
public void run() { public void run() {
List<ContactListItem> contactItems = new ArrayList<>(); List<ContactListItem> contactItems = new ArrayList<>();
try { try {
Collection<Contact> contacts = for (Contact c : getSharedBy()) {
forumSharingManager.getSharedBy(groupId);
for (Contact c : contacts) {
LocalAuthor localAuthor = identityManager LocalAuthor localAuthor = identityManager
.getLocalAuthor(c.getLocalAuthorId()); .getLocalAuthor(c.getLocalAuthorId());
ContactListItem item = ContactListItem item =
@@ -134,9 +137,7 @@ public class ForumSharingStatusActivity extends BriarActivity {
public void run() { public void run() {
List<ContactListItem> contactItems = new ArrayList<>(); List<ContactListItem> contactItems = new ArrayList<>();
try { try {
Collection<Contact> contacts = for (Contact c : getSharedWith()) {
forumSharingManager.getSharedWith(groupId);
for (Contact c : contacts) {
LocalAuthor localAuthor = identityManager LocalAuthor localAuthor = identityManager
.getLocalAuthor(c.getLocalAuthorId()); .getLocalAuthor(c.getLocalAuthorId());
ContactListItem item = ContactListItem item =

View File

@@ -1,22 +1,18 @@
package org.briarproject.android.forum; package org.briarproject.android.sharing;
import android.content.Context; import android.content.Context;
import android.support.v4.content.ContextCompat;
import android.text.format.DateUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.contact.BaseContactListAdapter; import org.briarproject.android.contact.BaseContactListAdapter;
import org.briarproject.android.contact.ContactListItem; import org.briarproject.android.contact.ContactListItem;
public class ForumSharingStatusAdapter class SharingStatusAdapter
extends BaseContactListAdapter<BaseContactListAdapter.BaseContactHolder> { extends BaseContactListAdapter<BaseContactListAdapter.BaseContactHolder> {
public ForumSharingStatusAdapter(Context context) { SharingStatusAdapter(Context context) {
super(context, null); super(context, null);
} }

View File

@@ -0,0 +1,37 @@
package org.briarproject.android.sharing;
import org.briarproject.android.ActivityComponent;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.db.DbException;
import java.util.Collection;
import javax.inject.Inject;
public class SharingStatusBlogActivity extends SharingStatusActivity {
// Fields that are accessed from background threads must be volatile
@Inject
protected volatile BlogSharingManager blogSharingManager;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
/**
* This must only be called from the DbThread
*/
protected Collection<Contact> getSharedWith() throws DbException {
return blogSharingManager.getSharedWith(getGroupId());
}
/**
* This must only be called from the DbThread
*/
protected Collection<Contact> getSharedBy() throws DbException {
return blogSharingManager.getSharedBy(getGroupId());
}
}

View File

@@ -0,0 +1,37 @@
package org.briarproject.android.sharing;
import org.briarproject.android.ActivityComponent;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.db.DbException;
import org.briarproject.api.forum.ForumSharingManager;
import java.util.Collection;
import javax.inject.Inject;
public class SharingStatusForumActivity extends SharingStatusActivity {
// Fields that are accessed from background threads must be volatile
@Inject
protected volatile ForumSharingManager forumSharingManager;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
/**
* This must only be called from the DbThread
*/
protected Collection<Contact> getSharedWith() throws DbException {
return forumSharingManager.getSharedWith(getGroupId());
}
/**
* This must only be called from the DbThread
*/
protected Collection<Contact> getSharedBy() throws DbException {
return forumSharingManager.getSharedBy(getGroupId());
}
}

View File

@@ -2,6 +2,7 @@ package org.briarproject.android.util;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v7.widget.AppCompatTextView; import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet; import android.util.AttributeSet;
@@ -10,8 +11,10 @@ import android.widget.FrameLayout;
import android.widget.TextView; import android.widget.TextView;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.api.identity.Author;
import de.hdodenhof.circleimageview.CircleImageView; import de.hdodenhof.circleimageview.CircleImageView;
import im.delight.android.identicons.IdenticonDrawable;
public class TextAvatarView extends FrameLayout { public class TextAvatarView extends FrameLayout {
@@ -83,4 +86,10 @@ public class TextAvatarView extends FrameLayout {
} }
} }
public void setAuthorAvatar(Author author) {
Drawable drawable = new IdenticonDrawable(author.getId().getBytes());
background.setImageDrawable(drawable);
character.setVisibility(GONE);
}
} }

View File

@@ -8,20 +8,20 @@ import org.briarproject.api.sync.MessageId;
public class BlogInvitationRequest extends InvitationRequest { public class BlogInvitationRequest extends InvitationRequest {
private final String blogTitle; private final String blogAuthorName;
public BlogInvitationRequest(MessageId id, SessionId sessionId, public BlogInvitationRequest(MessageId id, SessionId sessionId,
ContactId contactId, String blogTitle, String message, ContactId contactId, String blogAuthorName, String message,
boolean available, long time, boolean local, boolean sent, boolean available, long time, boolean local, boolean sent,
boolean seen, boolean read) { boolean seen, boolean read) {
super(id, sessionId, contactId, message, available, time, local, sent, super(id, sessionId, contactId, message, available, time, local, sent,
seen, read); seen, read);
this.blogTitle = blogTitle; this.blogAuthorName = blogAuthorName;
} }
public String getBlogTitle() { public String getBlogAuthorName() {
return blogTitle; return blogAuthorName;
} }
} }

View File

@@ -0,0 +1,17 @@
package org.briarproject.api.blogs;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.sharing.InvitationResponse;
import org.briarproject.api.sync.MessageId;
public class BlogInvitationResponse extends InvitationResponse {
public BlogInvitationResponse(MessageId id, SessionId sessionId,
ContactId contactId, boolean accept, long time, boolean local,
boolean sent, boolean seen, boolean read) {
super(id, sessionId, contactId, accept, time, local, sent, seen, read);
}
}

View File

@@ -36,7 +36,7 @@ public interface BlogManager {
Collection<Blog> getBlogs(LocalAuthor localAuthor) throws DbException; Collection<Blog> getBlogs(LocalAuthor localAuthor) throws DbException;
/** Returns only the personal blog of the given author. */ /** Returns only the personal blog of the given author. */
Blog getPersonalBlog(Author author) throws DbException; Blog getPersonalBlog(Author author);
/** Returns all blogs to which the user subscribes. */ /** Returns all blogs to which the user subscribes. */
Collection<Blog> getBlogs() throws DbException; Collection<Blog> getBlogs() throws DbException;

View File

@@ -3,14 +3,14 @@ package org.briarproject.api.blogs;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.sharing.InvitationMessage;
import org.briarproject.api.sharing.SharingManager; import org.briarproject.api.sharing.SharingManager;
import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import java.util.Collection; import java.util.Collection;
public interface BlogSharingManager public interface BlogSharingManager extends SharingManager<Blog> {
extends SharingManager<Blog, BlogInvitationRequest> {
/** /**
* Returns the unique ID of the blog sharing client. * Returns the unique ID of the blog sharing client.
@@ -34,7 +34,7 @@ public interface BlogSharingManager
* Returns all blogs sharing messages sent by the Contact * Returns all blogs sharing messages sent by the Contact
* identified by contactId. * identified by contactId.
*/ */
Collection<BlogInvitationRequest> getInvitationMessages( Collection<InvitationMessage> getInvitationMessages(
ContactId contactId) throws DbException; ContactId contactId) throws DbException;
/** /**

View File

@@ -10,7 +10,7 @@ import org.briarproject.api.sync.GroupId;
import java.util.Collection; import java.util.Collection;
public interface ForumSharingManager extends SharingManager<Forum, InvitationMessage> { public interface ForumSharingManager extends SharingManager<Forum> {
/** Returns the unique ID of the forum sharing client. */ /** Returns the unique ID of the forum sharing client. */
ClientId getClientId(); ClientId getClientId();

View File

@@ -20,6 +20,12 @@ public interface IdentityManager {
/** Returns the local pseudonym with the given ID. */ /** Returns the local pseudonym with the given ID. */
LocalAuthor getLocalAuthor(AuthorId a) throws DbException; LocalAuthor getLocalAuthor(AuthorId a) throws DbException;
/** Returns the main local identity. */
LocalAuthor getLocalAuthor() throws DbException;
/** Returns the main local identity within the given Transaction. */
LocalAuthor getLocalAuthor(Transaction txn) throws DbException;
/** Returns all local pseudonyms. */ /** Returns all local pseudonyms. */
Collection<LocalAuthor> getLocalAuthors() throws DbException; Collection<LocalAuthor> getLocalAuthors() throws DbException;

View File

@@ -8,7 +8,7 @@ import org.briarproject.api.sync.GroupId;
import java.util.Collection; import java.util.Collection;
public interface SharingManager<S extends Shareable, IM extends InvitationMessage> { public interface SharingManager<S extends Shareable> {
/** Returns the unique ID of the group sharing client. */ /** Returns the unique ID of the group sharing client. */
ClientId getClientId(); ClientId getClientId();
@@ -30,7 +30,7 @@ public interface SharingManager<S extends Shareable, IM extends InvitationMessag
* Returns all group sharing messages sent by the Contact * Returns all group sharing messages sent by the Contact
* identified by contactId. * identified by contactId.
*/ */
Collection<IM> getInvitationMessages( Collection<InvitationMessage> getInvitationMessages(
ContactId contactId) throws DbException; ContactId contactId) throws DbException;
/** Returns all shareables to which the user has been invited. */ /** Returns all shareables to which the user has been invited. */

View File

@@ -69,7 +69,7 @@ class BlogFactoryImpl implements BlogFactory {
Author a = Author a =
authorFactory.createAuthor(blog.getString(1), blog.getRaw(2)); authorFactory.createAuthor(blog.getString(1), blog.getRaw(2));
// TODO change permanent depending on how this will be used // TODO change permanent depending on how this will be used
boolean permanent = false; boolean permanent = true;
return new Blog(g, blog.getString(0), description, a, permanent); return new Blog(g, blog.getString(0), description, a, permanent);
} }

View File

@@ -282,7 +282,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
} }
@Override @Override
public Blog getPersonalBlog(Author author) throws DbException { public Blog getPersonalBlog(Author author) {
return blogFactory.createPersonalBlog(author); return blogFactory.createPersonalBlog(author);
} }

View File

@@ -10,9 +10,7 @@ import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import javax.inject.Inject; import javax.inject.Inject;
@@ -69,12 +67,22 @@ class IdentityManagerImpl implements IdentityManager {
return author; return author;
} }
@Override
public LocalAuthor getLocalAuthor() throws DbException {
return getLocalAuthors().iterator().next();
}
@Override
public LocalAuthor getLocalAuthor(Transaction txn) throws DbException {
return getLocalAuthors(txn).iterator().next();
}
@Override @Override
public Collection<LocalAuthor> getLocalAuthors() throws DbException { public Collection<LocalAuthor> getLocalAuthors() throws DbException {
Collection<LocalAuthor> authors; Collection<LocalAuthor> authors;
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
authors = db.getLocalAuthors(txn); authors = getLocalAuthors(txn);
txn.setComplete(); txn.setComplete();
} finally { } finally {
db.endTransaction(txn); db.endTransaction(txn);
@@ -82,6 +90,12 @@ class IdentityManagerImpl implements IdentityManager {
return authors; return authors;
} }
private Collection<LocalAuthor> getLocalAuthors(Transaction txn)
throws DbException {
return db.getLocalAuthors(txn);
}
@Override @Override
public void removeLocalAuthor(AuthorId a) throws DbException { public void removeLocalAuthor(AuthorId a) throws DbException {
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);

View File

@@ -4,6 +4,7 @@ import org.briarproject.api.FormatException;
import org.briarproject.api.blogs.Blog; import org.briarproject.api.blogs.Blog;
import org.briarproject.api.blogs.BlogFactory; import org.briarproject.api.blogs.BlogFactory;
import org.briarproject.api.blogs.BlogInvitationRequest; import org.briarproject.api.blogs.BlogInvitationRequest;
import org.briarproject.api.blogs.BlogInvitationResponse;
import org.briarproject.api.blogs.BlogManager; import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogManager.RemoveBlogHook; import org.briarproject.api.blogs.BlogManager.RemoveBlogHook;
import org.briarproject.api.blogs.BlogSharingManager; import org.briarproject.api.blogs.BlogSharingManager;
@@ -12,6 +13,7 @@ import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager; import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.PrivateGroupFactory; import org.briarproject.api.clients.PrivateGroupFactory;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList; import org.briarproject.api.data.BdfList;
@@ -24,6 +26,9 @@ import org.briarproject.api.event.BlogInvitationReceivedEvent;
import org.briarproject.api.event.BlogInvitationResponseReceivedEvent; import org.briarproject.api.event.BlogInvitationResponseReceivedEvent;
import org.briarproject.api.identity.Author; import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorFactory; import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.sharing.InvitationMessage;
import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
@@ -40,13 +45,17 @@ import static org.briarproject.api.blogs.BlogConstants.BLOG_PUBLIC_KEY;
import static org.briarproject.api.blogs.BlogConstants.BLOG_TITLE; import static org.briarproject.api.blogs.BlogConstants.BLOG_TITLE;
class BlogSharingManagerImpl extends class BlogSharingManagerImpl extends
SharingManagerImpl<Blog, BlogInvitation, BlogInvitationRequest, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationReceivedEvent, BlogInvitationResponseReceivedEvent> SharingManagerImpl<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationReceivedEvent, BlogInvitationResponseReceivedEvent>
implements BlogSharingManager, RemoveBlogHook { implements BlogSharingManager, RemoveBlogHook {
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString( static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
"bee438b5de0b3a685badc4e49d76e72d" "bee438b5de0b3a685badc4e49d76e72d"
+ "21e01c4b569a775112756bdae267a028")); + "21e01c4b569a775112756bdae267a028"));
@Inject
IdentityManager identityManager;
private final BlogManager blogManager;
private final SFactory sFactory; private final SFactory sFactory;
private final IFactory iFactory; private final IFactory iFactory;
private final ISFactory isFactory; private final ISFactory isFactory;
@@ -64,6 +73,7 @@ class BlogSharingManagerImpl extends
super(db, messageQueueManager, clientHelper, metadataParser, super(db, messageQueueManager, clientHelper, metadataParser,
metadataEncoder, random, privateGroupFactory, clock); metadataEncoder, random, privateGroupFactory, clock);
this.blogManager = blogManager;
sFactory = new SFactory(authorFactory, blogFactory, blogManager); sFactory = new SFactory(authorFactory, blogFactory, blogManager);
iFactory = new IFactory(); iFactory = new IFactory();
isFactory = new ISFactory(); isFactory = new ISFactory();
@@ -78,21 +88,37 @@ class BlogSharingManagerImpl extends
} }
@Override @Override
protected BlogInvitationRequest createInvitationRequest(MessageId id, protected boolean canBeShared(Transaction txn, GroupId g, Contact c)
throws DbException {
// check if g is our personal blog
LocalAuthor author = identityManager.getLocalAuthor(txn);
Blog b = blogManager.getPersonalBlog(author);
if (b.getId().equals(g)) return false;
// check if g is c's personal blog
b = blogManager.getPersonalBlog(c.getAuthor());
if (b.getId().equals(g)) return false;
return super.canBeShared(txn, g, c);
}
@Override
protected InvitationMessage createInvitationRequest(MessageId id,
BlogInvitation msg, ContactId contactId, boolean available, BlogInvitation msg, ContactId contactId, boolean available,
long time, boolean local, boolean sent, boolean seen, long time, boolean local, boolean sent, boolean seen,
boolean read) { boolean read) {
return new BlogInvitationRequest(id, msg.getSessionId(), contactId, return new BlogInvitationRequest(id, msg.getSessionId(), contactId,
msg.getBlogTitle(), msg.getMessage(), available, time, local, msg.getBlogAuthorName(), msg.getMessage(), available, time,
sent, seen, read); local, sent, seen, read);
} }
@Override @Override
protected BlogInvitationRequest createInvitationResponse(MessageId id, protected InvitationMessage createInvitationResponse(MessageId id,
SessionId sessionId, ContactId contactId, boolean accept, long time, SessionId sessionId, ContactId contactId, boolean accept, long time,
boolean local, boolean sent, boolean seen, boolean read) { boolean local, boolean sent, boolean seen, boolean read) {
// TODO implement when doing blog sharing return new BlogInvitationResponse(id, sessionId, contactId, accept,
return null; time, local, sent, seen, read);
} }
@Override @Override

View File

@@ -58,7 +58,7 @@ class BlogSharingValidator extends BdfMessageValidator {
checkLength(name, 1, MAX_BLOG_TITLE_LENGTH); checkLength(name, 1, MAX_BLOG_TITLE_LENGTH);
String desc = body.getString(3); String desc = body.getString(3);
checkLength(desc, 1, MAX_BLOG_DESC_LENGTH); checkLength(desc, 0, MAX_BLOG_DESC_LENGTH);
BdfList author = body.getList(4); BdfList author = body.getList(4);
checkSize(author, 2); checkSize(author, 2);

View File

@@ -37,7 +37,7 @@ import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT; import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
class ForumSharingManagerImpl extends class ForumSharingManagerImpl extends
SharingManagerImpl<Forum, ForumInvitation, InvitationMessage, ForumInviteeSessionState, ForumSharerSessionState, ForumInvitationReceivedEvent, ForumInvitationResponseReceivedEvent> SharingManagerImpl<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState, ForumInvitationReceivedEvent, ForumInvitationResponseReceivedEvent>
implements ForumSharingManager, ForumManager.RemoveForumHook { implements ForumSharingManager, ForumManager.RemoveForumHook {
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString( static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(

View File

@@ -83,9 +83,9 @@ import static org.briarproject.api.sharing.SharingMessage.BaseMessage;
import static org.briarproject.api.sharing.SharingMessage.Invitation; import static org.briarproject.api.sharing.SharingMessage.Invitation;
import static org.briarproject.sharing.InviteeSessionState.State.AWAIT_LOCAL_RESPONSE; import static org.briarproject.sharing.InviteeSessionState.State.AWAIT_LOCAL_RESPONSE;
abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IM extends InvitationMessage, IS extends InviteeSessionState, SS extends SharerSessionState, IR extends InvitationReceivedEvent, IRR extends InvitationResponseReceivedEvent> abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS extends InviteeSessionState, SS extends SharerSessionState, IR extends InvitationReceivedEvent, IRR extends InvitationResponseReceivedEvent>
extends BdfIncomingMessageHook extends BdfIncomingMessageHook
implements SharingManager<S, IM>, Client, AddContactHook, implements SharingManager<S>, Client, AddContactHook,
RemoveContactHook { RemoveContactHook {
private static final Logger LOG = private static final Logger LOG =
@@ -117,11 +117,11 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IM
public abstract ClientId getClientId(); public abstract ClientId getClientId();
protected abstract IM createInvitationRequest(MessageId id, I msg, protected abstract InvitationMessage createInvitationRequest(MessageId id, I msg,
ContactId contactId, boolean available, long time, boolean local, ContactId contactId, boolean available, long time, boolean local,
boolean sent, boolean seen, boolean read); boolean sent, boolean seen, boolean read);
protected abstract IM createInvitationResponse(MessageId id, protected abstract InvitationMessage createInvitationResponse(MessageId id,
SessionId sessionId, ContactId contactId, boolean accept, long time, SessionId sessionId, ContactId contactId, boolean accept, long time,
boolean local, boolean sent, boolean seen, boolean read); boolean local, boolean sent, boolean seen, boolean read);
@@ -326,7 +326,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IM
} }
@Override @Override
public Collection<IM> getInvitationMessages(ContactId contactId) public Collection<InvitationMessage> getInvitationMessages(ContactId contactId)
throws DbException { throws DbException {
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
@@ -334,7 +334,8 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IM
Contact contact = db.getContact(txn, contactId); Contact contact = db.getContact(txn, contactId);
Group group = getContactGroup(contact); Group group = getContactGroup(contact);
Collection<IM> list = new ArrayList<IM>(); Collection<InvitationMessage> list =
new ArrayList<InvitationMessage>();
Map<MessageId, BdfDictionary> map = clientHelper Map<MessageId, BdfDictionary> map = clientHelper
.getMessageMetadataAsDictionary(txn, group.getId()); .getMessageMetadataAsDictionary(txn, group.getId());
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) { for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
@@ -362,7 +363,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IM
available = ((InviteeSessionState) s).getState() == available = ((InviteeSessionState) s).getState() ==
AWAIT_LOCAL_RESPONSE; AWAIT_LOCAL_RESPONSE;
} }
IM im = createInvitationRequest(m.getKey(), msg, InvitationMessage im = createInvitationRequest(m.getKey(), msg,
contactId, available, time, local, contactId, available, time, local,
status.isSent(), status.isSeen(), read); status.isSent(), status.isSeen(), read);
list.add(im); list.add(im);
@@ -373,9 +374,9 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IM
BaseMessage msg = BaseMessage BaseMessage msg = BaseMessage
.from(getIFactory(), group.getId(), d); .from(getIFactory(), group.getId(), d);
SessionId sessionId = msg.getSessionId(); SessionId sessionId = msg.getSessionId();
IM im = createInvitationResponse(m.getKey(), sessionId, InvitationMessage im = createInvitationResponse(
contactId, accept, time, local, m.getKey(), sessionId, contactId, accept, time,
status.isSent(), status.isSeen(), read); local, status.isSent(), status.isSeen(), read);
list.add(im); list.add(im);
} }
else { else {
@@ -499,7 +500,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IM
return canBeShared; return canBeShared;
} }
private boolean canBeShared(Transaction txn, GroupId g, Contact c) protected boolean canBeShared(Transaction txn, GroupId g, Contact c)
throws DbException { throws DbException {
try { try {

View File

@@ -27,6 +27,8 @@ public class SharingModule {
ForumSharingValidator forumSharingValidator; ForumSharingValidator forumSharingValidator;
@Inject @Inject
ForumSharingManager forumSharingManager; ForumSharingManager forumSharingManager;
@Inject
BlogSharingManager blogSharingManager;
} }
@Provides @Provides