From 9bfb58a764e70ae592a278540f5a3b0bc3b47d9a Mon Sep 17 00:00:00 2001
From: Torsten Grote <t@grobox.de>
Date: Tue, 11 Apr 2017 11:31:14 -0300
Subject: [PATCH] Show blog posts from RSS feeds with a dedicated icon

This adds a field to the post headers and some more tests.
---
 .../briar/android/blog/BlogPostItem.java      |  4 ++
 .../android/blog/BlogPostViewHolder.java      |  7 +-
 .../briar/android/view/AuthorView.java        | 26 +++++++
 .../src/main/res/drawable/ic_rss_feed.xml     | 25 +++++++
 briar-android/src/main/res/values/attrs.xml   |  2 +
 .../briar/api/blog/BlogCommentHeader.java     |  5 +-
 .../briar/api/blog/BlogConstants.java         |  1 +
 .../briar/api/blog/BlogPostHeader.java        | 13 +++-
 .../briar/blog/BlogManagerImpl.java           | 14 +++-
 .../briar/blog/BlogPostValidator.java         |  2 +
 .../blog/BlogManagerIntegrationTest.java      | 67 ++++++++++++++++++-
 .../feed/FeedManagerIntegrationTest.java      |  8 +++
 12 files changed, 164 insertions(+), 10 deletions(-)
 create mode 100644 briar-android/src/main/res/drawable/ic_rss_feed.xml

diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java
index d8561ad4d0..546f9ac180 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java
@@ -48,6 +48,10 @@ public class BlogPostItem implements Comparable<BlogPostItem> {
 		return body;
 	}
 
+	public boolean isRssFeed() {
+		return header.isRssFeed();
+	}
+
 	public boolean isRead() {
 		return read;
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java
index ba1d11e683..b0f4187b40 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java
@@ -108,7 +108,8 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
 		author.setAuthor(a);
 		author.setAuthorStatus(post.getAuthorStatus());
 		author.setDate(post.getTimestamp());
-		author.setPersona(AuthorView.NORMAL);
+		author.setPersona(
+				item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL);
 		// TODO make author clickable more often #624
 		if (item.getHeader().getType() == POST) {
 			author.setBlogLink(post.getGroupId());
@@ -168,7 +169,9 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
 		reblogger.setVisibility(VISIBLE);
 		reblogger.setPersona(AuthorView.REBLOGGER);
 
-		author.setPersona(AuthorView.COMMENTER);
+		author.setPersona(item.getHeader().getParent().isRssFeed() ?
+				AuthorView.RSS_FEED_REBLOGGED :
+				AuthorView.COMMENTER);
 
 		// comments
 		for (BlogCommentHeader c : item.getComments()) {
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java
index fc145268d6..67aa5a3b92 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java
@@ -40,6 +40,8 @@ public class AuthorView extends RelativeLayout {
 	public static final int REBLOGGER = 1;
 	public static final int COMMENTER = 2;
 	public static final int LIST = 3;
+	public static final int RSS_FEED = 4;
+	public static final int RSS_FEED_REBLOGGED = 5;
 
 	private final CircleImageView avatar;
 	private final ImageView avatarIcon;
@@ -124,6 +126,12 @@ public class AuthorView extends RelativeLayout {
 		setOnClickListener(null);
 	}
 
+	/**
+	 * Styles this view for a different persona.
+	 *
+	 * Attention: If used in a RecyclerView with RSS_FEED,
+	 *            call this after setAuthor()
+	 */
 	public void setPersona(int persona) {
 		switch (persona) {
 			case NORMAL:
@@ -158,6 +166,24 @@ public class AuthorView extends RelativeLayout {
 				setCenterVertical(authorName, true);
 				setCenterVertical(trustIndicator, true);
 				break;
+			case RSS_FEED:
+				avatarIcon.setVisibility(INVISIBLE);
+				date.setVisibility(VISIBLE);
+				avatar.setImageResource(R.drawable.ic_rss_feed);
+				setAvatarSize(R.dimen.blogs_avatar_normal_size);
+				setTextSize(authorName, R.dimen.text_size_small);
+				setCenterVertical(authorName, false);
+				setCenterVertical(trustIndicator, false);
+				break;
+			case RSS_FEED_REBLOGGED:
+				avatarIcon.setVisibility(INVISIBLE);
+				date.setVisibility(VISIBLE);
+				avatar.setImageResource(R.drawable.ic_rss_feed);
+				setAvatarSize(R.dimen.blogs_avatar_comment_size);
+				setTextSize(authorName, R.dimen.text_size_tiny);
+				setCenterVertical(authorName, false);
+				setCenterVertical(trustIndicator, false);
+				break;
 		}
 	}
 
diff --git a/briar-android/src/main/res/drawable/ic_rss_feed.xml b/briar-android/src/main/res/drawable/ic_rss_feed.xml
new file mode 100644
index 0000000000..6d441fa064
--- /dev/null
+++ b/briar-android/src/main/res/drawable/ic_rss_feed.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="30dp"
+        android:height="30dp"
+        android:viewportHeight="30"
+        android:viewportWidth="30">
+
+	<path
+		android:fillColor="#ffa500"
+		android:pathData="M0,8.88178e-16 L30,8.88178e-16 L30,30 L0,30 L0,8.88178e-16 Z"/>
+	<path
+		android:fillColor="#ffffff"
+		android:pathData="M8.9322,18.0339 C10.6078,18.0339,11.9661,19.3922,11.9661,21.0678
+C11.9661,22.7434,10.6078,24.1017,8.9322,24.1017
+C7.25663,24.1017,5.8983,22.7434,5.8983,21.0678
+C5.8983,19.3922,7.25663,18.0339,8.9322,18.0339 Z"/>
+	<path
+		android:fillColor="#ffffff"
+		android:pathData="M5.8983,15 A9.1016949,9.1016949,0,0,1,15,24.1017 L18.0339,24.1017
+A12.135593,12.135593,0,0,0,5.8983,11.9661 Z"/>
+	<path
+		android:fillColor="#ffffff"
+		android:pathData="M5.8983,8.9322 A15.169492,15.169492,0,0,1,21.0678,24.1017 L24.1017,24.1017
+A18.20339,18.20339,0,0,0,5.8983,5.8983 Z"/>
+</vector>
\ No newline at end of file
diff --git a/briar-android/src/main/res/values/attrs.xml b/briar-android/src/main/res/values/attrs.xml
index 6d6207cd0b..8655d2126f 100644
--- a/briar-android/src/main/res/values/attrs.xml
+++ b/briar-android/src/main/res/values/attrs.xml
@@ -12,6 +12,8 @@
 			<enum name="reblogger" value="1"/>
 			<enum name="commenter" value="2"/>
 			<enum name="list" value="3"/>
+			<enum name="rss_feed" value="4"/>
+			<enum name="rss_feed_reblogged" value="5"/>
 		</attr>
 	</declare-styleable>
 
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java
index 70fded2813..af28ca1a2e 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java
@@ -26,7 +26,7 @@ public class BlogCommentHeader extends BlogPostHeader {
 			Status authorStatus, boolean read) {
 
 		super(type, groupId, id, parent.getId(), timestamp,
-				timeReceived, author, authorStatus, read);
+				timeReceived, author, authorStatus, false, read);
 
 		if (type != COMMENT && type != WRAPPED_COMMENT)
 			throw new IllegalArgumentException("Incompatible Message Type");
@@ -41,6 +41,9 @@ public class BlogCommentHeader extends BlogPostHeader {
 	}
 
 	public BlogPostHeader getParent() {
+		if (parent instanceof BlogCommentHeader)
+			return ((BlogCommentHeader) parent).getParent();
 		return parent;
 	}
+
 }
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java
index 99c4514b26..4c8ff25d10 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java
@@ -28,6 +28,7 @@ public interface BlogConstants {
 	String KEY_AUTHOR_NAME = "name";
 	String KEY_PUBLIC_KEY = "publicKey";
 	String KEY_AUTHOR = "author";
+	String KEY_RSS_FEED = "rssFeed";
 	String KEY_READ = "read";
 	String KEY_COMMENT = "comment";
 	String KEY_ORIGINAL_MSG_ID = "originalMessageId";
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java
index cf01dfc571..57a0ef58a9 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java
@@ -17,21 +17,23 @@ public class BlogPostHeader extends PostHeader {
 	private final MessageType type;
 	private final GroupId groupId;
 	private final long timeReceived;
+	private final boolean rssFeed;
 
 	public BlogPostHeader(MessageType type, GroupId groupId, MessageId id,
 			@Nullable MessageId parentId, long timestamp, long timeReceived,
-			Author author, Status authorStatus, boolean read) {
+			Author author, Status authorStatus, boolean rssFeed, boolean read) {
 		super(id, parentId, timestamp, author, authorStatus, read);
 		this.type = type;
 		this.groupId = groupId;
 		this.timeReceived = timeReceived;
+		this.rssFeed = rssFeed;
 	}
 
 	public BlogPostHeader(MessageType type, GroupId groupId, MessageId id,
 			long timestamp, long timeReceived, Author author,
-			Status authorStatus, boolean read) {
+			Status authorStatus, boolean rssFeed, boolean read) {
 		this(type, groupId, id, null, timestamp, timeReceived, author,
-				authorStatus, read);
+				authorStatus, rssFeed, read);
 	}
 
 	public MessageType getType() {
@@ -45,4 +47,9 @@ public class BlogPostHeader extends PostHeader {
 	public long getTimeReceived() {
 		return timeReceived;
 	}
+
+	public boolean isRssFeed() {
+		return rssFeed;
+	}
+
 }
diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java
index 22b8009167..91945f3108 100644
--- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java
@@ -61,6 +61,7 @@ import static org.briarproject.briar.api.blog.BlogConstants.KEY_ORIGINAL_PARENT_
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_PARENT_MSG_ID;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_PUBLIC_KEY;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_READ;
+import static org.briarproject.briar.api.blog.BlogConstants.KEY_RSS_FEED;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIMESTAMP;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIME_RECEIVED;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_TYPE;
@@ -253,15 +254,18 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 	@Override
 	public void addLocalPost(Transaction txn, BlogPost p) throws DbException {
 		try {
+			GroupId groupId = p.getMessage().getGroupId();
+			Blog b = getBlog(txn, groupId);
+
 			BdfDictionary meta = new BdfDictionary();
 			meta.put(KEY_TYPE, POST.getInt());
 			meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp());
 			meta.put(KEY_AUTHOR, authorToBdfDictionary(p.getAuthor()));
 			meta.put(KEY_READ, true);
+			meta.put(KEY_RSS_FEED, b.isRssFeed());
 			clientHelper.addLocalMessage(txn, p.getMessage(), meta, true);
 
 			// broadcast event about new post
-			GroupId groupId = p.getMessage().getGroupId();
 			MessageId postId = p.getMessage().getId();
 			BlogPostHeader h =
 					getPostHeaderFromMetadata(txn, groupId, postId, meta);
@@ -350,6 +354,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 			wMessage = blogPostFactory
 					.wrapPost(groupId, wDescriptor, wTimestamp, body);
 			meta.put(KEY_TYPE, WRAPPED_POST.getInt());
+			meta.put(KEY_RSS_FEED, pOriginalHeader.isRssFeed());
 		} else if (type == COMMENT) {
 			Group wGroup = db.getGroup(txn, pOriginalHeader.getGroupId());
 			byte[] wDescriptor = wGroup.getDescriptor();
@@ -598,8 +603,11 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 		String name = d.getString(KEY_AUTHOR_NAME);
 		byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY);
 		Author author = new Author(authorId, name, publicKey);
+		boolean isFeedPost = meta.getBoolean(KEY_RSS_FEED, false);
 		Status authorStatus;
-		if (authorStatuses.containsKey(authorId)) {
+		if (isFeedPost) {
+			authorStatus = Status.UNKNOWN;
+		} else if (authorStatuses.containsKey(authorId)) {
 			authorStatus = authorStatuses.get(authorId);
 		} else {
 			authorStatus = identityManager.getAuthorStatus(txn, authorId);
@@ -616,7 +624,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 					timestamp, timeReceived, author, authorStatus, read);
 		} else {
 			return new BlogPostHeader(type, groupId, id, timestamp,
-					timeReceived, author, authorStatus, read);
+					timeReceived, author, authorStatus, isFeedPost, read);
 		}
 	}
 
diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java
index 33fb0bddf7..5e74400c52 100644
--- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java
+++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java
@@ -39,6 +39,7 @@ import static org.briarproject.briar.api.blog.BlogConstants.KEY_ORIGINAL_PARENT_
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_PARENT_MSG_ID;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_PUBLIC_KEY;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_READ;
+import static org.briarproject.briar.api.blog.BlogConstants.KEY_RSS_FEED;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIMESTAMP;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIME_RECEIVED;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_TYPE;
@@ -123,6 +124,7 @@ class BlogPostValidator extends BdfMessageValidator {
 		BdfDictionary meta = new BdfDictionary();
 		meta.put(KEY_ORIGINAL_MSG_ID, m.getId());
 		meta.put(KEY_AUTHOR, authorToBdfDictionary(a));
+		meta.put(KEY_RSS_FEED, b.isRssFeed());
 		return new BdfMessageContext(meta);
 	}
 
diff --git a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java
index 643fe482c3..c2c00ea571 100644
--- a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java
@@ -1,5 +1,6 @@
 package org.briarproject.briar.blog;
 
+import org.briarproject.bramble.api.db.Transaction;
 import org.briarproject.bramble.api.sync.MessageId;
 import org.briarproject.bramble.test.TestDatabaseModule;
 import org.briarproject.briar.api.blog.Blog;
@@ -32,7 +33,7 @@ public class BlogManagerIntegrationTest
 		extends BriarIntegrationTest<BriarIntegrationTestComponent> {
 
 	private BlogManager blogManager0, blogManager1;
-	private Blog blog0, blog1;
+	private Blog blog0, blog1, rssBlog;
 
 	@Rule
 	public ExpectedException thrown = ExpectedException.none();
@@ -50,6 +51,12 @@ public class BlogManagerIntegrationTest
 
 		blog0 = blogFactory.createBlog(author0);
 		blog1 = blogFactory.createBlog(author1);
+
+		rssBlog = blogFactory.createFeedBlog(author0);
+		Transaction txn = db0.startTransaction(false);
+		blogManager0.addBlog(txn, rssBlog);
+		db0.commitTransaction(txn);
+		db0.endTransaction(txn);
 	}
 
 	@Override
@@ -393,4 +400,62 @@ public class BlogManagerIntegrationTest
 		assertEquals(2, headers0.size());
 	}
 
+	@Test
+	public void testFeedPost() throws Exception {
+		assertTrue(rssBlog.isRssFeed());
+
+		// add a feed post to rssBlog
+		final String body = getRandomString(42);
+		BlogPost p = blogPostFactory
+				.createBlogPost(rssBlog.getId(), clock.currentTimeMillis(),
+						null, author0, body);
+		blogManager0.addLocalPost(p);
+
+		// make sure it got saved as an RSS feed post
+		Collection<BlogPostHeader> headers =
+				blogManager0.getPostHeaders(rssBlog.getId());
+		assertEquals(1, headers.size());
+		BlogPostHeader header = headers.iterator().next();
+		assertEquals(POST, header.getType());
+		assertTrue(header.isRssFeed());
+	}
+
+	@Test
+	public void testFeedReblog() throws Exception {
+		// add a feed post to rssBlog
+		final String body = getRandomString(42);
+		BlogPost p = blogPostFactory
+				.createBlogPost(rssBlog.getId(), clock.currentTimeMillis(),
+						null, author0, body);
+		blogManager0.addLocalPost(p);
+
+		// reblog feed post to own blog
+		Collection<BlogPostHeader> headers =
+				blogManager0.getPostHeaders(rssBlog.getId());
+		assertEquals(1, headers.size());
+		BlogPostHeader header = headers.iterator().next();
+		blogManager0.addLocalComment(author0, blog0.getId(), null, header);
+
+		// make sure it got saved as an RSS feed post
+		headers = blogManager0.getPostHeaders(blog0.getId());
+		assertEquals(1, headers.size());
+		BlogCommentHeader commentHeader =
+				(BlogCommentHeader) headers.iterator().next();
+		assertEquals(COMMENT, commentHeader.getType());
+		assertTrue(commentHeader.getParent().isRssFeed());
+
+		// reblog reblogged post again to own blog
+		blogManager0
+				.addLocalComment(author0, blog0.getId(), null, commentHeader);
+
+		// make sure it got saved as an RSS feed post
+		headers = blogManager0.getPostHeaders(blog0.getId());
+		assertEquals(2, headers.size());
+		for (BlogPostHeader h: headers) {
+			assertTrue(h instanceof BlogCommentHeader);
+			assertEquals(COMMENT, h.getType());
+			assertTrue(((BlogCommentHeader) h).getParent().isRssFeed());
+		}
+	}
+
 }
diff --git a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java
index 25d4992c41..2fc24aae58 100644
--- a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java
@@ -12,6 +12,7 @@ import org.briarproject.bramble.test.TestUtils;
 import org.briarproject.bramble.transport.TransportModule;
 import org.briarproject.briar.api.blog.Blog;
 import org.briarproject.briar.api.blog.BlogManager;
+import org.briarproject.briar.api.blog.BlogPostHeader;
 import org.briarproject.briar.api.feed.Feed;
 import org.briarproject.briar.api.feed.FeedManager;
 import org.briarproject.briar.blog.BlogModule;
@@ -88,6 +89,13 @@ public class FeedManagerIntegrationTest extends BriarTestCase {
 		assertEquals(feed.getTitle(), feed.getBlog().getName());
 		assertEquals(feed.getTitle(), feed.getLocalAuthor().getName());
 
+		// check the feed entries have been added to the blog as expected
+		Collection<BlogPostHeader> headers =
+				blogManager.getPostHeaders(feedBlog.getId());
+		for (BlogPostHeader header : headers) {
+			assertTrue(header.isRssFeed());
+		}
+
 		// now let's remove the feed's blog again
 		blogManager.removeBlog(feedBlog);
 		blogs = blogManager.getBlogs();
-- 
GitLab