mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
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:
10
briar-android/res/drawable/ic_chat.xml
Normal file
10
briar-android/res/drawable/ic_chat.xml
Normal 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>
|
||||
10
briar-android/res/drawable/ic_repeat.xml
Normal file
10
briar-android/res/drawable/ic_repeat.xml
Normal 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>
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
12
briar-android/res/menu/blogs_feed_actions.xml
Normal file
12
briar-android/res/menu/blogs_feed_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="always"/>
|
||||
|
||||
</menu>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user