Compare commits
3 Commits
377_replac
...
115-tor-pe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84b3670624 | ||
|
|
749695187e | ||
|
|
c4db72abf2 |
28
.idea/codeStyleSettings.xml
generated
@@ -37,34 +37,6 @@
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="ANNOTATION_PARAMETER_WRAP" value="1" />
|
||||
</JavaCodeStyleSettings>
|
||||
<Objective-C-extensions>
|
||||
<option name="GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES" value="ASK" />
|
||||
<option name="RELEASE_STYLE" value="IVAR" />
|
||||
<option name="TYPE_QUALIFIERS_PLACEMENT" value="BEFORE" />
|
||||
<file>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
|
||||
</file>
|
||||
<class>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
|
||||
</class>
|
||||
<extensions>
|
||||
<pair source="cpp" header="h" />
|
||||
<pair source="c" header="h" />
|
||||
</extensions>
|
||||
</Objective-C-extensions>
|
||||
<XML>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.briarproject;
|
||||
import net.jodah.concurrentunit.Waiter;
|
||||
|
||||
import org.briarproject.api.blogs.Blog;
|
||||
import org.briarproject.api.blogs.BlogCommentHeader;
|
||||
import org.briarproject.api.blogs.BlogFactory;
|
||||
import org.briarproject.api.blogs.BlogManager;
|
||||
import org.briarproject.api.blogs.BlogPost;
|
||||
@@ -51,13 +50,11 @@ import javax.inject.Inject;
|
||||
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
|
||||
import static org.briarproject.api.blogs.MessageType.COMMENT;
|
||||
import static org.briarproject.api.blogs.MessageType.POST;
|
||||
import static org.briarproject.api.blogs.MessageType.WRAPPED_COMMENT;
|
||||
import static org.briarproject.api.blogs.MessageType.WRAPPED_POST;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.PENDING;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.VALID;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -92,6 +89,7 @@ public class BlogManagerTest {
|
||||
private final int TIMEOUT = 15000;
|
||||
private final String AUTHOR1 = "Author 1";
|
||||
private final String AUTHOR2 = "Author 2";
|
||||
private final String CONTENT_TYPE = "text/plain";
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ForumSharingIntegrationTest.class.getName());
|
||||
@@ -174,15 +172,15 @@ public class BlogManagerTest {
|
||||
defaultInit();
|
||||
|
||||
// check that blog0 has no posts
|
||||
final String body = TestUtils.getRandomString(42);
|
||||
final byte[] body = TestUtils.getRandomBytes(42);
|
||||
Collection<BlogPostHeader> headers0 =
|
||||
blogManager0.getPostHeaders(blog0.getId());
|
||||
assertEquals(0, headers0.size());
|
||||
|
||||
// add a post to blog0
|
||||
BlogPost p = blogPostFactory
|
||||
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
|
||||
author0, body);
|
||||
.createBlogPost(blog0.getId(), null, clock.currentTimeMillis(),
|
||||
null, author0, CONTENT_TYPE, body);
|
||||
blogManager0.addLocalPost(p);
|
||||
|
||||
// check that post is now in blog0
|
||||
@@ -190,7 +188,8 @@ public class BlogManagerTest {
|
||||
assertEquals(1, headers0.size());
|
||||
|
||||
// check that body is there
|
||||
assertEquals(body, blogManager0.getPostBody(p.getMessage().getId()));
|
||||
assertArrayEquals(body,
|
||||
blogManager0.getPostBody(p.getMessage().getId()));
|
||||
|
||||
// make sure that blog0 at author1 doesn't have the post yet
|
||||
Collection<BlogPostHeader> headers1 =
|
||||
@@ -204,10 +203,10 @@ public class BlogManagerTest {
|
||||
// make sure post arrived
|
||||
headers1 = blogManager1.getPostHeaders(blog0.getId());
|
||||
assertEquals(1, headers1.size());
|
||||
assertEquals(POST, headers1.iterator().next().getType());
|
||||
|
||||
// check that body is there
|
||||
assertEquals(body, blogManager1.getPostBody(p.getMessage().getId()));
|
||||
assertArrayEquals(body,
|
||||
blogManager1.getPostBody(p.getMessage().getId()));
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
@@ -218,10 +217,10 @@ public class BlogManagerTest {
|
||||
defaultInit();
|
||||
|
||||
// add a post to blog1
|
||||
final String body = TestUtils.getRandomString(42);
|
||||
final byte[] body = TestUtils.getRandomBytes(42);
|
||||
BlogPost p = blogPostFactory
|
||||
.createBlogPost(blog1.getId(), clock.currentTimeMillis(), null,
|
||||
author0, body);
|
||||
.createBlogPost(blog1.getId(), null, clock.currentTimeMillis(),
|
||||
null, author0, CONTENT_TYPE, body);
|
||||
blogManager0.addLocalPost(p);
|
||||
|
||||
// check that post is now in blog1
|
||||
@@ -288,261 +287,20 @@ public class BlogManagerTest {
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlogComment() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// add a post to blog0
|
||||
final String body = TestUtils.getRandomString(42);
|
||||
BlogPost p = blogPostFactory
|
||||
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
|
||||
author0, body);
|
||||
blogManager0.addLocalPost(p);
|
||||
|
||||
// sync the post over
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// make sure post arrived
|
||||
Collection<BlogPostHeader> headers1 =
|
||||
blogManager1.getPostHeaders(blog0.getId());
|
||||
assertEquals(1, headers1.size());
|
||||
assertEquals(POST, headers1.iterator().next().getType());
|
||||
|
||||
// 1 adds a comment to that blog post
|
||||
String comment = "This is a comment on a blog post!";
|
||||
blogManager1
|
||||
.addLocalComment(author1, blog1.getId(), comment,
|
||||
headers1.iterator().next());
|
||||
|
||||
// sync comment over
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 2);
|
||||
|
||||
// make sure comment and wrapped post arrived
|
||||
Collection<BlogPostHeader> headers0 =
|
||||
blogManager0.getPostHeaders(blog1.getId());
|
||||
assertEquals(1, headers0.size());
|
||||
assertEquals(COMMENT, headers0.iterator().next().getType());
|
||||
BlogCommentHeader h = (BlogCommentHeader) headers0.iterator().next();
|
||||
assertEquals(author0, h.getParent().getAuthor());
|
||||
|
||||
// ensure that body can be retrieved from wrapped post
|
||||
assertEquals(body, blogManager0.getPostBody(h.getParentId()));
|
||||
|
||||
// 1 has only their own comment in their blog
|
||||
headers1 = blogManager1.getPostHeaders(blog1.getId());
|
||||
assertEquals(1, headers1.size());
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlogCommentOnOwnPost() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// add a post to blog0
|
||||
final String body = TestUtils.getRandomString(42);
|
||||
BlogPost p = blogPostFactory
|
||||
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
|
||||
author0, body);
|
||||
blogManager0.addLocalPost(p);
|
||||
|
||||
// get header of own post
|
||||
Collection<BlogPostHeader> headers0 =
|
||||
blogManager0.getPostHeaders(blog0.getId());
|
||||
assertEquals(1, headers0.size());
|
||||
BlogPostHeader header = headers0.iterator().next();
|
||||
|
||||
// add a comment on own post
|
||||
String comment = "This is a comment on my own blog post!";
|
||||
blogManager0
|
||||
.addLocalComment(author0, blog0.getId(), comment, header);
|
||||
|
||||
// sync the post and comment over
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 2);
|
||||
|
||||
// make sure post arrived
|
||||
Collection<BlogPostHeader> headers1 =
|
||||
blogManager1.getPostHeaders(blog0.getId());
|
||||
assertEquals(2, headers1.size());
|
||||
for (BlogPostHeader h : headers1) {
|
||||
if (h.getType() == POST) {
|
||||
assertEquals(body, blogManager1.getPostBody(h.getId()));
|
||||
} else {
|
||||
assertEquals(comment, ((BlogCommentHeader)h).getComment());
|
||||
}
|
||||
}
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommentOnComment() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// add a post to blog0
|
||||
final String body = TestUtils.getRandomString(42);
|
||||
BlogPost p = blogPostFactory
|
||||
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
|
||||
author0, body);
|
||||
blogManager0.addLocalPost(p);
|
||||
|
||||
// sync the post over
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// make sure post arrived
|
||||
Collection<BlogPostHeader> headers1 =
|
||||
blogManager1.getPostHeaders(blog0.getId());
|
||||
assertEquals(1, headers1.size());
|
||||
assertEquals(POST, headers1.iterator().next().getType());
|
||||
|
||||
// 1 reblogs that blog post
|
||||
blogManager1
|
||||
.addLocalComment(author1, blog1.getId(), null,
|
||||
headers1.iterator().next());
|
||||
|
||||
// sync comment over
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 2);
|
||||
|
||||
// make sure comment and wrapped post arrived
|
||||
Collection<BlogPostHeader> headers0 =
|
||||
blogManager0.getPostHeaders(blog1.getId());
|
||||
assertEquals(1, headers0.size());
|
||||
|
||||
// get header of comment
|
||||
BlogPostHeader cHeader = headers0.iterator().next();
|
||||
assertEquals(COMMENT, cHeader.getType());
|
||||
|
||||
// comment on the comment
|
||||
String comment = "This is a comment on a reblogged post.";
|
||||
blogManager0
|
||||
.addLocalComment(author0, blog0.getId(), comment, cHeader);
|
||||
|
||||
// sync comment over
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 3);
|
||||
|
||||
// check that comment arrived
|
||||
headers1 =
|
||||
blogManager1.getPostHeaders(blog0.getId());
|
||||
assertEquals(2, headers1.size());
|
||||
|
||||
// get header of comment
|
||||
cHeader = null;
|
||||
for (BlogPostHeader h : headers1) {
|
||||
if (h.getType() == COMMENT) {
|
||||
cHeader = h;
|
||||
}
|
||||
}
|
||||
assertTrue(cHeader != null);
|
||||
|
||||
// another comment on the comment
|
||||
String comment2 = "This is a comment on a comment.";
|
||||
blogManager1.addLocalComment(author1, blog1.getId(), comment2, cHeader);
|
||||
|
||||
// sync comment over
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 4);
|
||||
|
||||
// make sure new comment arrived
|
||||
headers0 =
|
||||
blogManager0.getPostHeaders(blog1.getId());
|
||||
assertEquals(2, headers0.size());
|
||||
boolean satisfied = false;
|
||||
for (BlogPostHeader h : headers0) {
|
||||
assertEquals(COMMENT, h.getType());
|
||||
BlogCommentHeader c = (BlogCommentHeader) h;
|
||||
if (c.getComment() != null && c.getComment().equals(comment2)) {
|
||||
assertEquals(author0, c.getParent().getAuthor());
|
||||
assertEquals(WRAPPED_COMMENT, c.getParent().getType());
|
||||
assertEquals(comment,
|
||||
((BlogCommentHeader) c.getParent()).getComment());
|
||||
assertEquals(WRAPPED_COMMENT,
|
||||
((BlogCommentHeader) c.getParent()).getParent()
|
||||
.getType());
|
||||
assertEquals(WRAPPED_POST,
|
||||
((BlogCommentHeader) ((BlogCommentHeader) c
|
||||
.getParent()).getParent()).getParent()
|
||||
.getType());
|
||||
satisfied = true;
|
||||
}
|
||||
}
|
||||
assertTrue(satisfied);
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommentOnOwnComment() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// add a post to blog0
|
||||
final String body = TestUtils.getRandomString(42);
|
||||
BlogPost p = blogPostFactory
|
||||
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
|
||||
author0, body);
|
||||
blogManager0.addLocalPost(p);
|
||||
|
||||
// sync the post over
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// make sure post arrived
|
||||
Collection<BlogPostHeader> headers1 =
|
||||
blogManager1.getPostHeaders(blog0.getId());
|
||||
assertEquals(1, headers1.size());
|
||||
assertEquals(POST, headers1.iterator().next().getType());
|
||||
|
||||
// 1 reblogs that blog post with a comment
|
||||
String comment = "This is a comment on a post.";
|
||||
blogManager1
|
||||
.addLocalComment(author1, blog1.getId(), comment,
|
||||
headers1.iterator().next());
|
||||
|
||||
// get comment from own blog
|
||||
headers1 = blogManager1.getPostHeaders(blog1.getId());
|
||||
assertEquals(1, headers1.size());
|
||||
assertEquals(COMMENT, headers1.iterator().next().getType());
|
||||
BlogCommentHeader ch = (BlogCommentHeader) headers1.iterator().next();
|
||||
assertEquals(comment, ch.getComment());
|
||||
|
||||
comment = "This is a comment on a post with a comment.";
|
||||
blogManager1.addLocalComment(author1, blog1.getId(), comment, ch);
|
||||
|
||||
// sync both comments over (2 comments + 1 wrapped post)
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 3);
|
||||
|
||||
// make sure both comments arrived
|
||||
Collection<BlogPostHeader> headers0 =
|
||||
blogManager0.getPostHeaders(blog1.getId());
|
||||
assertEquals(2, headers0.size());
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
|
||||
private class Listener implements EventListener {
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
if (!event.isLocal()) {
|
||||
if (event.getState() == DELIVERED) {
|
||||
deliveryWaiter.resume();
|
||||
} else if (event.getState() == INVALID ||
|
||||
} else if (event.getState() == VALID ||
|
||||
event.getState() == INVALID ||
|
||||
event.getState() == PENDING) {
|
||||
validationWaiter.resume();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ 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;
|
||||
@@ -423,7 +424,7 @@ public class BlogSharingIntegrationTest extends BriarTestCase {
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// blog was added successfully and is shared both ways
|
||||
// blog was added successfully and is shard both ways
|
||||
assertEquals(3, blogManager1.getBlogs().size());
|
||||
Collection<Contact> sharedWith =
|
||||
blogSharingManager0.getSharedWith(blog2.getId());
|
||||
@@ -513,13 +514,20 @@ public class BlogSharingIntegrationTest extends BriarTestCase {
|
||||
volatile boolean requestReceived = false;
|
||||
volatile boolean responseReceived = false;
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
State s = event.getState();
|
||||
if ((s == DELIVERED || s == INVALID) && !event.isLocal()) {
|
||||
LOG.info("TEST: Sharer received message");
|
||||
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) {
|
||||
@@ -564,13 +572,20 @@ public class BlogSharingIntegrationTest extends BriarTestCase {
|
||||
this(accept, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
State s = event.getState();
|
||||
if ((s == DELIVERED || s == INVALID) && !event.isLocal()) {
|
||||
LOG.info("TEST: Invitee received message");
|
||||
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) {
|
||||
|
||||
@@ -57,6 +57,7 @@ import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGT
|
||||
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.PENDING;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.VALID;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ForumManagerTest {
|
||||
@@ -297,7 +298,7 @@ public class ForumManagerTest {
|
||||
assertEquals(1, forumManager0.getPostHeaders(g).size());
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
// send the child post to 1
|
||||
// send posts to 1
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
assertEquals(1, forumManager0.getPostHeaders(g).size());
|
||||
@@ -326,14 +327,14 @@ public class ForumManagerTest {
|
||||
}
|
||||
|
||||
private class Listener implements EventListener {
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
if (!event.isLocal()) {
|
||||
if (event.getState() == DELIVERED) {
|
||||
deliveryWaiter.resume();
|
||||
} else if (event.getState() == INVALID ||
|
||||
} else if (event.getState() == VALID ||
|
||||
event.getState() == INVALID ||
|
||||
event.getState() == PENDING) {
|
||||
validationWaiter.resume();
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sharing.InvitationItem;
|
||||
import org.briarproject.api.sharing.InvitationMessage;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.SyncSession;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
@@ -927,13 +928,20 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
volatile boolean requestReceived = false;
|
||||
volatile boolean responseReceived = false;
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
State s = event.getState();
|
||||
if ((s == DELIVERED || s == INVALID) && !event.isLocal()) {
|
||||
LOG.info("TEST: Sharer received message");
|
||||
ClientId c = event.getClientId();
|
||||
if ((s == DELIVERED || s == INVALID) &&
|
||||
c.equals(forumSharingManager0.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(forumManager0.getClientId())) {
|
||||
LOG.info("TEST: Sharer received forum post");
|
||||
msgWaiter.resume();
|
||||
}
|
||||
} else if (e instanceof ForumInvitationResponseReceivedEvent) {
|
||||
@@ -978,13 +986,20 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
this(accept, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
State s = event.getState();
|
||||
if ((s == DELIVERED || s == INVALID) && !event.isLocal()) {
|
||||
LOG.info("TEST: Invitee received message");
|
||||
ClientId c = event.getClientId();
|
||||
if ((s == DELIVERED || s == INVALID) &&
|
||||
c.equals(forumSharingManager0.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(forumManager0.getClientId())) {
|
||||
LOG.info("TEST: Invitee received forum post");
|
||||
msgWaiter.resume();
|
||||
}
|
||||
} else if (e instanceof ForumInvitationReceivedEvent) {
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
package org.briarproject;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import net.jodah.concurrentunit.Waiter;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
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.data.BdfDictionary;
|
||||
import org.briarproject.api.data.BdfEntry;
|
||||
@@ -29,18 +23,18 @@ 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.introduction.IntroducerProtocolState;
|
||||
import org.briarproject.api.introduction.IntroductionManager;
|
||||
import org.briarproject.api.introduction.IntroductionMessage;
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.properties.TransportProperties;
|
||||
import org.briarproject.api.properties.TransportPropertyManager;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.api.sync.SyncSession;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.api.sync.ValidationManager;
|
||||
import org.briarproject.api.sync.ValidationManager.State;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
@@ -62,7 +56,6 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -73,17 +66,13 @@ import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
|
||||
import static org.briarproject.TestPluginsModule.TRANSPORT_ID;
|
||||
import static org.briarproject.api.clients.MessageQueueManager.QUEUE_STATE_KEY;
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.STATE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TRANSPORT;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -92,23 +81,17 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
|
||||
private LifecycleManager lifecycleManager0, lifecycleManager1,
|
||||
lifecycleManager2;
|
||||
private SyncSessionFactory sync0, sync1, sync2;
|
||||
private ContactManager contactManager0, contactManager1, contactManager2;
|
||||
private ContactId contactId0, contactId1, contactId2;
|
||||
private IdentityManager identityManager0, identityManager1,
|
||||
identityManager2;
|
||||
private LocalAuthor author0, author1, author2;
|
||||
LifecycleManager lifecycleManager0, lifecycleManager1, lifecycleManager2;
|
||||
SyncSessionFactory sync0, sync1, sync2;
|
||||
ContactManager contactManager0, contactManager1, contactManager2;
|
||||
ContactId contactId0, contactId1, contactId2;
|
||||
IdentityManager identityManager0, identityManager1, identityManager2;
|
||||
LocalAuthor author0, author1, author2;
|
||||
|
||||
@Inject
|
||||
Clock clock;
|
||||
@Inject
|
||||
CryptoComponent crypto;
|
||||
@Inject
|
||||
AuthorFactory authorFactory;
|
||||
@Inject
|
||||
IntroductionGroupFactory introductionGroupFactory;
|
||||
|
||||
// objects accessed from background threads need to be volatile
|
||||
private volatile IntroductionManager introductionManager0;
|
||||
@@ -172,13 +155,32 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
public void testIntroductionSession() throws Exception {
|
||||
startLifecycles();
|
||||
try {
|
||||
// Add Identities And Contacts
|
||||
// Add Identities
|
||||
addDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
|
||||
// Add Transport Properties
|
||||
addTransportProperties();
|
||||
|
||||
// Add introducees as contacts
|
||||
contactId1 = contactManager0.addContact(author1,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
contactId2 = contactManager0.addContact(author2,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
// Add introducer back
|
||||
contactId0 = contactManager1.addContact(author0,
|
||||
author1.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
ContactId contactId02 = contactManager2.addContact(author0,
|
||||
author2.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
assertTrue(contactId0.equals(contactId02));
|
||||
|
||||
// listen to events
|
||||
IntroducerListener listener0 = new IntroducerListener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
@@ -258,13 +260,29 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
public void testIntroductionSessionFirstDecline() throws Exception {
|
||||
startLifecycles();
|
||||
try {
|
||||
// Add Identities And Contacts
|
||||
// Add Identities
|
||||
addDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
|
||||
// Add Transport Properties
|
||||
addTransportProperties();
|
||||
|
||||
// Add introducees as contacts
|
||||
contactId1 = contactManager0.addContact(author1, author0.getId(),
|
||||
master, clock.currentTimeMillis(), true, true, true
|
||||
);
|
||||
contactId2 = contactManager0.addContact(author2, author0.getId(),
|
||||
master, clock.currentTimeMillis(), true, true, true
|
||||
);
|
||||
// Add introducer back
|
||||
contactId0 = contactManager1.addContact(author0, author1.getId(),
|
||||
master, clock.currentTimeMillis(), true, true, true
|
||||
);
|
||||
ContactId contactId02 = contactManager2.addContact(author0,
|
||||
author2.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
assertTrue(contactId0.equals(contactId02));
|
||||
|
||||
// listen to events
|
||||
IntroducerListener listener0 = new IntroducerListener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
@@ -300,7 +318,7 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
assertTrue(listener0.response2Received);
|
||||
|
||||
// sync first forwarded response
|
||||
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
||||
deliverMessage(sync0, contactId0, sync2, contactId2);
|
||||
|
||||
// note how the introducer does not forward the second response,
|
||||
// because after the first decline the protocol finished
|
||||
@@ -335,13 +353,29 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
public void testIntroductionSessionSecondDecline() throws Exception {
|
||||
startLifecycles();
|
||||
try {
|
||||
// Add Identities And Contacts
|
||||
// Add Identities
|
||||
addDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
|
||||
// Add Transport Properties
|
||||
addTransportProperties();
|
||||
|
||||
// Add introducees as contacts
|
||||
contactId1 = contactManager0.addContact(author1, author0.getId(),
|
||||
master, clock.currentTimeMillis(), true, true, true
|
||||
);
|
||||
contactId2 = contactManager0.addContact(author2, author0.getId(),
|
||||
master, clock.currentTimeMillis(), true, true, true
|
||||
);
|
||||
// Add introducer back
|
||||
contactId0 = contactManager1.addContact(author0, author1.getId(),
|
||||
master, clock.currentTimeMillis(), false, true, true
|
||||
);
|
||||
ContactId contactId02 = contactManager2.addContact(author0,
|
||||
author2.getId(), master, clock.currentTimeMillis(), false,
|
||||
true, true
|
||||
);
|
||||
assertTrue(contactId0.equals(contactId02));
|
||||
|
||||
// listen to events
|
||||
IntroducerListener listener0 = new IntroducerListener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
@@ -407,13 +441,29 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
public void testIntroductionSessionDelayedFirstDecline() throws Exception {
|
||||
startLifecycles();
|
||||
try {
|
||||
// Add Identities And Contacts
|
||||
// Add Identities
|
||||
addDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
|
||||
// Add Transport Properties
|
||||
addTransportProperties();
|
||||
|
||||
// Add introducees as contacts
|
||||
contactId1 = contactManager0.addContact(author1, author0.getId(),
|
||||
master, clock.currentTimeMillis(), true, true, true
|
||||
);
|
||||
contactId2 = contactManager0.addContact(author2, author0.getId(),
|
||||
master, clock.currentTimeMillis(), true, true, true
|
||||
);
|
||||
// Add introducer back
|
||||
contactId0 = contactManager1.addContact(author0, author1.getId(),
|
||||
master, clock.currentTimeMillis(), false, true, true
|
||||
);
|
||||
ContactId contactId02 = contactManager2.addContact(author0,
|
||||
author2.getId(), master, clock.currentTimeMillis(), false,
|
||||
true, true
|
||||
);
|
||||
assertTrue(contactId0.equals(contactId02));
|
||||
|
||||
// listen to events
|
||||
IntroducerListener listener0 = new IntroducerListener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
@@ -470,13 +520,21 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
public void testIntroductionToSameContact() throws Exception {
|
||||
startLifecycles();
|
||||
try {
|
||||
// Add Identities And Contacts
|
||||
// Add Identities
|
||||
addDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
|
||||
// Add Transport Properties
|
||||
addTransportProperties();
|
||||
|
||||
// Add introducee as contact
|
||||
contactId1 = contactManager0.addContact(author1, author0.getId(),
|
||||
master, clock.currentTimeMillis(), true, true, true
|
||||
);
|
||||
// Add introducer back
|
||||
contactId0 = contactManager1.addContact(author0, author1.getId(),
|
||||
master, clock.currentTimeMillis(), true, true, true
|
||||
);
|
||||
|
||||
// listen to events
|
||||
IntroducerListener listener0 = new IntroducerListener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
@@ -612,13 +670,32 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
public void testSessionIdReuse() throws Exception {
|
||||
startLifecycles();
|
||||
try {
|
||||
// Add Identities And Contacts
|
||||
// Add Identities
|
||||
addDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
|
||||
// Add Transport Properties
|
||||
addTransportProperties();
|
||||
|
||||
// Add introducees as contacts
|
||||
contactId1 = contactManager0.addContact(author1,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
contactId2 = contactManager0.addContact(author2,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
// Add introducer back
|
||||
contactId0 = contactManager1.addContact(author0,
|
||||
author1.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
ContactId contactId02 = contactManager2.addContact(author0,
|
||||
author2.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
assertTrue(contactId0.equals(contactId02));
|
||||
|
||||
// listen to events
|
||||
IntroducerListener listener0 = new IntroducerListener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
@@ -690,13 +767,32 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
public void testIntroducerRemovedCleanup() throws Exception {
|
||||
startLifecycles();
|
||||
try {
|
||||
// Add Identities And Contacts
|
||||
// Add Identities
|
||||
addDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
|
||||
// Add Transport Properties
|
||||
addTransportProperties();
|
||||
|
||||
// Add introducees as contacts
|
||||
contactId1 = contactManager0.addContact(author1,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
contactId2 = contactManager0.addContact(author2,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
// Add introducer back
|
||||
contactId0 = contactManager1.addContact(author0,
|
||||
author1.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
ContactId contactId02 = contactManager2.addContact(author0,
|
||||
author2.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
assertTrue(contactId0.equals(contactId02));
|
||||
|
||||
// listen to events
|
||||
IntroducerListener listener0 = new IntroducerListener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
@@ -757,13 +853,32 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
public void testIntroduceesRemovedCleanup() throws Exception {
|
||||
startLifecycles();
|
||||
try {
|
||||
// Add Identities And Contacts
|
||||
// Add Identities
|
||||
addDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
|
||||
// Add Transport Properties
|
||||
addTransportProperties();
|
||||
|
||||
// Add introducees as contacts
|
||||
contactId1 = contactManager0.addContact(author1,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
contactId2 = contactManager0.addContact(author2,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
// Add introducer back
|
||||
contactId0 = contactManager1.addContact(author0,
|
||||
author1.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
ContactId contactId02 = contactManager2.addContact(author0,
|
||||
author2.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
assertTrue(contactId0.equals(contactId02));
|
||||
|
||||
// listen to events
|
||||
IntroducerListener listener0 = new IntroducerListener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
@@ -834,133 +949,7 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModifiedResponse() throws Exception {
|
||||
startLifecycles();
|
||||
try {
|
||||
addDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
addTransportProperties();
|
||||
|
||||
// listen to events
|
||||
IntroducerListener listener0 = new IntroducerListener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
IntroduceeListener listener1 = new IntroduceeListener(1, true);
|
||||
t1.getEventBus().addListener(listener1);
|
||||
IntroduceeListener listener2 = new IntroduceeListener(2, true);
|
||||
t2.getEventBus().addListener(listener2);
|
||||
|
||||
// make introduction
|
||||
long time = clock.currentTimeMillis();
|
||||
Contact introducee1 = contactManager0.getContact(contactId1);
|
||||
Contact introducee2 = contactManager0.getContact(contactId2);
|
||||
introductionManager0
|
||||
.makeIntroduction(introducee1, introducee2, "Hi!", time);
|
||||
|
||||
// sync request messages
|
||||
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
||||
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
||||
eventWaiter.await(TIMEOUT, 2);
|
||||
|
||||
// sync first response
|
||||
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// get response to be forwarded
|
||||
MessageId responseId = null;
|
||||
BdfDictionary response = null;
|
||||
Group g2 = introductionGroupFactory
|
||||
.createIntroductionGroup(introducee2);
|
||||
ClientHelper clientHelper0 = t0.getClientHelper();
|
||||
Map<MessageId, BdfDictionary> map =
|
||||
clientHelper0.getMessageMetadataAsDictionary(g2.getId());
|
||||
for (Map.Entry<MessageId, BdfDictionary> entry : map.entrySet()) {
|
||||
if (entry.getValue().getLong(TYPE) == TYPE_RESPONSE) {
|
||||
responseId = entry.getKey();
|
||||
response = entry.getValue();
|
||||
}
|
||||
}
|
||||
assertTrue(responseId != null && response != null);
|
||||
|
||||
// adapt outgoing message queue to removed message
|
||||
decreaseOutgoingMessageCounter(clientHelper0, g2.getId(), 1);
|
||||
|
||||
// modify response by changing transport properties
|
||||
BdfDictionary tp = response.getDictionary(TRANSPORT);
|
||||
tp.put("fakeId", BdfDictionary.of(new BdfEntry("fake", "fake")));
|
||||
response.put(TRANSPORT, tp);
|
||||
|
||||
// replace original response with modified one
|
||||
MessageSender sender0 = t0.getMessageSender();
|
||||
DatabaseComponent db0 = t0.getDatabaseComponent();
|
||||
Transaction txn = db0.startTransaction(false);
|
||||
try {
|
||||
db0.deleteMessage(txn, responseId);
|
||||
sender0.sendMessage(txn, response);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db0.endTransaction(txn);
|
||||
}
|
||||
|
||||
// sync second response
|
||||
deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// sync forwarded responses to introducees
|
||||
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
||||
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
||||
|
||||
// sync first ACK and its forward
|
||||
deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
|
||||
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
||||
|
||||
// sync second ACK and forward it
|
||||
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
|
||||
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
||||
|
||||
// introducee2 should have detected the fake now
|
||||
// and deleted introducee1 again
|
||||
Collection<Contact> contacts2;
|
||||
DatabaseComponent db2 = t2.getDatabaseComponent();
|
||||
txn = db2.startTransaction(true);
|
||||
try {
|
||||
contacts2 = db2.getContacts(txn);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db2.endTransaction(txn);
|
||||
}
|
||||
assertEquals(1, contacts2.size());
|
||||
|
||||
// sync abort message to introducer
|
||||
deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
|
||||
|
||||
// ensure introducer got the abort
|
||||
SessionId sessionId = new SessionId(response.getRaw(SESSION_ID));
|
||||
BdfDictionary state =
|
||||
clientHelper0.getMessageMetadataAsDictionary(sessionId);
|
||||
assertEquals(IntroducerProtocolState.ERROR.getValue(),
|
||||
state.getLong(STATE).intValue());
|
||||
|
||||
// sync abort messages to introducees
|
||||
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
||||
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
||||
|
||||
// although aborted, introducee1 keeps the contact,
|
||||
// so introducer can not make contacts disappear by sending abort
|
||||
Collection<Contact> contacts1;
|
||||
DatabaseComponent db1 = t1.getDatabaseComponent();
|
||||
txn = db1.startTransaction(true);
|
||||
try {
|
||||
contacts1 = db1.getContacts(txn);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db1.endTransaction(txn);
|
||||
}
|
||||
assertEquals(2, contacts1.size());
|
||||
} finally {
|
||||
stopLifecycles();
|
||||
}
|
||||
}
|
||||
// TODO add a test for faking responses when #256 is implemented
|
||||
|
||||
@After
|
||||
public void tearDown() throws InterruptedException {
|
||||
@@ -1003,48 +992,20 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
private void addDefaultIdentities() throws DbException {
|
||||
KeyPair keyPair0 = crypto.generateSignatureKeyPair();
|
||||
byte[] publicKey0 = keyPair0.getPublic().getEncoded();
|
||||
byte[] privateKey0 = keyPair0.getPrivate().getEncoded();
|
||||
author0 = authorFactory
|
||||
.createLocalAuthor(INTRODUCER, publicKey0, privateKey0);
|
||||
author0 = authorFactory.createLocalAuthor(INTRODUCER,
|
||||
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
|
||||
TestUtils.getRandomBytes(123));
|
||||
identityManager0.addLocalAuthor(author0);
|
||||
KeyPair keyPair1 = crypto.generateSignatureKeyPair();
|
||||
byte[] publicKey1 = keyPair1.getPublic().getEncoded();
|
||||
byte[] privateKey1 = keyPair1.getPrivate().getEncoded();
|
||||
author1 = authorFactory
|
||||
.createLocalAuthor(INTRODUCEE1, publicKey1, privateKey1);
|
||||
author1 = authorFactory.createLocalAuthor(INTRODUCEE1,
|
||||
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
|
||||
TestUtils.getRandomBytes(123));
|
||||
identityManager1.addLocalAuthor(author1);
|
||||
KeyPair keyPair2 = crypto.generateSignatureKeyPair();
|
||||
byte[] publicKey2 = keyPair2.getPublic().getEncoded();
|
||||
byte[] privateKey2 = keyPair2.getPrivate().getEncoded();
|
||||
author2 = authorFactory
|
||||
.createLocalAuthor(INTRODUCEE2, publicKey2, privateKey2);
|
||||
author2 = authorFactory.createLocalAuthor(INTRODUCEE2,
|
||||
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
|
||||
TestUtils.getRandomBytes(123));
|
||||
identityManager2.addLocalAuthor(author2);
|
||||
}
|
||||
|
||||
private void addDefaultContacts() throws DbException {
|
||||
// Add introducees as contacts
|
||||
contactId1 = contactManager0.addContact(author1,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
contactId2 = contactManager0.addContact(author2,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
// Add introducer back
|
||||
contactId0 = contactManager1.addContact(author0,
|
||||
author1.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
ContactId contactId02 = contactManager2.addContact(author0,
|
||||
author2.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
assertTrue(contactId0.equals(contactId02));
|
||||
}
|
||||
|
||||
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
||||
SyncSessionFactory toSync, ContactId toId)
|
||||
throws IOException, TimeoutException {
|
||||
@@ -1052,7 +1013,7 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
||||
SyncSessionFactory toSync, ContactId toId, @Nullable String debug)
|
||||
SyncSessionFactory toSync, ContactId toId, String debug)
|
||||
throws IOException, TimeoutException {
|
||||
|
||||
if (debug != null) LOG.info("TEST: Sending message from " + debug);
|
||||
@@ -1089,14 +1050,14 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
|
||||
private class IntroduceeListener implements EventListener {
|
||||
|
||||
private volatile boolean requestReceived = false;
|
||||
private volatile boolean succeeded = false;
|
||||
private volatile boolean aborted = false;
|
||||
public volatile boolean requestReceived = false;
|
||||
public volatile boolean succeeded = false;
|
||||
public volatile boolean aborted = false;
|
||||
|
||||
private final int introducee;
|
||||
private final boolean accept;
|
||||
|
||||
private IntroduceeListener(int introducee, boolean accept) {
|
||||
IntroduceeListener(int introducee, boolean accept) {
|
||||
this.introducee = introducee;
|
||||
this.accept = accept;
|
||||
}
|
||||
@@ -1106,9 +1067,13 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
State s = event.getState();
|
||||
if ((s == DELIVERED || s == INVALID) && !event.isLocal()) {
|
||||
ClientId c = event.getClientId();
|
||||
if ((s == DELIVERED || s == INVALID) &&
|
||||
c.equals(introductionManager0.getClientId()) &&
|
||||
!event.isLocal()) {
|
||||
LOG.info("TEST: Introducee" + introducee +
|
||||
" received message");
|
||||
" received message in group " +
|
||||
event.getMessage().getGroupId().hashCode());
|
||||
msgWaiter.resume();
|
||||
}
|
||||
} else if (e instanceof IntroductionRequestReceivedEvent) {
|
||||
@@ -1142,7 +1107,6 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
}
|
||||
}
|
||||
} catch (DbException | IOException exception) {
|
||||
msgWaiter.rethrow(exception);
|
||||
eventWaiter.rethrow(exception);
|
||||
} finally {
|
||||
eventWaiter.resume();
|
||||
@@ -1162,16 +1126,19 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
|
||||
private class IntroducerListener implements EventListener {
|
||||
|
||||
private volatile boolean response1Received = false;
|
||||
private volatile boolean response2Received = false;
|
||||
private volatile boolean aborted = false;
|
||||
public volatile boolean response1Received = false;
|
||||
public volatile boolean response2Received = false;
|
||||
public volatile boolean aborted = false;
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
if (event.getState() == DELIVERED && !event.isLocal()) {
|
||||
LOG.info("TEST: Introducer received message");
|
||||
if (event.getState() == DELIVERED && event.getClientId()
|
||||
.equals(introductionManager0.getClientId()) &&
|
||||
!event.isLocal()) {
|
||||
LOG.info("TEST: Introducer received message in group " +
|
||||
event.getMessage().getGroupId().hashCode());
|
||||
msgWaiter.resume();
|
||||
}
|
||||
} else if (e instanceof IntroductionResponseReceivedEvent) {
|
||||
@@ -1193,16 +1160,6 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private void decreaseOutgoingMessageCounter(ClientHelper clientHelper,
|
||||
GroupId g, int num) throws FormatException, DbException {
|
||||
BdfDictionary gD = clientHelper.getGroupMetadataAsDictionary(g);
|
||||
LOG.warning(gD.toString());
|
||||
BdfDictionary queue = gD.getDictionary(QUEUE_STATE_KEY);
|
||||
queue.put("nextOut", queue.getLong("nextOut") - num);
|
||||
gD.put(QUEUE_STATE_KEY, queue);
|
||||
clientHelper.mergeGroupMetadata(g, gD);
|
||||
}
|
||||
|
||||
private void injectEagerSingletons(
|
||||
IntroductionIntegrationTestComponent component) {
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject;
|
||||
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
@@ -86,8 +85,6 @@ public interface IntroductionIntegrationTestComponent {
|
||||
|
||||
DatabaseComponent getDatabaseComponent();
|
||||
|
||||
ClientHelper getClientHelper();
|
||||
|
||||
MessageSender getMessageSender();
|
||||
|
||||
IntroductionGroupFactory getIntroductionGroupFactory();
|
||||
|
||||
@@ -211,17 +211,6 @@
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.blogs.ReblogActivity"
|
||||
android:label="@string/blogs_reblog_button"
|
||||
android:parentActivityName=".android.blogs.BlogActivity"
|
||||
android:windowSoftInputMode="stateHidden">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.blogs.BlogActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.blogs.RssFeedImportActivity"
|
||||
android:label="@string/blogs_rss_feeds_import"
|
||||
|
||||
@@ -11,6 +11,9 @@ dependencies {
|
||||
compile project(':briar-api')
|
||||
compile project(':briar-core')
|
||||
compile fileTree(dir: 'libs', include: '*.jar')
|
||||
// This shouldn't be necessary; per section 23.4.4 of the Gradle docs:
|
||||
// "file dependencies are included in transitive project dependencies within the same build".
|
||||
compile files('../briar-core/libs/jsocks.jar')
|
||||
|
||||
compile "com.android.support:support-v4:$supportVersion"
|
||||
compile("com.android.support:appcompat-v7:$supportVersion") {
|
||||
@@ -24,7 +27,6 @@ dependencies {
|
||||
exclude module: 'support-v4'
|
||||
exclude module: 'recyclerview-v7'
|
||||
}
|
||||
compile "com.android.support:cardview-v7:$supportVersion"
|
||||
compile('ch.acra:acra:4.8.5') {
|
||||
exclude module: 'support-v4'
|
||||
exclude module: 'support-annotations'
|
||||
@@ -59,7 +61,6 @@ dependencyVerification {
|
||||
'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1',
|
||||
'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
|
||||
'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe',
|
||||
'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b',
|
||||
]
|
||||
}
|
||||
|
||||
@@ -188,8 +189,8 @@ project.afterEvaluate {
|
||||
preBuild.dependsOn {
|
||||
[
|
||||
'verifyTorGeoIp',
|
||||
'verifyTorBinaryArm',
|
||||
'verifyTorBinaryArmPie',
|
||||
// 'verifyTorBinaryArm',
|
||||
// 'verifyTorBinaryArmPie',
|
||||
'verifyTorBinaryX86',
|
||||
'verifyTorBinaryX86Pie'
|
||||
]
|
||||
|
||||
BIN
briar-android/res/drawable-hdpi/alerts_and_states_error.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
briar-android/res/drawable-hdpi/message_notification_icon.png
Normal file
|
After Width: | Height: | Size: 366 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 16 KiB |
BIN
briar-android/res/drawable-hdpi/navigation_accept.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 16 KiB |
BIN
briar-android/res/drawable-ldpi/message_notification_icon.png
Normal file
|
After Width: | Height: | Size: 210 B |
BIN
briar-android/res/drawable-mdpi/alerts_and_states_error.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
briar-android/res/drawable-mdpi/message_notification_icon.png
Normal file
|
After Width: | Height: | Size: 175 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 16 KiB |
BIN
briar-android/res/drawable-mdpi/navigation_accept.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 15 KiB |
BIN
briar-android/res/drawable-xhdpi/alerts_and_states_error.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
briar-android/res/drawable-xhdpi/message_notification_icon.png
Normal file
|
After Width: | Height: | Size: 515 B |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 18 KiB |
BIN
briar-android/res/drawable-xhdpi/navigation_accept.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 16 KiB |
@@ -1,5 +0,0 @@
|
||||
<vector android:alpha="0.54" android:height="24dp"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M15.73,3L8.27,3L3,8.27v7.46L8.27,21h7.46L21,15.73L21,8.27L15.73,3zM12,17.3c-0.72,0 -1.3,-0.58 -1.3,-1.3 0,-0.72 0.58,-1.3 1.3,-1.3 0.72,0 1.3,0.58 1.3,1.3 0,0.72 -0.58,1.3 -1.3,1.3zM13,13h-2L11,7h2v6z"/>
|
||||
</vector>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<corners
|
||||
android:radius="@dimen/unread_bubble_size"/>
|
||||
|
||||
<solid
|
||||
android:color="@color/briar_text_primary_inverse"/>
|
||||
|
||||
<stroke
|
||||
android:color="@color/briar_text_primary"
|
||||
android:width="1dp"/>
|
||||
|
||||
</shape>
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
||||
android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM6,9h12v2L6,11L6,9zM14,14L6,14v-2h8v2zM18,8L6,8L6,6h12v2z"/>
|
||||
</vector>
|
||||
@@ -4,6 +4,6 @@
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z"/>
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
|
||||
</vector>
|
||||
@@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:alpha="0.54"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,5.9c1.16,0 2.1,0.94 2.1,2.1s-0.94,2.1 -2.1,2.1S9.9,9.16 9.9,8s0.94,-2.1 2.1,-2.1m0,9c2.97,0 6.1,1.46 6.1,2.1v1.1L5.9,18.1L5.9,17c0,-0.64 3.13,-2.1 6.1,-2.1M12,4C9.79,4 8,5.79 8,8s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM12,13c-2.67,0 -8,1.34 -8,4v3h16v-3c0,-2.66 -5.33,-4 -8,-4z"/>
|
||||
</vector>
|
||||
@@ -1,7 +1,6 @@
|
||||
<?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="match_parent"
|
||||
android:orientation="horizontal">
|
||||
@@ -23,7 +22,6 @@
|
||||
android:src="@drawable/qr_code_intro"/>
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1">
|
||||
@@ -39,6 +37,22 @@
|
||||
android:paddingStart="@dimen/margin_activity_horizontal"
|
||||
android:paddingTop="@dimen/margin_activity_vertical">
|
||||
|
||||
<TextView
|
||||
style="@style/BriarTextBody"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/your_nickname"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:background="@drawable/border_spinner"
|
||||
android:spinnerMode="dropdown"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -51,8 +65,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
android:padding="@dimen/margin_medium"
|
||||
android:src="@drawable/qr_code_explanation"
|
||||
tools:ignore="ContentDescription"/>
|
||||
android:src="@drawable/qr_code_explanation"/>
|
||||
|
||||
<TextView
|
||||
style="@style/BriarTextBody"
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
android:id="@+id/pager"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".android.blogs.BlogActivity"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
android:id="@+id/fragmentContainer"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="invisible"/>
|
||||
|
||||
</FrameLayout>
|
||||
6
briar-android/res/layout/activity_introduction.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
android:id="@+id/introductionContainer"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
@@ -1,63 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:showIn="@layout/list_item_blog_post">
|
||||
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
android:id="@+id/avatar"
|
||||
style="@style/BriarAvatar"
|
||||
android:layout_width="@dimen/blogs_avatar_normal_size"
|
||||
android:layout_height="@dimen/blogs_avatar_normal_size"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginRight="@dimen/margin_medium"
|
||||
tools:src="@drawable/ic_launcher"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatarIcon"
|
||||
android:layout_width="@dimen/blogs_avatar_icon_size"
|
||||
android:layout_height="@dimen/blogs_avatar_icon_size"
|
||||
android:layout_alignBottom="@+id/avatar"
|
||||
android:layout_alignRight="@+id/avatar"
|
||||
android:background="@drawable/bubble_white"
|
||||
android:padding="2dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_repeat"
|
||||
android:visibility="invisible"
|
||||
tools:ignore="ContentDescription"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/authorName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@+id/avatar"
|
||||
android:layout_toEndOf="@+id/avatar"
|
||||
android:layout_toRightOf="@+id/avatar"
|
||||
android:textColor="@color/briar_text_primary"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
tools:text="Author Name"/>
|
||||
|
||||
<org.briarproject.android.util.TrustIndicatorView
|
||||
android:id="@+id/trustIndicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@+id/authorName"
|
||||
android:layout_alignTop="@+id/authorName"
|
||||
android:layout_marginLeft="@dimen/margin_small"
|
||||
android:layout_toRightOf="@id/authorName"
|
||||
android:scaleType="center"
|
||||
tools:src="@drawable/trust_indicator_verified"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dateView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/authorName"
|
||||
android:layout_toEndOf="@+id/avatar"
|
||||
android:layout_toRightOf="@+id/avatar"
|
||||
android:gravity="bottom"
|
||||
android:textColor="@color/briar_text_secondary"
|
||||
android:textSize="@dimen/text_size_tiny"
|
||||
tools:text="yesterday"/>
|
||||
|
||||
</merge>
|
||||
28
briar-android/res/layout/dropdown_author.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?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="match_parent"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
android:id="@+id/avatarView"
|
||||
style="@style/BriarAvatar"
|
||||
android:layout_width="@dimen/dropdown_picture_size"
|
||||
android:layout_height="@dimen/dropdown_picture_size"
|
||||
android:layout_margin="@dimen/margin_small"
|
||||
tools:src="@drawable/ic_launcher"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nameView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/margin_small"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
tools:text="This is a name of an author. It can be quite long."/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,32 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:descendantFocusability="beforeDescendants"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true">
|
||||
<!-- Above focusability attributes prevent automatic scroll-down,
|
||||
because body text is selectable -->
|
||||
android:padding="@dimen/margin_activity_horizontal">
|
||||
|
||||
<include
|
||||
android:id="@+id/postLayout"
|
||||
style="@style/BriarCard"
|
||||
layout="@layout/list_item_blog_post"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
android:id="@+id/avatar"
|
||||
style="@style/BriarAvatar"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginRight="@dimen/margin_medium"
|
||||
tools:src="@drawable/ic_launcher"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
<TextView
|
||||
android:id="@+id/authorName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"/>
|
||||
android:layout_alignTop="@+id/avatar"
|
||||
android:layout_toEndOf="@+id/avatar"
|
||||
android:layout_toRightOf="@+id/avatar"
|
||||
android:textColor="@color/briar_text_primary"
|
||||
android:textSize="@dimen/text_size_tiny"
|
||||
tools:text="Author Name"/>
|
||||
|
||||
</FrameLayout>
|
||||
<TextView
|
||||
android:id="@+id/date"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@id/avatar"
|
||||
android:layout_below="@+id/authorName"
|
||||
android:layout_toEndOf="@+id/avatar"
|
||||
android:layout_toRightOf="@+id/avatar"
|
||||
android:gravity="bottom"
|
||||
android:textColor="@color/briar_text_primary"
|
||||
android:textSize="@dimen/text_size_tiny"
|
||||
tools:text="yesterday"/>
|
||||
|
||||
<org.briarproject.android.util.TrustIndicatorView
|
||||
android:id="@+id/trustIndicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/margin_small"
|
||||
android:layout_toRightOf="@+id/authorName"
|
||||
tools:src="@drawable/trust_indicator_verified"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_below="@+id/avatar"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:textColor="@color/briar_text_primary"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
android:textStyle="bold"
|
||||
tools:text="This Is A Blog Post Title"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/title"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:textColor="@color/briar_text_secondary"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
tools:text="Body of Blog Post. This could be insanely large or just a short text as well."/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
46
briar-android/res/layout/fragment_blogs.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.design.widget.TabLayout
|
||||
android:id="@+id/tabLayout"
|
||||
style="@style/BriarTabLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<android.support.design.widget.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/blogs_feed"/>
|
||||
|
||||
<android.support.design.widget.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/blogs_my_blogs"/>
|
||||
|
||||
<android.support.design.widget.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/blogs_blog_list"/>
|
||||
|
||||
<android.support.design.widget.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/blogs_available_blogs"/>
|
||||
|
||||
<android.support.design.widget.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/blogs_drafts"/>
|
||||
|
||||
</android.support.design.widget.TabLayout>
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
</LinearLayout>
|
||||
7
briar-android/res/layout/fragment_blogs_my.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.briarproject.android.util.BriarRecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:listitem="@layout/list_item_blog"/>
|
||||
@@ -1,8 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView
|
||||
android:id="@+id/scrollView"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
@@ -17,6 +15,22 @@
|
||||
android:paddingStart="@dimen/margin_activity_horizontal"
|
||||
android:paddingTop="@dimen/margin_activity_vertical">
|
||||
|
||||
<TextView
|
||||
style="@style/BriarTextBody"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/your_nickname"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:background="@drawable/border_spinner"
|
||||
android:spinnerMode="dropdown"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="match_parent"
|
||||
@@ -24,8 +38,7 @@
|
||||
android:layout_marginTop="@dimen/margin_xlarge"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/qr_code_intro"
|
||||
tools:ignore="ContentDescription"/>
|
||||
android:src="@drawable/qr_code_intro"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -1,73 +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="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/margin_large">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/link_warning_title"
|
||||
android:textColor="@color/briar_primary"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_large"
|
||||
android:text="@string/link_warning_intro"
|
||||
android:textColor="@color/briar_primary"
|
||||
android:textSize="@dimen/text_size_medium"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/urlView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_large"
|
||||
android:textIsSelectable="true"
|
||||
android:typeface="monospace"
|
||||
tools:text="http://very.bad.site.com"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_large"
|
||||
android:text="@string/link_warning_text"
|
||||
android:textColor="@color/briar_primary"
|
||||
android:textSize="@dimen/text_size_medium"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/cancelButton"
|
||||
style="@style/BriarButtonFlat.Positive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.5"
|
||||
android:text="@string/cancel"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/openButton"
|
||||
style="@style/BriarButtonFlat.Negative"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.5"
|
||||
android:text="@string/link_warning_open_link"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,46 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView
|
||||
android:id="@+id/scrollView"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/window_background">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/margin_small">
|
||||
|
||||
<include
|
||||
android:id="@+id/postLayout"
|
||||
layout="@layout/list_item_blog_post"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/inputText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/postLayout"
|
||||
android:layout_margin="@dimen/listitem_vertical_margin"
|
||||
android:gravity="bottom"
|
||||
android:hint="@string/blogs_reblog_comment_hint"
|
||||
android:inputType="textShortMessage|textMultiLine|textCapSentences|textAutoCorrect"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/publishButton"
|
||||
style="@style/BriarButton"
|
||||
android:layout_below="@+id/inputText"
|
||||
android:enabled="false"
|
||||
android:text="@string/blogs_reblog_button"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</ScrollView>
|
||||
@@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:showIn="@layout/list_item_blog_post">
|
||||
|
||||
<View
|
||||
android:id="@+id/inputDivider"
|
||||
style="@style/Divider.Horizontal"/>
|
||||
|
||||
<org.briarproject.android.util.AuthorView
|
||||
android:id="@+id/authorView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:padding="@dimen/listitem_vertical_margin"
|
||||
app:persona="commenter"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bodyView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/authorView"
|
||||
android:paddingBottom="@dimen/listitem_vertical_margin"
|
||||
android:paddingLeft="@dimen/listitem_vertical_margin"
|
||||
android:paddingRight="@dimen/listitem_vertical_margin"
|
||||
android:textColor="@color/briar_text_secondary"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
tools:text="This is a comment that appears below a blog post. Usually, it is expected to be rather short. Not much longer than this one."/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -1,79 +1,118 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.CardView
|
||||
android:id="@+id/postLayout"
|
||||
style="@style/BriarCard"
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?android:attr/selectableItemBackground">
|
||||
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
|
||||
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
||||
android:layout_marginTop="@dimen/listitem_vertical_margin"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
|
||||
<LinearLayout
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
android:id="@+id/avatar"
|
||||
style="@style/BriarAvatar"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginBottom="@dimen/margin_medium"
|
||||
android:layout_marginRight="@dimen/margin_medium"
|
||||
tools:src="@drawable/ic_launcher"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/authorName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@+id/avatar"
|
||||
android:layout_toEndOf="@+id/avatar"
|
||||
android:layout_toRightOf="@+id/avatar"
|
||||
android:textColor="@color/briar_text_primary"
|
||||
android:textSize="@dimen/text_size_tiny"
|
||||
tools:text="Author Name"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dateView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@id/avatar"
|
||||
android:layout_below="@+id/authorName"
|
||||
android:layout_toEndOf="@+id/avatar"
|
||||
android:layout_toRightOf="@+id/avatar"
|
||||
android:gravity="bottom"
|
||||
android:textColor="@color/briar_text_primary"
|
||||
android:textSize="@dimen/text_size_tiny"
|
||||
tools:text="yesterday"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/newView"
|
||||
style="@style/BriarTag"
|
||||
android:layout_alignBottom="@+id/dateView"
|
||||
android:layout_marginLeft="@dimen/margin_small"
|
||||
android:layout_toRightOf="@+id/dateView"
|
||||
android:text="@string/tag_new"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<org.briarproject.android.util.TrustIndicatorView
|
||||
android:id="@+id/trustIndicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@+id/authorName"
|
||||
android:layout_alignTop="@+id/authorName"
|
||||
android:layout_marginLeft="@dimen/margin_small"
|
||||
android:layout_toRightOf="@+id/authorName"
|
||||
android:scaleType="center"
|
||||
tools:src="@drawable/trust_indicator_verified"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/chatView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toLeftOf="@+id/commentView"
|
||||
android:padding="@dimen/margin_small"
|
||||
android:src="@drawable/ic_chat"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/commentView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||
android:padding="@dimen/margin_small"
|
||||
android:src="@drawable/ic_repeat"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_below="@+id/avatar"
|
||||
android:layout_marginBottom="@dimen/margin_medium"
|
||||
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
|
||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="3"
|
||||
android:textColor="@color/briar_text_primary"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
android:visibility="gone"
|
||||
tools:text="This is a blog post title which can also be longer"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/listitem_vertical_margin">
|
||||
<TextView
|
||||
android:id="@+id/bodyView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/titleView"
|
||||
android:layout_marginEnd="@dimen/margin_medium"
|
||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||
android:textColor="@color/briar_text_secondary"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
tools:text="This is a body text that shows the content of a blog post. This one is not short, but it is also not too long."/>
|
||||
|
||||
<org.briarproject.android.util.AuthorView
|
||||
android:id="@+id/rebloggerView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginBottom="@dimen/listitem_horizontal_margin"
|
||||
android:layout_toLeftOf="@+id/commentView"
|
||||
app:persona="reblogger"/>
|
||||
<View
|
||||
style="@style/Divider.ForumList"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_below="@+id/bodyView"
|
||||
android:layout_marginTop="@dimen/listitem_vertical_margin"/>
|
||||
|
||||
<org.briarproject.android.util.AuthorView
|
||||
android:id="@+id/authorView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_below="@+id/rebloggerView"
|
||||
android:layout_marginBottom="@dimen/listitem_vertical_margin"
|
||||
android:layout_toLeftOf="@+id/commentView"/>
|
||||
</RelativeLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/commentView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:contentDescription="@string/blogs_reblog_comment_hint"
|
||||
android:padding="@dimen/margin_small"
|
||||
android:src="@drawable/ic_repeat"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bodyView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/authorView"
|
||||
android:textColor="@color/briar_text_secondary"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
tools:text="This is a body text that shows the content of a blog post.\n\nThis one is not short, but it is also not too long."/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/commentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
layout="@layout/list_item_blog_comment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</android.support.v7.widget.CardView>
|
||||
|
||||
@@ -6,45 +6,37 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/msgBody"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/message_bubble_margin_tail"
|
||||
android:layout_marginRight="@dimen/message_bubble_margin_non_tail"
|
||||
android:background="@drawable/msg_in_top"
|
||||
android:textColor="@color/briar_text_primary"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
tools:text="Short message"/>
|
||||
<include
|
||||
android:id="@+id/messageLayout"
|
||||
layout="@layout/list_item_msg_in"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/noticeLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left|start"
|
||||
android:background="@drawable/notice_in"
|
||||
android:layout_marginLeft="@dimen/message_bubble_margin_tail"
|
||||
android:layout_marginRight="@dimen/message_bubble_margin_non_tail"
|
||||
android:background="@drawable/notice_in_bottom">
|
||||
android:layout_marginRight="@dimen/message_bubble_margin_non_tail">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/introductionText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="80dp"
|
||||
android:textColor="@color/briar_text_secondary"
|
||||
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_alignEnd="@+id/introductionText"
|
||||
android:layout_alignRight="@+id/introductionText"
|
||||
android:layout_below="@+id/declineButton"
|
||||
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
|
||||
android:textColor="@color/private_message_date"
|
||||
android:textSize="@dimen/text_size_tiny"
|
||||
tools:text="Dec 24, 13:37"/>
|
||||
@@ -64,8 +56,8 @@
|
||||
style="@style/BriarButtonFlat.Negative"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/introductionText"
|
||||
android:layout_marginBottom="-15dp"
|
||||
android:layout_below="@+id/introductionText"
|
||||
android:layout_toLeftOf="@+id/acceptButton"
|
||||
android:layout_toStartOf="@+id/acceptButton"
|
||||
android:text="@string/decline"/>
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
android:id="@+id/msgBody"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/briar_text_primary"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
android:textColor="@color/briar_text_primary"
|
||||
tools:text="Short message"/>
|
||||
|
||||
<TextView
|
||||
|
||||
@@ -6,44 +6,36 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/msgBody"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/message_bubble_margin_non_tail"
|
||||
android:layout_marginRight="@dimen/message_bubble_margin_tail"
|
||||
android:background="@drawable/msg_out_top"
|
||||
android:textColor="@color/briar_text_primary_inverse"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
tools:text="This is a long long long message that spans over several lines.\n\nIt ends here."/>
|
||||
<include
|
||||
android:id="@+id/messageLayout"
|
||||
layout="@layout/list_item_msg_out"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/noticeLayout"
|
||||
android:layout_width="match_parent"
|
||||
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"
|
||||
android:background="@drawable/notice_out_bottom">
|
||||
android:layout_marginRight="@dimen/message_bubble_margin_tail">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/introductionText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/briar_text_secondary"
|
||||
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:layout_marginTop="@dimen/message_bubble_timestamp_margin"
|
||||
android:textColor="@color/private_message_date"
|
||||
android:textSize="@dimen/text_size_tiny"
|
||||
tools:text="Dec 24, 13:37"/>
|
||||
@@ -52,10 +44,10 @@
|
||||
android:id="@+id/introductionStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@+id/introductionTime"
|
||||
android:layout_marginLeft="@dimen/margin_medium"
|
||||
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"/>
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right|end"
|
||||
android:background="@drawable/msg_out"
|
||||
android:layout_marginLeft="@dimen/message_bubble_margin_non_tail"
|
||||
android:layout_marginRight="@dimen/message_bubble_margin_tail"
|
||||
android:background="@drawable/msg_out">
|
||||
android:layout_marginRight="@dimen/message_bubble_margin_tail">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/msgBody"
|
||||
@@ -28,11 +28,11 @@
|
||||
android:id="@+id/msgTime"
|
||||
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/msgBody"
|
||||
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/private_message_date_inverse"
|
||||
android:textSize="@dimen/text_size_tiny"
|
||||
tools:text="Dec 24, 13:37"/>
|
||||
|
||||
@@ -6,46 +6,37 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/msgBody"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left|start"
|
||||
android:layout_marginLeft="@dimen/message_bubble_margin_tail"
|
||||
android:layout_marginRight="@dimen/message_bubble_margin_non_tail"
|
||||
android:background="@drawable/msg_in_top"
|
||||
android:textColor="@color/briar_text_primary"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
tools:text="Short message"/>
|
||||
<include
|
||||
android:id="@+id/messageLayout"
|
||||
layout="@layout/list_item_msg_in"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/noticeLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left|start"
|
||||
android:background="@drawable/notice_in"
|
||||
android:layout_marginLeft="@dimen/message_bubble_margin_tail"
|
||||
android:layout_marginRight="@dimen/message_bubble_margin_non_tail"
|
||||
android:background="@drawable/notice_in_bottom">
|
||||
android:layout_marginRight="@dimen/message_bubble_margin_non_tail">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/introductionText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="80dp"
|
||||
android:textColor="@color/briar_text_secondary"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
android:textStyle="italic"
|
||||
android:textColor="@color/briar_text_secondary"
|
||||
tools:text="@string/forum_invitation_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_alignEnd="@+id/introductionText"
|
||||
android:layout_alignRight="@+id/introductionText"
|
||||
android:layout_below="@+id/showInvitationsButton"
|
||||
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
|
||||
android:textColor="@color/private_message_date"
|
||||
android:textSize="@dimen/text_size_tiny"
|
||||
tools:text="Dec 24, 13:37"/>
|
||||
@@ -55,10 +46,10 @@
|
||||
style="@style/BriarButtonFlat.Positive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="-15dp"
|
||||
android:layout_alignEnd="@+id/introductionText"
|
||||
android:layout_alignRight="@+id/introductionText"
|
||||
android:layout_below="@+id/introductionText"
|
||||
android:layout_marginBottom="-15dp"
|
||||
tools:text="@string/forum_show_invitations"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_write_blog_post"
|
||||
android:icon="@drawable/forum_item_create_white"
|
||||
android:title="@string/blogs_write_blog_post"
|
||||
android:visible="false"
|
||||
app:showAsAction="ifRoom"
|
||||
tools:visible="true"/>
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_blog_share"
|
||||
@@ -27,8 +18,6 @@
|
||||
android:id="@+id/action_blog_delete"
|
||||
android:icon="@drawable/action_delete_white"
|
||||
android:title="@string/blogs_remove_blog"
|
||||
android:visible="false"
|
||||
app:showAsAction="never"
|
||||
tools:visible="true"/>
|
||||
app:showAsAction="never"/>
|
||||
|
||||
</menu>
|
||||
12
briar-android/res/menu/blogs_my_actions.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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_create_blog"
|
||||
android:icon="@drawable/ic_add_white"
|
||||
android:title="@string/blogs_my_blogs_create"
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
||||
</menu>
|
||||
12
briar-android/res/menu/blogs_my_blog_actions.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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_write_blog_post"
|
||||
android:icon="@drawable/forum_item_create_white"
|
||||
android:title="@string/blogs_write_blog_post"
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
||||
</menu>
|
||||
@@ -1,17 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<declare-styleable name="BriarRecyclerView">
|
||||
<attr name="scrollToEnd" format="boolean" />
|
||||
<attr name="emptyText" format="string" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="AuthorView">
|
||||
<attr name="persona" format="enum">
|
||||
<enum name="normal" value="0"/>
|
||||
<enum name="reblogger" value="1"/>
|
||||
<enum name="commenter" value="2"/>
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
||||
@@ -35,6 +35,7 @@
|
||||
<color name="menu_background">#FFFFFF</color>
|
||||
|
||||
<color name="spinner_border">#61000000</color> <!-- 38% Black -->
|
||||
<color name="spinner_arrow">@color/briar_blue_dark</color>
|
||||
<color name="forum_discussion_nested_line">#cfd2d4</color>
|
||||
<color name="forum_cell_highlight">#ffffff</color>
|
||||
</resources>
|
||||
@@ -27,6 +27,7 @@
|
||||
<dimen name="listitem_picture_frame_size">53dp</dimen>
|
||||
<dimen name="listitem_picture_frame_offset">2dp</dimen>
|
||||
<dimen name="listitem_selectable_picture_size">40dp</dimen>
|
||||
<dimen name="dropdown_picture_size">32dp</dimen>
|
||||
<dimen name="avatar_forum_size">48dp</dimen>
|
||||
<dimen name="avatar_border_width">2dp</dimen>
|
||||
<dimen name="avatar_text_size">30sp</dimen>
|
||||
@@ -35,15 +36,11 @@
|
||||
<dimen name="unread_bubble_padding_horizontal">6dp</dimen>
|
||||
<dimen name="unread_bubble_size">19dp</dimen>
|
||||
|
||||
<dimen name="message_bubble_margin_tail">3dp</dimen>
|
||||
<dimen name="message_bubble_margin_non_tail">30dp</dimen>
|
||||
<dimen name="message_bubble_timestamp_margin">7dp</dimen>
|
||||
<dimen name="message_bubble_margin_tail">14dp</dimen>
|
||||
<dimen name="message_bubble_margin_non_tail">51dp</dimen>
|
||||
<dimen name="message_bubble_timestamp_margin">15dp</dimen>
|
||||
<dimen name="forum_nested_line_width">2dp</dimen>
|
||||
<dimen name="forum_nested_indicator">24dp</dimen>
|
||||
<dimen name="forum_avatar_size">20dp</dimen>
|
||||
|
||||
<dimen name="blogs_avatar_normal_size">30dp</dimen>
|
||||
<dimen name="blogs_avatar_icon_size">15dp</dimen>
|
||||
<dimen name="blogs_avatar_comment_size">20dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<string name="nav_drawer_close_description">Close the navigation drawer</string>
|
||||
<string name="contact_list_button">Contacts</string>
|
||||
<string name="forums_button">Forums</string>
|
||||
<string name="blogs_button">Blogs</string>
|
||||
<string name="blogs_button">Micro Blogs</string>
|
||||
<string name="settings_button">Settings</string>
|
||||
<string name="sign_out_button">Sign Out</string>
|
||||
|
||||
@@ -73,7 +73,6 @@
|
||||
<string name="offline">Offline</string>
|
||||
<string name="send">Send</string>
|
||||
<string name="no_data">No data</string>
|
||||
<string name="ellipsis">…</string>
|
||||
|
||||
<!-- Contacts and Private Conversations-->
|
||||
<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>
|
||||
@@ -135,10 +134,8 @@
|
||||
<string name="introduction_response_accepted_received">%1$s accepted the introduction to %2$s.</string>
|
||||
<string name="introduction_response_declined_received">%1$s declined the introduction to %2$s.</string>
|
||||
<string name="introduction_response_declined_received_by_introducee">%1$s says that %2$s declined the introduction.</string>
|
||||
<plurals name="introduction_notification_text">
|
||||
<item quantity="one">New contact added.</item>
|
||||
<item quantity="other">%d new contacts added.</item>
|
||||
</plurals>
|
||||
<string name="introduction_success_title">Introduced contact was added</string>
|
||||
<string name="introduction_success_text">You have been introduced to %1$s.</string>
|
||||
|
||||
<!-- Forums -->
|
||||
<string name="no_forums">You don\'t have any forums yet.\n\nWhy don\'t you create a new one yourself by tapping the + icon at the top?\n\nYou can also ask your contacts to share forums with you.</string>
|
||||
@@ -200,16 +197,19 @@
|
||||
<string name="nobody">Nobody</string>
|
||||
|
||||
<!-- Blogs -->
|
||||
<string name="blogs_feed">Feed</string>
|
||||
<string name="blogs_my_blogs">My Blogs</string>
|
||||
<string name="blogs_my_blogs_create">Create Blog</string>
|
||||
<string name="blogs_my_blogs_label">Add new Blog</string>
|
||||
<string name="blogs_my_blogs_create_hint_title">Blog title (cannot be changed later)</string>
|
||||
<string name="blogs_my_blogs_create_hint_desc">A short description of your new blog</string>
|
||||
<string name="blogs_my_blogs_create_hint_desc_explanation">Potential readers may or may not subscribe to your blog based on the content of the description.</string>
|
||||
<string name="blogs_my_blogs_empty_state">You don\'t have any blogs.\n\nWhy don\'t you create one now by clicking the plus in the top right screen corner?</string>
|
||||
<string name="blogs_my_blogs_blog_empty_state">This is the place for content of your blog.\n\nIt seems like you haven\'t written anything yet.\n\nPlease tap the pen icon to compose a new blog post.\n\nDon\'t forget to go public and share your blog.</string>
|
||||
<string name="blogs_my_blogs_created">Blog created</string>
|
||||
<string name="blogs_blog_is_empty">This blog is empty</string>
|
||||
<string name="blogs_other_blog_empty_state">This blog is currently empty.\n\nEither the author hasn\'t written anything yet, or the person who shared this blog with you needs to come online, so posts can be synchronized.</string>
|
||||
<string name="tag_new">NEW</string>
|
||||
<string name="read_more">read more</string>
|
||||
<string name="blogs_write_blog_post">Write Blog Post</string>
|
||||
<string name="blogs_write_blog_post_title_hint">Add a title (optional)</string>
|
||||
<string name="blogs_write_blog_post_body_hint">Type your blog post here</string>
|
||||
@@ -225,11 +225,10 @@
|
||||
<string name="blogs_remove_blog_dialog_message">Are you sure that you want to remove this blog and all posts?\nNote that this will not remove the blog from other people\'s devices.</string>
|
||||
<string name="blogs_remove_blog_ok">Remove Blog</string>
|
||||
<string name="blogs_blog_removed">Blog Removed</string>
|
||||
<string name="blogs_reblog_comment_hint">Add an optional comment</string>
|
||||
<string name="blogs_reblog_button">Reblog</string>
|
||||
|
||||
<string name="blogs_blog_list">Blog List</string>
|
||||
<string name="blogs_available_blogs">Available Blogs</string>
|
||||
<string name="blogs_drafts">Drafts</string>
|
||||
|
||||
<!-- Blog Sharing -->
|
||||
<string name="blogs_sharing_share">Share Blog</string>
|
||||
@@ -308,14 +307,9 @@
|
||||
<string name="feedback_settings_title">Feedback</string>
|
||||
<string name="send_feedback">Send feedback</string>
|
||||
|
||||
<!-- Link Warning -->
|
||||
<string name="link_warning_title">Link Warning</string>
|
||||
<string name="link_warning_intro">You are about to open the following link with an external app.</string>
|
||||
<string name="link_warning_text">This can be used to identify you. Think about whether you trust the person that sent you this link and consider opening it with Orfox.</string>
|
||||
<string name="link_warning_open_link">Open Link</string>
|
||||
|
||||
<!-- Multiple Identities -->
|
||||
<string name="anonymous">Anonymous</string>
|
||||
<string name="new_identity_item">New identity\u2026</string>
|
||||
<string name="new_identity_title">New Identity</string>
|
||||
<string name="create_identity_button">Create Identity</string>
|
||||
<string name="identity_created_toast">Identity created</string>
|
||||
|
||||
@@ -124,11 +124,6 @@
|
||||
<item name="tabTextColor">@color/briar_text_primary_inverse</item>
|
||||
</style>
|
||||
|
||||
<style name="BriarCard" parent="CardView">
|
||||
<item name="cardUseCompatPadding">true</item>
|
||||
<item name="android:layout_margin">@dimen/margin_small</item>
|
||||
</style>
|
||||
|
||||
<!-- This fixes the missing TextAppearance.Design.Counter.Overflow style -->
|
||||
<style name="BriarTextCounter.Overflow" parent="TextAppearance.Design.Counter">
|
||||
<item name="android:textColor">@color/briar_button_negative</item>
|
||||
|
||||
@@ -6,13 +6,10 @@ import org.briarproject.android.blogs.BlogActivity;
|
||||
import org.briarproject.android.blogs.BlogFragment;
|
||||
import org.briarproject.android.blogs.BlogListFragment;
|
||||
import org.briarproject.android.blogs.BlogPostFragment;
|
||||
import org.briarproject.android.blogs.BlogPostPagerFragment;
|
||||
import org.briarproject.android.blogs.BlogsFragment;
|
||||
import org.briarproject.android.blogs.CreateBlogActivity;
|
||||
import org.briarproject.android.blogs.FeedPostFragment;
|
||||
import org.briarproject.android.blogs.FeedFragment;
|
||||
import org.briarproject.android.blogs.FeedPostPagerFragment;
|
||||
import org.briarproject.android.blogs.ReblogActivity;
|
||||
import org.briarproject.android.blogs.ReblogFragment;
|
||||
import org.briarproject.android.blogs.MyBlogsFragment;
|
||||
import org.briarproject.android.blogs.RssFeedImportActivity;
|
||||
import org.briarproject.android.blogs.RssFeedManageActivity;
|
||||
import org.briarproject.android.blogs.WriteBlogPostActivity;
|
||||
@@ -26,7 +23,7 @@ import org.briarproject.android.introduction.ContactChooserFragment;
|
||||
import org.briarproject.android.introduction.IntroductionActivity;
|
||||
import org.briarproject.android.introduction.IntroductionMessageFragment;
|
||||
import org.briarproject.android.invitation.AddContactActivity;
|
||||
import org.briarproject.android.keyagreement.IntroFragment;
|
||||
import org.briarproject.android.keyagreement.ChooseIdentityFragment;
|
||||
import org.briarproject.android.keyagreement.KeyAgreementActivity;
|
||||
import org.briarproject.android.keyagreement.ShowQrCodeFragment;
|
||||
import org.briarproject.android.panic.PanicPreferencesActivity;
|
||||
@@ -95,14 +92,6 @@ public interface ActivityComponent {
|
||||
void inject(BlogFragment fragment);
|
||||
|
||||
void inject(BlogPostFragment fragment);
|
||||
void inject(FeedPostFragment fragment);
|
||||
|
||||
void inject(BlogPostPagerFragment fragment);
|
||||
void inject(FeedPostPagerFragment fragment);
|
||||
|
||||
void inject(ReblogFragment fragment);
|
||||
|
||||
void inject(ReblogActivity activity);
|
||||
|
||||
void inject(SettingsActivity activity);
|
||||
|
||||
@@ -117,9 +106,11 @@ public interface ActivityComponent {
|
||||
// Fragments
|
||||
void inject(ContactListFragment fragment);
|
||||
void inject(ForumListFragment fragment);
|
||||
void inject(BlogsFragment fragment);
|
||||
void inject(BlogListFragment fragment);
|
||||
void inject(FeedFragment fragment);
|
||||
void inject(IntroFragment fragment);
|
||||
void inject(MyBlogsFragment fragment);
|
||||
void inject(ChooseIdentityFragment fragment);
|
||||
void inject(ShowQrCodeFragment fragment);
|
||||
void inject(ContactChooserFragment fragment);
|
||||
void inject(ContactSelectorFragment fragment);
|
||||
|
||||
@@ -2,14 +2,10 @@ package org.briarproject.android;
|
||||
|
||||
import android.app.Application;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
|
||||
@@ -18,6 +14,7 @@ import org.briarproject.android.api.AndroidExecutor;
|
||||
import org.briarproject.android.api.AndroidNotificationManager;
|
||||
import org.briarproject.android.contact.ConversationActivity;
|
||||
import org.briarproject.android.forum.ForumActivity;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.db.DatabaseExecutor;
|
||||
import org.briarproject.api.db.DbException;
|
||||
@@ -62,38 +59,20 @@ import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
|
||||
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.android.BriarActivity.GROUP_ID;
|
||||
import static org.briarproject.android.NavDrawerActivity.INTENT_BLOGS;
|
||||
import static org.briarproject.android.NavDrawerActivity.INTENT_CONTACTS;
|
||||
import static org.briarproject.android.NavDrawerActivity.INTENT_FORUMS;
|
||||
import static org.briarproject.android.fragment.SettingsFragment.PREF_NOTIFY_BLOG;
|
||||
import static org.briarproject.android.fragment.SettingsFragment.SETTINGS_NAMESPACE;
|
||||
|
||||
class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
Service, EventListener {
|
||||
|
||||
// Notification IDs
|
||||
private static final int PRIVATE_MESSAGE_NOTIFICATION_ID = 3;
|
||||
private static final int FORUM_POST_NOTIFICATION_ID = 4;
|
||||
private static final int BLOG_POST_NOTIFICATION_ID = 5;
|
||||
private static final int INTRODUCTION_SUCCESS_NOTIFICATION_ID = 6;
|
||||
|
||||
// Content URIs to differentiate between pending intents
|
||||
private static final String CONTACT_URI =
|
||||
"content://org.briarproject/contact";
|
||||
private static final String FORUM_URI =
|
||||
"content://org.briarproject/forum";
|
||||
private static final String BLOG_URI =
|
||||
"content://org.briarproject/blog";
|
||||
|
||||
// Actions for intents that are broadcast when notifications are dismissed
|
||||
private static final String CLEAR_PRIVATE_MESSAGE_ACTION =
|
||||
"org.briarproject.briar.CLEAR_PRIVATE_MESSAGE_NOTIFICATION";
|
||||
private static final String CLEAR_FORUM_ACTION =
|
||||
"org.briarproject.briar.CLEAR_FORUM_NOTIFICATION";
|
||||
private static final String CLEAR_BLOG_ACTION =
|
||||
"org.briarproject.briar.CLEAR_BLOG_NOTIFICATION";
|
||||
private static final String CLEAR_INTRODUCTION_ACTION =
|
||||
"org.briarproject.briar.CLEAR_INTRODUCTION_NOTIFICATION";
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(AndroidNotificationManagerImpl.class.getName());
|
||||
@@ -103,19 +82,16 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
private final MessagingManager messagingManager;
|
||||
private final AndroidExecutor androidExecutor;
|
||||
private final Context appContext;
|
||||
private final BroadcastReceiver receiver = new DeleteIntentReceiver();
|
||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||
|
||||
// The following must only be accessed on the main UI thread
|
||||
private final Map<GroupId, Integer> contactCounts = new HashMap<>();
|
||||
private final Map<GroupId, Integer> forumCounts = new HashMap<>();
|
||||
private final Map<GroupId, Integer> blogCounts = new HashMap<>();
|
||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||
|
||||
private int contactTotal = 0, forumTotal = 0, blogTotal = 0;
|
||||
private int introductionTotal = 0;
|
||||
private int nextRequestId = 0;
|
||||
private GroupId blockedGroup = null;
|
||||
private boolean blockContacts = false, blockForums = false;
|
||||
private boolean blockBlogs = false, blockIntroductions = false;
|
||||
private GroupId visibleGroup = null;
|
||||
private boolean blogBlocked = false;
|
||||
|
||||
private volatile Settings settings = new Settings();
|
||||
|
||||
@@ -133,43 +109,21 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
@Override
|
||||
public void startService() throws ServiceException {
|
||||
if (used.getAndSet(true)) throw new IllegalStateException();
|
||||
// Load settings
|
||||
try {
|
||||
settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
|
||||
} catch (DbException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
// Register a broadcast receiver for notifications being dismissed
|
||||
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(CLEAR_PRIVATE_MESSAGE_ACTION);
|
||||
filter.addAction(CLEAR_FORUM_ACTION);
|
||||
filter.addAction(CLEAR_BLOG_ACTION);
|
||||
filter.addAction(CLEAR_INTRODUCTION_ACTION);
|
||||
appContext.registerReceiver(receiver, filter);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
try {
|
||||
f.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopService() throws ServiceException {
|
||||
// Clear all notifications and unregister the broadcast receiver
|
||||
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
|
||||
Future<Void> f = androidExecutor.submit(new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() {
|
||||
clearPrivateMessageNotification();
|
||||
clearForumPostNotification();
|
||||
clearBlogPostNotification();
|
||||
clearIntroductionSuccessNotification();
|
||||
appContext.unregisterReceiver(receiver);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
@@ -180,36 +134,19 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void clearPrivateMessageNotification() {
|
||||
contactCounts.clear();
|
||||
contactTotal = 0;
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.cancel(PRIVATE_MESSAGE_NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void clearForumPostNotification() {
|
||||
forumCounts.clear();
|
||||
forumTotal = 0;
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.cancel(FORUM_POST_NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void clearBlogPostNotification() {
|
||||
blogCounts.clear();
|
||||
blogTotal = 0;
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.cancel(BLOG_POST_NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void clearIntroductionSuccessNotification() {
|
||||
introductionTotal = 0;
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.cancel(INTRODUCTION_SUCCESS_NOTIFICATION_ID);
|
||||
@@ -221,28 +158,29 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
|
||||
if (s.getNamespace().equals(SETTINGS_NAMESPACE)) loadSettings();
|
||||
} else if (e instanceof PrivateMessageReceivedEvent) {
|
||||
PrivateMessageReceivedEvent p = (PrivateMessageReceivedEvent) e;
|
||||
showPrivateMessageNotification(p.getGroupId());
|
||||
PrivateMessageReceivedEvent m = (PrivateMessageReceivedEvent) e;
|
||||
showPrivateMessageNotification(m.getGroupId());
|
||||
} else if (e instanceof ForumPostReceivedEvent) {
|
||||
ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
|
||||
showForumPostNotification(f.getGroupId());
|
||||
ForumPostReceivedEvent m = (ForumPostReceivedEvent) e;
|
||||
showForumPostNotification(m.getGroupId());
|
||||
} else if (e instanceof BlogPostAddedEvent) {
|
||||
BlogPostAddedEvent b = (BlogPostAddedEvent) e;
|
||||
showBlogPostNotification(b.getGroupId());
|
||||
BlogPostAddedEvent be = (BlogPostAddedEvent) e;
|
||||
showBlogPostNotification(be.getGroupId());
|
||||
} else if (e instanceof IntroductionRequestReceivedEvent) {
|
||||
ContactId c = ((IntroductionRequestReceivedEvent) e).getContactId();
|
||||
showNotificationForPrivateConversation(c);
|
||||
} else if (e instanceof IntroductionResponseReceivedEvent) {
|
||||
ContactId c = ((IntroductionResponseReceivedEvent) e).getContactId();
|
||||
showNotificationForPrivateConversation(c);
|
||||
} else if (e instanceof IntroductionSucceededEvent) {
|
||||
Contact c = ((IntroductionSucceededEvent) e).getContact();
|
||||
showIntroductionSucceededNotification(c);
|
||||
} else if (e instanceof InvitationReceivedEvent) {
|
||||
ContactId c = ((InvitationReceivedEvent) e).getContactId();
|
||||
showNotificationForPrivateConversation(c);
|
||||
} else if (e instanceof InvitationResponseReceivedEvent) {
|
||||
ContactId c = ((InvitationResponseReceivedEvent) e).getContactId();
|
||||
showNotificationForPrivateConversation(c);
|
||||
} else if (e instanceof IntroductionSucceededEvent) {
|
||||
showIntroductionNotification();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,35 +198,35 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
});
|
||||
}
|
||||
|
||||
private void showPrivateMessageNotification(final GroupId g) {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void showPrivateMessageNotification(final GroupId g) {
|
||||
androidExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (blockContacts) return;
|
||||
if (g.equals(blockedGroup)) return;
|
||||
Integer count = contactCounts.get(g);
|
||||
if (count == null) contactCounts.put(g, 1);
|
||||
else contactCounts.put(g, count + 1);
|
||||
contactTotal++;
|
||||
updatePrivateMessageNotification();
|
||||
if (!g.equals(visibleGroup))
|
||||
updatePrivateMessageNotification();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearPrivateMessageNotification(final GroupId g) {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
androidExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Integer count = contactCounts.remove(g);
|
||||
if (count == null) return; // Already cleared
|
||||
contactTotal -= count;
|
||||
// FIXME: If the notification isn't showing, this may show it
|
||||
updatePrivateMessageNotification();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void updatePrivateMessageNotification() {
|
||||
if (contactTotal == 0) {
|
||||
clearPrivateMessageNotification();
|
||||
@@ -307,13 +245,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
b.setDefaults(getDefaults());
|
||||
b.setOnlyAlertOnce(true);
|
||||
b.setAutoCancel(true);
|
||||
// Clear the counters if the notification is dismissed
|
||||
Intent clear = new Intent(CLEAR_PRIVATE_MESSAGE_ACTION);
|
||||
PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
|
||||
clear, 0);
|
||||
b.setDeleteIntent(delete);
|
||||
if (contactCounts.size() == 1) {
|
||||
// Touching the notification shows the relevant conversation
|
||||
Intent i = new Intent(appContext, ConversationActivity.class);
|
||||
GroupId g = contactCounts.keySet().iterator().next();
|
||||
i.putExtra(GROUP_ID, g.getBytes());
|
||||
@@ -325,11 +257,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
t.addNextIntent(i);
|
||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||
} else {
|
||||
// Touching the notification shows the contact list
|
||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||
i.putExtra(INTENT_CONTACTS, true);
|
||||
i.putExtra(NavDrawerActivity.INTENT_CONTACTS, true);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i.setData(Uri.parse(CONTACT_URI));
|
||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||
t.addParentStack(NavDrawerActivity.class);
|
||||
t.addNextIntent(i);
|
||||
@@ -345,7 +275,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private int getDefaults() {
|
||||
int defaults = DEFAULT_LIGHTS;
|
||||
boolean sound = settings.getBoolean("notifySound", true);
|
||||
@@ -358,46 +287,34 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllContactNotifications() {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
public void showForumPostNotification(final GroupId g) {
|
||||
androidExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clearPrivateMessageNotification();
|
||||
clearIntroductionSuccessNotification();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void showForumPostNotification(final GroupId g) {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (blockForums) return;
|
||||
if (g.equals(blockedGroup)) return;
|
||||
Integer count = forumCounts.get(g);
|
||||
if (count == null) forumCounts.put(g, 1);
|
||||
else forumCounts.put(g, count + 1);
|
||||
forumTotal++;
|
||||
updateForumPostNotification();
|
||||
if (!g.equals(visibleGroup))
|
||||
updateForumPostNotification();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearForumPostNotification(final GroupId g) {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
androidExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Integer count = forumCounts.remove(g);
|
||||
if (count == null) return; // Already cleared
|
||||
forumTotal -= count;
|
||||
// FIXME: If the notification isn't showing, this may show it
|
||||
updateForumPostNotification();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void updateForumPostNotification() {
|
||||
if (forumTotal == 0) {
|
||||
clearForumPostNotification();
|
||||
@@ -415,13 +332,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
b.setDefaults(getDefaults());
|
||||
b.setOnlyAlertOnce(true);
|
||||
b.setAutoCancel(true);
|
||||
// Clear the counters if the notification is dismissed
|
||||
Intent clear = new Intent(CLEAR_FORUM_ACTION);
|
||||
PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
|
||||
clear, 0);
|
||||
b.setDeleteIntent(delete);
|
||||
if (forumCounts.size() == 1) {
|
||||
// Touching the notification shows the relevant forum
|
||||
Intent i = new Intent(appContext, ForumActivity.class);
|
||||
GroupId g = forumCounts.keySet().iterator().next();
|
||||
i.putExtra(GROUP_ID, g.getBytes());
|
||||
@@ -433,11 +344,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
t.addNextIntent(i);
|
||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||
} else {
|
||||
// Touching the notification shows the forum list
|
||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||
i.putExtra(INTENT_FORUMS, true);
|
||||
i.putExtra(NavDrawerActivity.INTENT_FORUMS, true);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i.setData(Uri.parse(FORUM_URI));
|
||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||
t.addParentStack(NavDrawerActivity.class);
|
||||
t.addNextIntent(i);
|
||||
@@ -454,49 +363,28 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllForumPostNotifications() {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
public void showBlogPostNotification(final GroupId g) {
|
||||
androidExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clearForumPostNotification();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void showBlogPostNotification(final GroupId g) {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (blockBlogs) return;
|
||||
if (g.equals(blockedGroup)) return;
|
||||
Integer count = blogCounts.get(g);
|
||||
if (count == null) blogCounts.put(g, 1);
|
||||
else blogCounts.put(g, count + 1);
|
||||
blogTotal++;
|
||||
updateBlogPostNotification();
|
||||
if (!blogBlocked) {
|
||||
blogTotal++;
|
||||
updateBlogPostNotification();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearBlogPostNotification(final GroupId g) {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Integer count = blogCounts.remove(g);
|
||||
if (count == null) return; // Already cleared
|
||||
blogTotal -= count;
|
||||
updateBlogPostNotification();
|
||||
}
|
||||
});
|
||||
public void clearBlogPostNotification() {
|
||||
blogTotal = 0;
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.cancel(BLOG_POST_NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void updateBlogPostNotification() {
|
||||
if (blogTotal == 0) {
|
||||
clearBlogPostNotification();
|
||||
} else if (settings.getBoolean(PREF_NOTIFY_BLOG, true)) {
|
||||
if (settings.getBoolean(PREF_NOTIFY_BLOG, true)) {
|
||||
NotificationCompat.Builder b =
|
||||
new NotificationCompat.Builder(appContext);
|
||||
b.setSmallIcon(R.drawable.message_notification_icon);
|
||||
@@ -510,20 +398,15 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
b.setDefaults(getDefaults());
|
||||
b.setOnlyAlertOnce(true);
|
||||
b.setAutoCancel(true);
|
||||
// Clear the counters if the notification is dismissed
|
||||
Intent clear = new Intent(CLEAR_BLOG_ACTION);
|
||||
PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
|
||||
clear, 0);
|
||||
b.setDeleteIntent(delete);
|
||||
// Touching the notification shows the combined blog feed
|
||||
|
||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||
i.putExtra(INTENT_BLOGS, true);
|
||||
i.putExtra(NavDrawerActivity.INTENT_BLOGS, true);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i.setData(Uri.parse(BLOG_URI));
|
||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||
t.addParentStack(NavDrawerActivity.class);
|
||||
t.addNextIntent(i);
|
||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
b.setCategory(CATEGORY_SOCIAL);
|
||||
b.setVisibility(VISIBILITY_SECRET);
|
||||
@@ -534,154 +417,53 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllBlogPostNotifications() {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clearBlogPostNotification();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showIntroductionNotification() {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (blockIntroductions) return;
|
||||
introductionTotal++;
|
||||
updateIntroductionNotification();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void updateIntroductionNotification() {
|
||||
NotificationCompat.Builder b =
|
||||
new NotificationCompat.Builder(appContext);
|
||||
b.setSmallIcon(R.drawable.introduction_notification);
|
||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||
b.setContentText(appContext.getResources().getQuantityString(
|
||||
R.plurals.introduction_notification_text, introductionTotal,
|
||||
introductionTotal));
|
||||
String ringtoneUri = settings.get("notifyRingtoneUri");
|
||||
if (!StringUtils.isNullOrEmpty(ringtoneUri))
|
||||
b.setSound(Uri.parse(ringtoneUri));
|
||||
b.setDefaults(getDefaults());
|
||||
b.setOnlyAlertOnce(true);
|
||||
b.setAutoCancel(true);
|
||||
// Clear the counter if the notification is dismissed
|
||||
Intent clear = new Intent(CLEAR_INTRODUCTION_ACTION);
|
||||
PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
|
||||
clear, 0);
|
||||
b.setDeleteIntent(delete);
|
||||
// Touching the notification shows the contact list
|
||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||
i.putExtra(INTENT_CONTACTS, true);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i.setData(Uri.parse(CONTACT_URI));
|
||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||
t.addParentStack(NavDrawerActivity.class);
|
||||
t.addNextIntent(i);
|
||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
b.setCategory(CATEGORY_MESSAGE);
|
||||
b.setVisibility(VISIBILITY_SECRET);
|
||||
}
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID, b.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blockNotification(final GroupId g) {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
androidExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
blockedGroup = g;
|
||||
visibleGroup = g;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unblockNotification(final GroupId g) {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
androidExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (g.equals(blockedGroup)) blockedGroup = null;
|
||||
if (g.equals(visibleGroup)) visibleGroup = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blockAllContactNotifications() {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
public void blockBlogNotification() {
|
||||
androidExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
blockContacts = true;
|
||||
blockIntroductions = true;
|
||||
blogBlocked = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unblockAllContactNotifications() {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
public void unblockBlogNotification() {
|
||||
androidExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
blockContacts = false;
|
||||
blockIntroductions = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blockAllForumPostNotifications() {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
blockForums = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unblockAllForumPostNotifications() {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
blockForums = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blockAllBlogPostNotifications() {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
blockBlogs = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unblockAllBlogPostNotifications() {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
blockBlogs = false;
|
||||
blogBlocked = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showNotificationForPrivateConversation(final ContactId c) {
|
||||
dbExecutor.execute(new Runnable() {
|
||||
androidExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
GroupId g = messagingManager.getConversationId(c);
|
||||
showPrivateMessageNotification(g);
|
||||
GroupId group = messagingManager.getConversationId(c);
|
||||
showPrivateMessageNotification(group);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
@@ -690,25 +472,35 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
});
|
||||
}
|
||||
|
||||
private class DeleteIntentReceiver extends BroadcastReceiver {
|
||||
private void showIntroductionSucceededNotification(final Contact c) {
|
||||
androidExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
NotificationCompat.Builder b =
|
||||
new NotificationCompat.Builder(appContext);
|
||||
b.setSmallIcon(R.drawable.introduction_notification);
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (CLEAR_PRIVATE_MESSAGE_ACTION.equals(action)) {
|
||||
clearPrivateMessageNotification();
|
||||
} else if (CLEAR_FORUM_ACTION.equals(action)) {
|
||||
clearForumPostNotification();
|
||||
} else if (CLEAR_BLOG_ACTION.equals(action)) {
|
||||
clearBlogPostNotification();
|
||||
} else if (CLEAR_INTRODUCTION_ACTION.equals(action)) {
|
||||
clearIntroductionSuccessNotification();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
b.setContentTitle(appContext
|
||||
.getString(R.string.introduction_success_title));
|
||||
b.setContentText(appContext
|
||||
.getString(R.string.introduction_success_text,
|
||||
c.getAuthor().getName()));
|
||||
b.setDefaults(getDefaults());
|
||||
b.setAutoCancel(true);
|
||||
|
||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||
i.putExtra(NavDrawerActivity.INTENT_CONTACTS, true);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||
t.addParentStack(NavDrawerActivity.class);
|
||||
t.addNextIntent(i);
|
||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||
|
||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||
NotificationManager nm = (NotificationManager) o;
|
||||
nm.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID, b.build());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.briarproject.android;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
@@ -17,14 +16,12 @@ import static android.view.inputmethod.InputMethodManager.SHOW_FORCED;
|
||||
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
||||
import static org.briarproject.android.TestingConstants.PREVENT_SCREENSHOTS;
|
||||
|
||||
public abstract class BaseActivity extends AppCompatActivity
|
||||
implements DestroyableActivity {
|
||||
public abstract class BaseActivity extends AppCompatActivity {
|
||||
|
||||
protected ActivityComponent activityComponent;
|
||||
|
||||
private final List<ActivityLifecycleController> lifecycleControllers =
|
||||
new ArrayList<>();
|
||||
private boolean destroyed = false;
|
||||
|
||||
public abstract void injectActivity(ActivityComponent component);
|
||||
|
||||
@@ -81,17 +78,11 @@ public abstract class BaseActivity extends AppCompatActivity
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
destroyed = true;
|
||||
for (ActivityLifecycleController alc : lifecycleControllers) {
|
||||
alc.onActivityDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public boolean hasBeenDestroyed() {
|
||||
return destroyed;
|
||||
}
|
||||
|
||||
public void showSoftKeyboardForced(View view) {
|
||||
Object o = getSystemService(INPUT_METHOD_SERVICE);
|
||||
((InputMethodManager) o).showSoftInput(view, SHOW_FORCED);
|
||||
|
||||
@@ -59,6 +59,7 @@ public abstract class BriarActivity extends BaseActivity {
|
||||
|
||||
protected void signOut(final boolean removeFromRecentApps) {
|
||||
briarController.signOut(new UiResultHandler<Void>(this) {
|
||||
|
||||
@Override
|
||||
public void onResultUi(Void result) {
|
||||
if (removeFromRecentApps) startExitActivity();
|
||||
|
||||
@@ -7,7 +7,7 @@ import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.blogs.FeedFragment;
|
||||
import org.briarproject.android.blogs.BlogsFragment;
|
||||
import org.briarproject.android.contact.ContactListFragment;
|
||||
import org.briarproject.android.forum.ForumListFragment;
|
||||
import org.briarproject.android.fragment.BaseFragment;
|
||||
@@ -27,7 +27,7 @@ public abstract class BriarFragmentActivity extends BriarActivity {
|
||||
actionBar.setTitle(R.string.contact_list_button);
|
||||
} else if (fragmentTag.equals(ForumListFragment.TAG)) {
|
||||
actionBar.setTitle(R.string.forums_button);
|
||||
} else if (fragmentTag.equals(FeedFragment.TAG)) {
|
||||
} else if (fragmentTag.equals(BlogsFragment.TAG)) {
|
||||
actionBar.setTitle(R.string.blogs_button);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +108,7 @@ public class BriarService extends Service {
|
||||
}
|
||||
|
||||
private void showStartupFailureNotification(final StartResult result) {
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
androidExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
NotificationCompat.Builder b =
|
||||
new NotificationCompat.Builder(BriarService.this);
|
||||
@@ -198,13 +197,11 @@ public class BriarService extends Service {
|
||||
|
||||
private volatile IBinder binder = null;
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder binder) {
|
||||
this.binder = binder;
|
||||
binderLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {}
|
||||
|
||||
/** Waits for the service to connect and returns its binder. */
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.briarproject.android;
|
||||
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
public interface DestroyableActivity {
|
||||
|
||||
void runOnUiThread(Runnable runnable);
|
||||
|
||||
@UiThread
|
||||
boolean hasBeenDestroyed();
|
||||
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.blogs.FeedFragment;
|
||||
import org.briarproject.android.blogs.BlogsFragment;
|
||||
import org.briarproject.android.contact.ContactListFragment;
|
||||
import org.briarproject.android.controller.NavDrawerController;
|
||||
import org.briarproject.android.controller.TransportStateListener;
|
||||
@@ -74,9 +74,7 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
|
||||
super.onNewIntent(intent);
|
||||
exitIfStartupFailed(intent);
|
||||
checkAuthorHandle(intent);
|
||||
// FIXME why was the stack cleared here?
|
||||
// This prevents state from being restored properly
|
||||
// clearBackStack();
|
||||
clearBackStack();
|
||||
if (intent.getBooleanExtra(INTENT_FORUMS, false)) {
|
||||
startFragment(ForumListFragment.newInstance());
|
||||
}
|
||||
@@ -84,7 +82,7 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
|
||||
startFragment(ContactListFragment.newInstance());
|
||||
}
|
||||
else if (intent.getBooleanExtra(INTENT_BLOGS, false)) {
|
||||
startFragment(FeedFragment.newInstance());
|
||||
startFragment(BlogsFragment.newInstance());
|
||||
}
|
||||
setIntent(null);
|
||||
}
|
||||
@@ -188,7 +186,7 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
|
||||
startFragment(ForumListFragment.newInstance());
|
||||
break;
|
||||
case R.id.nav_btn_blogs:
|
||||
startFragment(FeedFragment.newInstance());
|
||||
startFragment(BlogsFragment.newInstance());
|
||||
break;
|
||||
case R.id.nav_btn_settings:
|
||||
startActivity(new Intent(this, SettingsActivity.class));
|
||||
@@ -250,6 +248,7 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
|
||||
@Override
|
||||
public void hideLoadingScreen() {
|
||||
drawerLayout.setDrawerLockMode(LOCK_MODE_UNLOCKED);
|
||||
CustomAnimations.animateHeight(toolbar, true, 250);
|
||||
progressViewGroup.setVisibility(INVISIBLE);
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ public class SplashScreenActivity extends BaseActivity {
|
||||
}
|
||||
|
||||
private void setPreferencesDefaults() {
|
||||
androidExecutor.runOnBackgroundThread(new Runnable() {
|
||||
androidExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
PreferenceManager.setDefaultValues(SplashScreenActivity.this,
|
||||
|
||||
@@ -13,21 +13,10 @@ public interface AndroidExecutor {
|
||||
* Runs the given task on a background thread with a message queue and
|
||||
* returns a Future for getting the result.
|
||||
*/
|
||||
<V> Future<V> runOnBackgroundThread(Callable<V> c);
|
||||
<V> Future<V> submit(Callable<V> c);
|
||||
|
||||
/**
|
||||
* Runs the given task on a background thread with a message queue.
|
||||
*/
|
||||
void runOnBackgroundThread(Runnable r);
|
||||
|
||||
/**
|
||||
* Runs the given task on the main UI thread and returns a Future for
|
||||
* getting the result.
|
||||
*/
|
||||
<V> Future<V> runOnUiThread(Callable<V> c);
|
||||
|
||||
/**
|
||||
* Runs the given task on the main UI thread.
|
||||
*/
|
||||
void runOnUiThread(Runnable r);
|
||||
void execute(Runnable r);
|
||||
}
|
||||
|
||||
@@ -2,37 +2,26 @@ package org.briarproject.android.api;
|
||||
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
/**
|
||||
* Manages notifications for private messages, forum posts, blog posts and
|
||||
* introductions.
|
||||
*/
|
||||
/** Manages notifications for private messages and forum posts. */
|
||||
public interface AndroidNotificationManager {
|
||||
|
||||
void showPrivateMessageNotification(GroupId g);
|
||||
|
||||
void clearPrivateMessageNotification(GroupId g);
|
||||
|
||||
void clearAllContactNotifications();
|
||||
void showForumPostNotification(GroupId g);
|
||||
|
||||
void clearForumPostNotification(GroupId g);
|
||||
|
||||
void clearAllForumPostNotifications();
|
||||
void showBlogPostNotification(GroupId g);
|
||||
|
||||
void clearBlogPostNotification(GroupId g);
|
||||
|
||||
void clearAllBlogPostNotifications();
|
||||
void clearBlogPostNotification();
|
||||
|
||||
void blockNotification(GroupId g);
|
||||
|
||||
void unblockNotification(GroupId g);
|
||||
|
||||
void blockAllContactNotifications();
|
||||
void blockBlogNotification();
|
||||
|
||||
void unblockAllContactNotifications();
|
||||
|
||||
void blockAllForumPostNotifications();
|
||||
|
||||
void unblockAllForumPostNotifications();
|
||||
|
||||
void blockAllBlogPostNotifications();
|
||||
|
||||
void unblockAllBlogPostNotifications();
|
||||
void unblockBlogNotification();
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package org.briarproject.android.blogs;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.api.blogs.BlogPostHeader;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface BaseController {
|
||||
|
||||
@UiThread
|
||||
void onStart();
|
||||
|
||||
@UiThread
|
||||
void onStop();
|
||||
|
||||
void loadBlogPosts(GroupId g,
|
||||
ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler);
|
||||
|
||||
void loadBlogPost(BlogPostHeader header,
|
||||
ResultExceptionHandler<BlogPostItem, DbException> handler);
|
||||
|
||||
void loadBlogPost(GroupId g, MessageId m,
|
||||
ResultExceptionHandler<BlogPostItem, DbException> handler);
|
||||
|
||||
void repeatPost(BlogPostItem item, @Nullable String comment,
|
||||
ResultExceptionHandler<Void, DbException> resultHandler);
|
||||
|
||||
void setOnBlogPostAddedListener(OnBlogPostAddedListener listener);
|
||||
|
||||
interface OnBlogPostAddedListener {
|
||||
@UiThread
|
||||
void onBlogPostAdded(BlogPostHeader header, boolean local);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
package org.briarproject.android.blogs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.annotation.CallSuper;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.briarproject.android.api.AndroidNotificationManager;
|
||||
import org.briarproject.android.controller.DbControllerImpl;
|
||||
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.api.blogs.Blog;
|
||||
import org.briarproject.api.blogs.BlogCommentHeader;
|
||||
import org.briarproject.api.blogs.BlogManager;
|
||||
import org.briarproject.api.blogs.BlogPostHeader;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.event.BlogPostAddedEvent;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
abstract class BaseControllerImpl extends DbControllerImpl
|
||||
implements BaseController, EventListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(BaseControllerImpl.class.getName());
|
||||
|
||||
@Inject
|
||||
protected Activity activity;
|
||||
@Inject
|
||||
protected EventBus eventBus;
|
||||
@Inject
|
||||
protected AndroidNotificationManager notificationManager;
|
||||
@Inject
|
||||
protected IdentityManager identityManager;
|
||||
|
||||
@Inject
|
||||
protected volatile BlogManager blogManager;
|
||||
|
||||
private final Map<MessageId, String> bodyCache = new ConcurrentHashMap<>();
|
||||
private final Map<MessageId, BlogPostHeader> headerCache =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
protected volatile OnBlogPostAddedListener listener;
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onStart() {
|
||||
if (listener == null)
|
||||
throw new IllegalStateException(
|
||||
"OnBlogPostAddedListener needs to be attached");
|
||||
eventBus.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onStop() {
|
||||
eventBus.removeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof BlogPostAddedEvent) {
|
||||
final BlogPostAddedEvent m = (BlogPostAddedEvent) e;
|
||||
LOG.info("New blog post added");
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onBlogPostAdded(m.getHeader(), m.isLocal());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnBlogPostAddedListener(OnBlogPostAddedListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadBlogPosts(final GroupId groupId,
|
||||
final ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler) {
|
||||
if (groupId == null) throw new IllegalStateException();
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Collection<BlogPostItem> items = loadItems(groupId);
|
||||
handler.onResult(items);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
handler.onException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected Collection<BlogPostItem> loadItems(GroupId groupId)
|
||||
throws DbException {
|
||||
long now = System.currentTimeMillis();
|
||||
Collection<BlogPostHeader> headers =
|
||||
blogManager.getPostHeaders(groupId);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading headers took " + duration + " ms");
|
||||
Collection<BlogPostItem> items = new ArrayList<>(headers.size());
|
||||
now = System.currentTimeMillis();
|
||||
for (BlogPostHeader h : headers) {
|
||||
headerCache.put(h.getId(), h);
|
||||
BlogPostItem item = getItem(h);
|
||||
items.add(item);
|
||||
}
|
||||
duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading bodies took " + duration + " ms");
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadBlogPost(final BlogPostHeader header,
|
||||
final ResultExceptionHandler<BlogPostItem, DbException> handler) {
|
||||
|
||||
String body = bodyCache.get(header.getId());
|
||||
if (body != null) {
|
||||
LOG.info("Loaded body from cache");
|
||||
handler.onResult(new BlogPostItem(header, body));
|
||||
return;
|
||||
}
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
BlogPostItem item = getItem(header);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading body took " + duration + " ms");
|
||||
handler.onResult(item);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
handler.onException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadBlogPost(final GroupId g, final MessageId m,
|
||||
final ResultExceptionHandler<BlogPostItem, DbException> handler) {
|
||||
|
||||
BlogPostHeader header = headerCache.get(m);
|
||||
if (header != null) {
|
||||
LOG.info("Loaded header from cache");
|
||||
loadBlogPost(header, handler);
|
||||
return;
|
||||
}
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
BlogPostHeader header = getPostHeader(g, m);
|
||||
BlogPostItem item = getItem(header);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading post took " + duration + " ms");
|
||||
handler.onResult(item);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
handler.onException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void repeatPost(final BlogPostItem item,
|
||||
final @Nullable String comment,
|
||||
final ResultExceptionHandler<Void, DbException> handler) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
LocalAuthor a = identityManager.getLocalAuthor();
|
||||
Blog b = blogManager.getPersonalBlog(a);
|
||||
BlogPostHeader h = item.getHeader();
|
||||
blogManager.addLocalComment(a, b.getId(), comment, h);
|
||||
handler.onResult(null);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
handler.onException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private BlogPostHeader getPostHeader(GroupId g, MessageId m)
|
||||
throws DbException {
|
||||
|
||||
if (g == null) throw new IllegalStateException();
|
||||
BlogPostHeader header = headerCache.get(m);
|
||||
if (header == null) {
|
||||
header = blogManager.getPostHeader(g, m);
|
||||
headerCache.put(m, header);
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
private BlogPostItem getItem(BlogPostHeader h) throws DbException {
|
||||
String body;
|
||||
if (h instanceof BlogCommentHeader) {
|
||||
BlogCommentHeader c = (BlogCommentHeader) h;
|
||||
BlogCommentItem item = new BlogCommentItem(c);
|
||||
body = getPostBody(item.getPostHeader().getId());
|
||||
item.setBody(body);
|
||||
return item;
|
||||
} else {
|
||||
body = getPostBody(h.getId());
|
||||
return new BlogPostItem(h, body);
|
||||
}
|
||||
}
|
||||
|
||||
private String getPostBody(MessageId m) throws DbException {
|
||||
String body = bodyCache.get(m);
|
||||
if (body == null) {
|
||||
body = blogManager.getPostBody(m);
|
||||
if (body != null) bodyCache.put(m, body);
|
||||
}
|
||||
//noinspection ConstantConditions
|
||||
return body;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package org.briarproject.android.blogs;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.CallSuper;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.fragment.BaseFragment;
|
||||
import org.briarproject.api.db.DbException;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.android.util.AndroidUtils.MIN_RESOLUTION;
|
||||
|
||||
public abstract class BasePostFragment extends BaseFragment {
|
||||
|
||||
private final Logger LOG =
|
||||
Logger.getLogger(BasePostFragment.class.getName());
|
||||
|
||||
private View view;
|
||||
private ProgressBar progressBar;
|
||||
private BlogPostViewHolder ui;
|
||||
private BlogPostItem post;
|
||||
private Runnable refresher;
|
||||
|
||||
@CallSuper
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
view = inflater.inflate(R.layout.fragment_blog_post, container,
|
||||
false);
|
||||
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
ui = new BlogPostViewHolder(view);
|
||||
return view;
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
startPeriodicUpdate();
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
stopPeriodicUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
getActivity().onBackPressed();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
protected void onBlogPostLoaded(BlogPostItem post) {
|
||||
progressBar.setVisibility(INVISIBLE);
|
||||
this.post = post;
|
||||
ui.bindItem(post);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
protected void onBlogPostLoadException(DbException exception) {
|
||||
// TODO: Decide how to handle errors in the UI
|
||||
finish();
|
||||
}
|
||||
|
||||
private void startPeriodicUpdate() {
|
||||
refresher = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (ui == null) return;
|
||||
LOG.info("Updating Content...");
|
||||
|
||||
ui.updateDate(post.getTimestamp());
|
||||
view.postDelayed(refresher, MIN_RESOLUTION);
|
||||
}
|
||||
};
|
||||
LOG.info("Adding Handler Callback");
|
||||
view.postDelayed(refresher, MIN_RESOLUTION);
|
||||
}
|
||||
|
||||
private void stopPeriodicUpdate() {
|
||||
if (refresher != null && ui != null) {
|
||||
LOG.info("Removing Handler Callback");
|
||||
view.removeCallbacks(refresher);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
package org.briarproject.android.blogs;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.blogs.BaseController.OnBlogPostAddedListener;
|
||||
import org.briarproject.android.fragment.BaseFragment;
|
||||
import org.briarproject.api.blogs.BlogPostHeader;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.android.blogs.BasePostPagerFragment.BlogPostPagerAdapter.INVALID_POSITION;
|
||||
|
||||
abstract class BasePostPagerFragment extends BaseFragment
|
||||
implements OnBlogPostAddedListener {
|
||||
|
||||
static final String POST_ID = "briar.POST_ID";
|
||||
|
||||
private ViewPager pager;
|
||||
private ProgressBar progressBar;
|
||||
private BlogPostPagerAdapter postPagerAdapter;
|
||||
private MessageId postId;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle state) {
|
||||
|
||||
Bundle args;
|
||||
if (state == null) args = getArguments();
|
||||
else args = state;
|
||||
byte[] p = args.getByteArray(POST_ID);
|
||||
if (p == null)
|
||||
throw new IllegalStateException("No post ID in args");
|
||||
postId = new MessageId(p);
|
||||
|
||||
View v = inflater.inflate(R.layout.fragment_blog_post_pager, container,
|
||||
false);
|
||||
progressBar = (ProgressBar) v.findViewById(R.id.progressBar);
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
|
||||
pager = (ViewPager) v.findViewById(R.id.pager);
|
||||
postPagerAdapter = new BlogPostPagerAdapter(getChildFragmentManager());
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
if (postId == null) {
|
||||
MessageId selected = getSelectedPost();
|
||||
if (selected != null) loadBlogPosts(selected);
|
||||
} else {
|
||||
loadBlogPosts(postId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
MessageId selected = getSelectedPost();
|
||||
if (selected != null)
|
||||
outState.putByteArray(POST_ID, selected.getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlogPostAdded(BlogPostHeader header, boolean local) {
|
||||
loadBlogPost(header);
|
||||
}
|
||||
|
||||
abstract void loadBlogPosts(final MessageId select);
|
||||
|
||||
abstract void loadBlogPost(BlogPostHeader header);
|
||||
|
||||
protected void onBlogPostsLoaded(MessageId select,
|
||||
Collection<BlogPostItem> posts) {
|
||||
|
||||
postId = null;
|
||||
postPagerAdapter.setPosts(posts);
|
||||
selectPost(select);
|
||||
}
|
||||
|
||||
protected void onBlogPostsLoadedException(DbException exception) {
|
||||
// TODO: Decide how to handle errors in the UI
|
||||
finish();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MessageId getSelectedPost() {
|
||||
if (postPagerAdapter.getCount() == 0) return null;
|
||||
int position = pager.getCurrentItem();
|
||||
return postPagerAdapter.getPost(position).getId();
|
||||
}
|
||||
|
||||
private void selectPost(MessageId m) {
|
||||
int pos = postPagerAdapter.getPostPosition(m);
|
||||
if (pos != INVALID_POSITION) {
|
||||
progressBar.setVisibility(INVISIBLE);
|
||||
pager.setAdapter(postPagerAdapter);
|
||||
pager.setCurrentItem(pos);
|
||||
}
|
||||
}
|
||||
|
||||
protected void addPost(BlogPostItem post) {
|
||||
MessageId selected = getSelectedPost();
|
||||
postPagerAdapter.addPost(post);
|
||||
if (selected != null) selectPost(selected);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
static class BlogPostPagerAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
static final int INVALID_POSITION = -1;
|
||||
private final List<BlogPostItem> posts = new ArrayList<>();
|
||||
|
||||
private BlogPostPagerAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return posts.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
BlogPostItem post = posts.get(position);
|
||||
return FeedPostFragment.newInstance(post.getGroupId(), post.getId());
|
||||
}
|
||||
|
||||
private BlogPostItem getPost(int position) {
|
||||
return posts.get(position);
|
||||
}
|
||||
|
||||
private void setPosts(Collection<BlogPostItem> posts) {
|
||||
this.posts.clear();
|
||||
this.posts.addAll(posts);
|
||||
Collections.sort(this.posts);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void addPost(BlogPostItem post) {
|
||||
posts.add(post);
|
||||
Collections.sort(posts);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private int getPostPosition(MessageId m) {
|
||||
int count = getCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (getPost(i).getId().equals(m)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return INVALID_POSITION;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,29 +2,56 @@ package org.briarproject.android.blogs;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.ActivityComponent;
|
||||
import org.briarproject.android.BriarActivity;
|
||||
import org.briarproject.android.blogs.BlogController.BlogPostListener;
|
||||
import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
|
||||
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
||||
import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
|
||||
import org.briarproject.api.blogs.BlogPostHeader;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
public class BlogActivity extends BriarActivity implements
|
||||
public class BlogActivity extends BriarActivity implements BlogPostListener,
|
||||
OnBlogPostClickListener, BaseFragmentListener {
|
||||
|
||||
static final int REQUEST_WRITE_POST = 1;
|
||||
static final int REQUEST_SHARE = 2;
|
||||
static final String BLOG_NAME = "briar.BLOG_NAME";
|
||||
static final String IS_MY_BLOG = "briar.IS_MY_BLOG";
|
||||
static final String IS_NEW_BLOG = "briar.IS_NEW_BLOG";
|
||||
|
||||
private static final String POST_ID = "briar.POST_ID";
|
||||
|
||||
private GroupId groupId;
|
||||
private ProgressBar progressBar;
|
||||
private ViewPager pager;
|
||||
private BlogPagerAdapter blogPagerAdapter;
|
||||
private BlogPostPagerAdapter postPagerAdapter;
|
||||
private String blogName;
|
||||
private boolean myBlog, isNew;
|
||||
private MessageId savedPostId;
|
||||
|
||||
@Inject
|
||||
BlogController blogController;
|
||||
@@ -37,24 +64,63 @@ public class BlogActivity extends BriarActivity implements
|
||||
Intent i = getIntent();
|
||||
byte[] b = i.getByteArrayExtra(GROUP_ID);
|
||||
if (b == null) throw new IllegalStateException("No group ID in intent");
|
||||
GroupId groupId = new GroupId(b);
|
||||
groupId = new GroupId(b);
|
||||
blogController.setGroupId(groupId);
|
||||
|
||||
// Name of the blog
|
||||
String blogName = i.getStringExtra(BLOG_NAME);
|
||||
// Name of the Blog from Intent
|
||||
blogName = i.getStringExtra(BLOG_NAME);
|
||||
if (blogName != null) setTitle(blogName);
|
||||
|
||||
// Was this blog just created?
|
||||
boolean isNew = i.getBooleanExtra(IS_NEW_BLOG, false);
|
||||
// Is this our blog and was it just created?
|
||||
myBlog = i.getBooleanExtra(IS_MY_BLOG, false);
|
||||
isNew = i.getBooleanExtra(IS_NEW_BLOG, false);
|
||||
|
||||
setContentView(R.layout.activity_fragment_container);
|
||||
setContentView(R.layout.activity_blog);
|
||||
|
||||
pager = (ViewPager) findViewById(R.id.pager);
|
||||
progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||
|
||||
if (state == null) {
|
||||
BlogFragment f = BlogFragment.newInstance(groupId, blogName, isNew);
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.fragmentContainer, f, f.getUniqueTag())
|
||||
.commit();
|
||||
blogPagerAdapter = new BlogPagerAdapter(getSupportFragmentManager());
|
||||
postPagerAdapter = new BlogPostPagerAdapter(
|
||||
getSupportFragmentManager());
|
||||
|
||||
if (state == null || state.getByteArray(POST_ID) == null) {
|
||||
// The blog fragment has its own progress bar
|
||||
hideLoadingScreen();
|
||||
pager.setAdapter(blogPagerAdapter);
|
||||
savedPostId = null;
|
||||
} else {
|
||||
// Adapter will be set in selectPostInPostPager()
|
||||
savedPostId = new MessageId(state.getByteArray(POST_ID));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (savedPostId == null) {
|
||||
MessageId selected = getSelectedPostInPostPager();
|
||||
if (selected != null) loadBlogPosts(selected);
|
||||
} else {
|
||||
loadBlogPosts(savedPostId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
MessageId selected = getSelectedPostInPostPager();
|
||||
if (selected != null)
|
||||
outState.putByteArray(POST_ID, selected.getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (pager.getAdapter() == postPagerAdapter) {
|
||||
pager.setAdapter(blogPagerAdapter);
|
||||
savedPostId = null;
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,15 +129,6 @@ public class BlogActivity extends BriarActivity implements
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlogPostClick(BlogPostItem post) {
|
||||
BlogPostPagerFragment f = BlogPostPagerFragment.newInstance(post.getId());
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.fragmentContainer, f, f.getUniqueTag())
|
||||
.addToBackStack(f.getUniqueTag())
|
||||
.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLoadingScreen(boolean isBlocking, int stringId) {
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
@@ -79,10 +136,176 @@ public class BlogActivity extends BriarActivity implements
|
||||
|
||||
@Override
|
||||
public void hideLoadingScreen() {
|
||||
progressBar.setVisibility(INVISIBLE);
|
||||
progressBar.setVisibility(GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFragmentCreated(String tag) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlogPostClick(BlogPostItem post) {
|
||||
loadBlogPosts(post.getId());
|
||||
}
|
||||
|
||||
private void loadBlogPosts(final MessageId select) {
|
||||
blogController.loadBlogPosts(
|
||||
new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>(
|
||||
this) {
|
||||
@Override
|
||||
public void onResultUi(Collection<BlogPostItem> posts) {
|
||||
hideLoadingScreen();
|
||||
savedPostId = null;
|
||||
postPagerAdapter.setPosts(posts);
|
||||
selectPostInPostPager(select);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExceptionUi(DbException exception) {
|
||||
// TODO: Decide how to handle errors in the UI
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlogPostAdded(BlogPostHeader header, boolean local) {
|
||||
if (pager.getAdapter() == postPagerAdapter) {
|
||||
loadBlogPost(header);
|
||||
} else {
|
||||
BlogFragment f = blogPagerAdapter.getFragment();
|
||||
if (f != null && f.isVisible()) f.onBlogPostAdded(header, local);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadBlogPost(BlogPostHeader header) {
|
||||
blogController.loadBlogPost(header,
|
||||
new UiResultExceptionHandler<BlogPostItem, DbException>(this) {
|
||||
@Override
|
||||
public void onResultUi(BlogPostItem post) {
|
||||
addPostToPostPager(post);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExceptionUi(DbException exception) {
|
||||
// TODO: Decide how to handle errors in the UI
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MessageId getSelectedPostInPostPager() {
|
||||
if (pager.getAdapter() != postPagerAdapter) return null;
|
||||
if (postPagerAdapter.getCount() == 0) return null;
|
||||
int position = pager.getCurrentItem();
|
||||
return postPagerAdapter.getPost(position).getId();
|
||||
}
|
||||
|
||||
private void selectPostInPostPager(MessageId m) {
|
||||
int count = postPagerAdapter.getCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (postPagerAdapter.getPost(i).getId().equals(m)) {
|
||||
pager.setAdapter(postPagerAdapter);
|
||||
pager.setCurrentItem(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addPostToPostPager(BlogPostItem post) {
|
||||
MessageId selected = getSelectedPostInPostPager();
|
||||
postPagerAdapter.addPost(post);
|
||||
if (selected != null) selectPostInPostPager(selected);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode,
|
||||
Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
// The BlogPostAddedEvent arrives when the controller is not listening,
|
||||
// so we need to manually reload the blog posts :(
|
||||
if (requestCode == REQUEST_WRITE_POST && resultCode == RESULT_OK) {
|
||||
if (pager.getAdapter() == postPagerAdapter) {
|
||||
MessageId selected = getSelectedPostInPostPager();
|
||||
if (selected != null) loadBlogPosts(selected);
|
||||
} else {
|
||||
BlogFragment f = blogPagerAdapter.getFragment();
|
||||
if (f != null && f.isVisible()) f.loadBlogPosts(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private class BlogPagerAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
private BlogFragment fragment = null;
|
||||
|
||||
private BlogPagerAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return BlogFragment.newInstance(groupId, blogName, myBlog, isNew);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, int position) {
|
||||
// save a reference to the single fragment here for later
|
||||
fragment =
|
||||
(BlogFragment) super.instantiateItem(container, position);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
private BlogFragment getFragment() {
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private static class BlogPostPagerAdapter
|
||||
extends FragmentStatePagerAdapter {
|
||||
|
||||
private final List<BlogPostItem> posts = new ArrayList<>();
|
||||
|
||||
private BlogPostPagerAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return posts.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return BlogPostFragment.newInstance(posts.get(position).getId());
|
||||
}
|
||||
|
||||
private BlogPostItem getPost(int position) {
|
||||
return posts.get(position);
|
||||
}
|
||||
|
||||
private void setPosts(Collection<BlogPostItem> posts) {
|
||||
this.posts.clear();
|
||||
this.posts.addAll(posts);
|
||||
Collections.sort(this.posts);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void addPost(BlogPostItem post) {
|
||||
posts.add(post);
|
||||
Collections.sort(posts);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
package org.briarproject.android.blogs;
|
||||
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
import org.briarproject.api.blogs.BlogCommentHeader;
|
||||
import org.briarproject.api.blogs.BlogPostHeader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@UiThread
|
||||
class BlogCommentItem extends BlogPostItem {
|
||||
|
||||
private static final BlogCommentComparator COMPARATOR =
|
||||
new BlogCommentComparator();
|
||||
|
||||
private final BlogPostHeader postHeader;
|
||||
private final List<BlogCommentHeader> comments = new ArrayList<>();
|
||||
|
||||
BlogCommentItem(BlogCommentHeader header) {
|
||||
super(header, null);
|
||||
postHeader = collectComments(header);
|
||||
Collections.sort(comments, COMPARATOR);
|
||||
}
|
||||
|
||||
private BlogPostHeader collectComments(BlogPostHeader header) {
|
||||
if (header instanceof BlogCommentHeader) {
|
||||
BlogCommentHeader comment = (BlogCommentHeader) header;
|
||||
if (comment.getComment() != null)
|
||||
comments.add(comment);
|
||||
return collectComments(comment.getParent());
|
||||
} else {
|
||||
return header;
|
||||
}
|
||||
}
|
||||
|
||||
public void setBody(String body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlogCommentHeader getHeader() {
|
||||
return (BlogCommentHeader) super.getHeader();
|
||||
}
|
||||
|
||||
@Override
|
||||
BlogPostHeader getPostHeader() {
|
||||
return postHeader;
|
||||
}
|
||||
|
||||
List<BlogCommentHeader> getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
private static class BlogCommentComparator
|
||||
implements Comparator<BlogCommentHeader> {
|
||||
@Override
|
||||
public int compare(BlogCommentHeader h1, BlogCommentHeader h2) {
|
||||
// re-use same comparator used for blog posts, but reverse it
|
||||
return BlogPostItem.compare(h2, h1);
|
||||
}
|
||||
}
|
||||
}
|
||||