Combined Blog Feed

This commit addes a combined blog feed that shows all posts of all
subscribed blogs in the order the blog posts have been received.

For now, this commit also hides other blog functionality like adding
additional blogs and browsing individual blogs.

Closes #417
This commit is contained in:
Torsten Grote
2016-06-22 16:47:01 -03:00
parent e0d2d09bdd
commit fd7278b488
22 changed files with 568 additions and 71 deletions

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:alpha="0.54"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
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>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:alpha="0.54"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M7,7h10v3l4,-4 -4,-4v3L5,5v6h2L7,7zM17,17L7,17v-3l-4,4 4,4v-3h12v-6h-2v4z"/>
</vector>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="15dp"
android:width="31dp"
android:height="12dp"
android:viewportHeight="20"
android:viewportWidth="49">
<path

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="15dp"
android:width="31dp"
android:height="12dp"
android:viewportHeight="20"
android:viewportWidth="49">
<path

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="15dp"
android:width="31dp"
android:height="12dp"
android:viewportHeight="20"
android:viewportWidth="49">
<path

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="15dp"
android:width="31dp"
android:height="12dp"
android:viewportHeight="20"
android:viewportWidth="49">
<path

View File

@@ -6,14 +6,86 @@
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
android:layout_marginTop="@dimen/margin_medium"
android:layout_marginTop="@dimen/listitem_vertical_margin"
android:background="?attr/selectableItemBackground">
<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:layout_alignParentTop="true"
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"
@@ -35,34 +107,12 @@
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."/>
<TextView
android:id="@+id/newView"
style="@style/BriarTag"
android:layout_alignBottom="@id/dateView"
android:layout_alignParentLeft="true"
android:text="@string/tag_new"
tools:visibility="visible"/>
<TextView
android:id="@+id/dateView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_below="@id/bodyView"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_marginTop="@dimen/margin_small"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_tiny"
tools:text="Dec 24"/>
<View
style="@style/Divider.ForumList"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/dateView"
android:layout_marginTop="@dimen/margin_medium"/>
android:layout_below="@+id/bodyView"
android:layout_marginTop="@dimen/listitem_vertical_margin"/>
</RelativeLayout>

View 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="always"/>
</menu>

View File

@@ -20,6 +20,7 @@
<dimen name="text_size_xlarge">34sp</dimen>
<dimen name="listitem_horizontal_margin">16dp</dimen>
<dimen name="listitem_vertical_margin">10dp</dimen>
<dimen name="listitem_text_left_margin">72dp</dimen>
<dimen name="listitem_height_one_line_avatar">56dp</dimen>
<dimen name="listitem_height_contact_selector">68dp</dimen>

View File

@@ -247,7 +247,7 @@
<string name="progress_title_please_wait">Please wait..</string>
<!-- Blogs -->
<string name="blogs_button">Blogs</string>
<string name="blogs_button">Micro Blogs</string>
<string name="blogs_feed">Feed</string>
<string name="blogs_my_blogs">My Blogs</string>
@@ -267,8 +267,12 @@
<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>
<string name="blogs_publish_blog_post">Publish</string>
<string name="blogs_blog_post_created">Blog Post Created</string>
<string name="blogs_blog_post_received">New Blog Post Received</string>
<string name="blogs_blog_post_scroll_to">Scroll To</string>
<string name="blogs_blog_failed_to_load">Blog failed to load</string>
<string name="blogs_blog_post_failed_to_load">Blog Post failed to load</string>
<string name="blogs_feed_empty_state">This is the global blog feed.\n\nIt looks like nobody blogged anything, yet.\n\nBe the first and tap the pen icon to write a new blog post.</string>
<string name="blogs_delete_blog">Delete Blog</string>
<string name="blogs_delete_blog_dialog_message">Are you sure that you want to delete this Blog and all posts?\nNote that this will not delete the blog from other people\'s devices.</string>
<string name="blogs_delete_blog_ok">Delete Blog</string>

View File

@@ -4,11 +4,14 @@ import android.app.Activity;
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.BlogsFragment;
import org.briarproject.android.blogs.CreateBlogActivity;
import org.briarproject.android.blogs.FeedFragment;
import org.briarproject.android.blogs.MyBlogsFragment;
import org.briarproject.android.contact.ContactListFragment;
import org.briarproject.android.blogs.WriteBlogPostActivity;
import org.briarproject.android.contact.ContactListFragment;
import org.briarproject.android.contact.ConversationActivity;
import org.briarproject.android.forum.AvailableForumsActivity;
import org.briarproject.android.forum.ContactSelectorFragment;
@@ -18,7 +21,6 @@ import org.briarproject.android.forum.ForumListFragment;
import org.briarproject.android.forum.ForumSharingStatusActivity;
import org.briarproject.android.forum.ShareForumActivity;
import org.briarproject.android.forum.ShareForumMessageFragment;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.identity.CreateIdentityActivity;
import org.briarproject.android.introduction.ContactChooserFragment;
import org.briarproject.android.introduction.IntroductionActivity;
@@ -88,7 +90,9 @@ public interface ActivityComponent {
// Fragments
void inject(ContactListFragment fragment);
void inject(ForumListFragment fragment);
void inject(BaseFragment fragment);
void inject(BlogsFragment fragment);
void inject(BlogListFragment fragment);
void inject(FeedFragment fragment);
void inject(MyBlogsFragment fragment);
void inject(ChooseIdentityFragment fragment);
void inject(ShowQrCodeFragment fragment);

View File

@@ -6,6 +6,8 @@ import android.content.SharedPreferences;
import org.briarproject.android.blogs.BlogController;
import org.briarproject.android.blogs.BlogControllerImpl;
import org.briarproject.android.blogs.FeedController;
import org.briarproject.android.blogs.FeedControllerImpl;
import org.briarproject.android.controller.BriarController;
import org.briarproject.android.controller.BriarControllerImpl;
import org.briarproject.android.controller.ConfigController;
@@ -116,6 +118,13 @@ public class ActivityModule {
return blogController;
}
@ActivityScope
@Provides
protected FeedController provideFeedController(
FeedControllerImpl feedController) {
return feedController;
}
@ActivityScope
@Provides
protected NavDrawerController provideNavDrawerController(

View File

@@ -7,15 +7,18 @@ import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.briarproject.R;
import org.briarproject.android.util.TrustIndicatorView;
import org.briarproject.api.identity.Author;
import org.briarproject.util.StringUtils;
import java.util.Collection;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import de.hdodenhof.circleimageview.CircleImageView;
import im.delight.android.identicons.IdenticonDrawable;
class BlogPostAdapter extends
RecyclerView.Adapter<BlogPostAdapter.BlogPostHolder> {
@@ -76,18 +79,20 @@ class BlogPostAdapter extends
@Override
public void onBindViewHolder(final BlogPostHolder ui, int position) {
final BlogPostItem item = getItem(position);
final BlogPostItem post = getItem(position);
// title
if (item.getTitle() != null) {
ui.title.setText(item.getTitle());
ui.title.setVisibility(VISIBLE);
} else {
ui.title.setVisibility(GONE);
}
Author author = post.getAuthor();
IdenticonDrawable d = new IdenticonDrawable(author.getId().getBytes());
ui.avatar.setImageDrawable(d);
ui.author.setText(author.getName());
ui.trust.setTrustLevel(post.getAuthorStatus());
// date
ui.date.setText(
DateUtils.getRelativeTimeSpanString(ctx, post.getTimestamp()));
// post body
ui.body.setText(StringUtils.fromUtf8(item.getBody()));
ui.body.setText(StringUtils.fromUtf8(post.getBody()));
ui.layout.setOnClickListener(new View.OnClickListener() {
@Override
@@ -95,14 +100,6 @@ class BlogPostAdapter extends
listener.onBlogPostClick(ui.getAdapterPosition());
}
});
// date
ui.date.setText(
DateUtils.getRelativeTimeSpanString(ctx, item.getTimestamp()));
// new tag
if (item.isRead()) ui.unread.setVisibility(GONE);
else ui.unread.setVisibility(VISIBLE);
}
@Override
@@ -136,18 +133,28 @@ class BlogPostAdapter extends
static class BlogPostHolder extends RecyclerView.ViewHolder {
private final ViewGroup layout;
private final TextView title;
private final TextView unread;
private final CircleImageView avatar;
private final TextView author;
private final TrustIndicatorView trust;
private final TextView date;
private final TextView unread;
private final ImageView chat;
private final ImageView comment;
private final TextView title;
private final TextView body;
BlogPostHolder(View v) {
super(v);
layout = (ViewGroup) v;
title = (TextView) v.findViewById(R.id.titleView);
unread = (TextView) v.findViewById(R.id.newView);
avatar = (CircleImageView) v.findViewById(R.id.avatar);
author = (TextView) v.findViewById(R.id.authorName);
trust = (TrustIndicatorView) v.findViewById(R.id.trustIndicator);
date = (TextView) v.findViewById(R.id.dateView);
unread = (TextView) v.findViewById(R.id.newView);
chat = (ImageView) v.findViewById(R.id.chatView);
comment = (ImageView) v.findViewById(R.id.commentView);
title = (TextView) v.findViewById(R.id.titleView);
body = (TextView) v.findViewById(R.id.bodyView);
}
}

View File

@@ -36,6 +36,10 @@ class BlogPostItem implements Comparable<BlogPostItem> {
return header.getTimestamp();
}
public long getTimeReceived() {
return header.getTimeReceived();
}
public Author getAuthor() {
return header.getAuthor();
}
@@ -56,7 +60,7 @@ class BlogPostItem implements Comparable<BlogPostItem> {
public int compareTo(@NonNull BlogPostItem other) {
if (this == other) return 0;
// The blog with the newest message comes first
long aTime = getTimestamp(), bTime = other.getTimestamp();
long aTime = getTimeReceived(), bTime = other.getTimeReceived();
if (aTime > bTime) return -1;
if (aTime < bTime) return 1;
// Break ties by post title

View File

@@ -15,6 +15,8 @@ import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.fragment.BaseFragment;
import static android.view.View.GONE;
public class BlogsFragment extends BaseFragment {
public final static String TAG = BlogsFragment.class.getName();
@@ -54,6 +56,8 @@ public class BlogsFragment extends BaseFragment {
viewPager.setAdapter(tabAdapter);
tabLayout.setupWithViewPager(viewPager);
tabLayout.setVisibility(GONE);
if (savedInstanceState != null) {
int position = savedInstanceState.getInt(SELECTED_TAB, 0);
viewPager.setCurrentItem(position);
@@ -88,17 +92,21 @@ public class BlogsFragment extends BaseFragment {
@Override
public int getCount() {
return titles.length;
return 1;
// return titles.length;
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 1:
return new MyBlogsFragment();
default:
return BlogListFragment.newInstance(position);
}
return FeedFragment.newInstance();
// switch (position) {
// case 0:
// return FeedFragment.newInstance();
// case 1:
// return new MyBlogsFragment();
// default:
// return BlogListFragment.newInstance(position);
// }
}
@Override

View File

@@ -0,0 +1,25 @@
package org.briarproject.android.blogs;
import org.briarproject.android.controller.ActivityLifecycleController;
import org.briarproject.android.controller.handler.UiResultHandler;
import org.briarproject.api.blogs.Blog;
import java.util.Collection;
public interface FeedController {
void onResume();
void onPause();
void loadPosts(
final UiResultHandler<Collection<BlogPostItem>> resultHandler);
void loadPersonalBlog(final UiResultHandler<Blog> resultHandler);
void setOnBlogPostAddedListener(OnBlogPostAddedListener listener);
interface OnBlogPostAddedListener {
void onBlogPostAdded(final BlogPostItem post);
}
}

View File

@@ -0,0 +1,133 @@
package org.briarproject.android.blogs;
import org.briarproject.android.controller.DbControllerImpl;
import org.briarproject.android.controller.handler.UiResultHandler;
import org.briarproject.api.blogs.Blog;
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.Author;
import org.briarproject.api.identity.IdentityManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
public class FeedControllerImpl extends DbControllerImpl
implements FeedController, EventListener {
private static final Logger LOG =
Logger.getLogger(FeedControllerImpl.class.getName());
@Inject
protected volatile BlogManager blogManager;
@Inject
protected volatile IdentityManager identityManager;
@Inject
protected volatile EventBus eventBus;
private volatile OnBlogPostAddedListener listener;
@Inject
FeedControllerImpl() {
}
public void onResume() {
eventBus.addListener(this);
}
public void onPause() {
eventBus.removeListener(this);
}
@Override
public void eventOccurred(Event e) {
if (!(e instanceof BlogPostAddedEvent)) return;
LOG.info("New blog post added");
if (listener != null) {
final BlogPostAddedEvent m = (BlogPostAddedEvent) e;
final BlogPostHeader header = m.getHeader();
try {
final byte[] body = blogManager.getPostBody(header.getId());
final BlogPostItem post = new BlogPostItem(header, body);
listener.onBlogPostAdded(post);
} catch (DbException ex) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, ex.toString(), ex);
}
}
}
@Override
public void loadPosts(
final UiResultHandler<Collection<BlogPostItem>> resultHandler) {
LOG.info("Loading blog posts...");
runOnDbThread(new Runnable() {
@Override
public void run() {
Collection<BlogPostItem> posts = new ArrayList<>();
try {
// load blog posts
long now = System.currentTimeMillis();
for (Blog b : blogManager.getBlogs()) {
Collection<BlogPostHeader> header =
blogManager.getPostHeaders(b.getId());
for (BlogPostHeader h : header) {
byte[] body = blogManager.getPostBody(h.getId());
posts.add(new BlogPostItem(h, body));
}
}
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Loading posts took " + duration + " ms");
resultHandler.onResult(posts);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
resultHandler.onResult(null);
}
}
});
}
@Override
public void loadPersonalBlog(final UiResultHandler<Blog> resultHandler) {
LOG.info("Loading personal blog...");
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
// load blog posts
long now = System.currentTimeMillis();
Author a =
identityManager.getLocalAuthors().iterator().next();
Blog b = blogManager.getPersonalBlog(a);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Loading pers. blog took " + duration + " ms");
resultHandler.onResult(b);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
resultHandler.onResult(null);
}
}
});
}
@Override
public void setOnBlogPostAddedListener(OnBlogPostAddedListener listener) {
this.listener = listener;
}
}

View File

@@ -0,0 +1,202 @@
package org.briarproject.android.blogs;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
import org.briarproject.android.controller.handler.UiResultHandler;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.util.BriarRecyclerView;
import org.briarproject.api.blogs.Blog;
import java.util.Collection;
import javax.inject.Inject;
import static android.app.Activity.RESULT_OK;
import static android.support.design.widget.Snackbar.LENGTH_LONG;
import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
import static org.briarproject.android.BriarActivity.GROUP_ID;
import static org.briarproject.android.blogs.BlogActivity.BLOG_NAME;
import static org.briarproject.android.blogs.BlogActivity.REQUEST_WRITE_POST;
public class FeedFragment extends BaseFragment implements
OnBlogPostClickListener, FeedController.OnBlogPostAddedListener {
public final static String TAG = FeedFragment.class.getName();
@Inject
FeedController feedController;
private BlogPostAdapter adapter;
private BriarRecyclerView list;
private Blog personalBlog = null;
static FeedFragment newInstance() {
FeedFragment f = new FeedFragment();
Bundle args = new Bundle();
f.setArguments(args);
return f;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setHasOptionsMenu(true);
View v = inflater.inflate(R.layout.fragment_blog, container, false);
adapter = new BlogPostAdapter(getActivity(), this);
list = (BriarRecyclerView) v.findViewById(R.id.postList);
list.setLayoutManager(new LinearLayoutManager(getActivity()));
list.setAdapter(adapter);
list.setEmptyText(R.string.blogs_feed_empty_state);
return v;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
feedController.setOnBlogPostAddedListener(this);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// The BlogPostAddedEvent arrives when the controller is not listening
if (requestCode == REQUEST_WRITE_POST && resultCode == RESULT_OK) {
// show snackbar informing about successful post creation
Snackbar s = Snackbar.make(list, R.string.blogs_blog_post_created,
LENGTH_LONG);
s.getView().setBackgroundResource(R.color.briar_primary);
OnClickListener onClick = new OnClickListener() {
@Override
public void onClick(View v) {
list.smoothScrollToPosition(0);
}
};
s.setActionTextColor(ContextCompat
.getColor(getContext(), R.color.briar_button_positive));
s.setAction(R.string.blogs_blog_post_scroll_to, onClick);
s.show();
}
}
@Override
public void onStart() {
super.onStart();
feedController
.loadPersonalBlog(new UiResultHandler<Blog>(getActivity()) {
@Override
public void onResultUi(Blog b) {
personalBlog = b;
}
});
}
@Override
public void onResume() {
super.onResume();
feedController.onResume();
feedController.loadPosts(
new UiResultHandler<Collection<BlogPostItem>>(getActivity()) {
@Override
public void onResultUi(Collection<BlogPostItem> posts) {
if (posts == null) {
// TODO show error?
} else if (posts.isEmpty()) {
list.showData();
} else {
adapter.addAll(posts);
}
}
});
}
@Override
public void onPause() {
super.onPause();
feedController.onPause();
// TODO save list position
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.blogs_feed_actions, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.action_write_blog_post:
if (personalBlog == null) return false;
Intent i =
new Intent(getActivity(), WriteBlogPostActivity.class);
i.putExtra(GROUP_ID, personalBlog.getId().getBytes());
i.putExtra(BLOG_NAME, personalBlog.getName());
ActivityOptionsCompat options =
makeCustomAnimation(getActivity(),
android.R.anim.slide_in_left,
android.R.anim.slide_out_right);
startActivityForResult(i, REQUEST_WRITE_POST,
options.toBundle());
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onBlogPostAdded(final BlogPostItem post) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.add(post);
Snackbar s =
Snackbar.make(list, R.string.blogs_blog_post_received,
LENGTH_LONG);
s.getView().setBackgroundResource(R.color.briar_primary);
OnClickListener onClick = new OnClickListener() {
@Override
public void onClick(View v) {
list.smoothScrollToPosition(0);
}
};
s.setActionTextColor(ContextCompat
.getColor(getContext(), R.color.briar_button_positive));
s.setAction(R.string.blogs_blog_post_scroll_to, onClick);
s.show();
}
});
}
@Override
public void onBlogPostClick(int position) {
// noop
}
@Override
public String getUniqueTag() {
return TAG;
}
}

View File

@@ -112,6 +112,11 @@ public class BriarRecyclerView extends FrameLayout {
emptyView.setText(text);
}
public void setEmptyText(int res) {
if (recyclerView == null) initViews();
emptyView.setText(res);
}
public void showProgressBar() {
if (recyclerView == null) initViews();
recyclerView.setVisibility(INVISIBLE);
@@ -140,6 +145,11 @@ public class BriarRecyclerView extends FrameLayout {
recyclerView.scrollToPosition(position);
}
public void smoothScrollToPosition(int position) {
if (recyclerView == null) initViews();
recyclerView.smoothScrollToPosition(position);
}
public RecyclerView getRecyclerView() {
return this.recyclerView;
}

View File

@@ -8,6 +8,8 @@ import android.widget.ImageView;
import org.briarproject.R;
import org.briarproject.api.identity.Author.Status;
import static org.briarproject.api.identity.Author.Status.OURSELVES;
public class TrustIndicatorView extends ImageView {
public TrustIndicatorView(Context context) {
@@ -24,6 +26,11 @@ public class TrustIndicatorView extends ImageView {
}
public void setTrustLevel(Status status) {
if (status == OURSELVES) {
setVisibility(GONE);
return;
}
int res;
switch (status) {
case ANONYMOUS:

View File

@@ -5,7 +5,7 @@ import java.io.UnsupportedEncodingException;
/** A pseudonym for a user. */
public class Author {
public enum Status { ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED }
public enum Status { ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES }
private final AuthorId id;
private final String name;

View File

@@ -17,6 +17,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import javax.inject.Inject;
import static org.briarproject.api.identity.Author.Status.OURSELVES;
import static org.briarproject.api.identity.Author.Status.UNKNOWN;
import static org.briarproject.api.identity.Author.Status.VERIFIED;
@@ -110,7 +111,7 @@ class IdentityManagerImpl implements IdentityManager {
throws DbException {
// Compare to the IDs of the user's identities
for (LocalAuthor a : db.getLocalAuthors(txn))
if (a.getId().equals(authorId)) return VERIFIED;
if (a.getId().equals(authorId)) return OURSELVES;
// Compare to the IDs of contacts' identities
for (Contact c : db.getContacts(txn))
if (c.getAuthor().getId().equals(authorId)) return VERIFIED;