mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 22:29:53 +01:00
Add a BlogActivity that shows a list of blog posts
This commit lays the groundwork for #415
This commit is contained in:
@@ -159,6 +159,15 @@
|
|||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".android.blogs.BlogActivity"
|
||||||
|
android:parentActivityName=".android.NavDrawerActivity">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
|
android:value=".android.NavDrawerActivity"
|
||||||
|
/>
|
||||||
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.identity.CreateIdentityActivity"
|
android:name=".android.identity.CreateIdentityActivity"
|
||||||
android:label="@string/new_identity_title"
|
android:label="@string/new_identity_title"
|
||||||
|
|||||||
10
briar-android/res/layout/activity_blog.xml
Normal file
10
briar-android/res/layout/activity_blog.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<org.briarproject.android.util.BriarRecyclerView
|
||||||
|
android:id="@+id/postList"
|
||||||
|
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="match_parent"
|
||||||
|
app:scrollToEnd="false"
|
||||||
|
tools:context=".android.blogs.BlogActivity"/>
|
||||||
68
briar-android/res/layout/list_item_blog_post.xml
Normal file
68
briar-android/res/layout/list_item_blog_post.xml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
|
||||||
|
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
||||||
|
android:layout_marginTop="@dimen/margin_medium"
|
||||||
|
android:background="?attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/titleView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
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"/>
|
||||||
|
|
||||||
|
<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."/>
|
||||||
|
|
||||||
|
<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"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
12
briar-android/res/menu/blogs_my_blog_actions.xml
Normal file
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>
|
||||||
@@ -257,8 +257,13 @@
|
|||||||
<string name="blogs_my_blogs_create_hint_desc">A short description of your new blog</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_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_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_my_blogs_created">Blog created</string>
|
||||||
<string name="blogs_blog_is_empty">This blog is empty</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="blogs_post_more">more</string>
|
||||||
|
<string name="blogs_write_blog_post">Write Blog Post</string>
|
||||||
|
|
||||||
<string name="blogs_blog_list">Blog List</string>
|
<string name="blogs_blog_list">Blog List</string>
|
||||||
<string name="blogs_available_blogs">Available Blogs</string>
|
<string name="blogs_available_blogs">Available Blogs</string>
|
||||||
|
|||||||
@@ -57,6 +57,17 @@
|
|||||||
<item name="android:textColor">@android:color/primary_text_light</item>
|
<item name="android:textColor">@android:color/primary_text_light</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="BriarTag">
|
||||||
|
<item name="android:layout_width">wrap_content</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:layout_marginRight">@dimen/margin_medium</item>
|
||||||
|
<item name="android:paddingLeft">3dp</item>
|
||||||
|
<item name="android:paddingRight">3dp</item>
|
||||||
|
<item name="android:background">@color/briar_primary</item>
|
||||||
|
<item name="android:textSize">@dimen/text_size_tiny</item>
|
||||||
|
<item name="android:textColor">@color/briar_text_primary_inverse</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="Divider">
|
<style name="Divider">
|
||||||
<item name="android:background">@color/divider</item>
|
<item name="android:background">@color/divider</item>
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.android;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import org.briarproject.android.blogs.BlogActivity;
|
||||||
import org.briarproject.android.blogs.CreateBlogActivity;
|
import org.briarproject.android.blogs.CreateBlogActivity;
|
||||||
import org.briarproject.android.blogs.MyBlogsFragment;
|
import org.briarproject.android.blogs.MyBlogsFragment;
|
||||||
import org.briarproject.android.contact.ContactListFragment;
|
import org.briarproject.android.contact.ContactListFragment;
|
||||||
@@ -67,6 +68,8 @@ public interface ActivityComponent {
|
|||||||
|
|
||||||
void inject(CreateBlogActivity activity);
|
void inject(CreateBlogActivity activity);
|
||||||
|
|
||||||
|
void inject(BlogActivity activity);
|
||||||
|
|
||||||
void inject(SettingsActivity activity);
|
void inject(SettingsActivity activity);
|
||||||
|
|
||||||
void inject(ChangePasswordActivity activity);
|
void inject(ChangePasswordActivity activity);
|
||||||
|
|||||||
@@ -0,0 +1,174 @@
|
|||||||
|
package org.briarproject.android.blogs;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import org.briarproject.R;
|
||||||
|
import org.briarproject.android.ActivityComponent;
|
||||||
|
import org.briarproject.android.BriarActivity;
|
||||||
|
import org.briarproject.android.util.BriarRecyclerView;
|
||||||
|
import org.briarproject.api.blogs.BlogManager;
|
||||||
|
import org.briarproject.api.blogs.BlogPostHeader;
|
||||||
|
import org.briarproject.api.db.DbException;
|
||||||
|
import org.briarproject.api.db.NoSuchGroupException;
|
||||||
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.support.design.widget.Snackbar.LENGTH_LONG;
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
|
public class BlogActivity extends BriarActivity {
|
||||||
|
|
||||||
|
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 int WRITE_POST = 1;
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(BlogActivity.class.getName());
|
||||||
|
|
||||||
|
private BlogPostAdapter adapter;
|
||||||
|
private BriarRecyclerView list;
|
||||||
|
private String blogName;
|
||||||
|
private boolean myBlog;
|
||||||
|
|
||||||
|
// Fields that are accessed from background threads must be volatile
|
||||||
|
private volatile GroupId groupId = null;
|
||||||
|
private volatile boolean scrollToTop = false;
|
||||||
|
@Inject
|
||||||
|
volatile BlogManager blogManager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle state) {
|
||||||
|
super.onCreate(state);
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_blog);
|
||||||
|
|
||||||
|
Intent i = getIntent();
|
||||||
|
byte[] b = i.getByteArrayExtra(GROUP_ID);
|
||||||
|
if (b == null) throw new IllegalStateException("No Group in intent.");
|
||||||
|
groupId = new GroupId(b);
|
||||||
|
blogName = i.getStringExtra(BLOG_NAME);
|
||||||
|
if (blogName != null) setTitle(blogName);
|
||||||
|
myBlog = i.getBooleanExtra(IS_MY_BLOG, false);
|
||||||
|
|
||||||
|
adapter = new BlogPostAdapter(this, blogName);
|
||||||
|
list = (BriarRecyclerView) this.findViewById(R.id.postList);
|
||||||
|
list.setLayoutManager(new LinearLayoutManager(this));
|
||||||
|
list.setAdapter(adapter);
|
||||||
|
if (myBlog) {
|
||||||
|
list.setEmptyText(
|
||||||
|
getString(R.string.blogs_my_blogs_blog_empty_state));
|
||||||
|
} else {
|
||||||
|
list.setEmptyText(getString(R.string.blogs_other_blog_empty_state));
|
||||||
|
}
|
||||||
|
|
||||||
|
// show snackbar if this blog was just created
|
||||||
|
boolean isNew = i.getBooleanExtra(IS_NEW_BLOG, false);
|
||||||
|
if (isNew) {
|
||||||
|
Snackbar s = Snackbar.make(list, R.string.blogs_my_blogs_created,
|
||||||
|
LENGTH_LONG);
|
||||||
|
s.getView().setBackgroundResource(R.color.briar_primary);
|
||||||
|
s.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
loadBlogPosts();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
if (myBlog) {
|
||||||
|
MenuInflater inflater = getMenuInflater();
|
||||||
|
inflater.inflate(R.menu.blogs_my_blog_actions, menu);
|
||||||
|
}
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.action_write_blog_post:
|
||||||
|
/* Intent i = new Intent(this, WriteBlogPostActivity.class);
|
||||||
|
i.putExtra(GROUP_ID, groupId.getBytes());
|
||||||
|
i.putExtra(BLOG_NAME, blogName);
|
||||||
|
startActivityForResult(i, WRITE_POST);
|
||||||
|
*/ return true;
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
if (requestCode == WRITE_POST && resultCode == RESULT_OK) {
|
||||||
|
scrollToTop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectActivity(ActivityComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadBlogPosts() {
|
||||||
|
runOnDbThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
// load blog posts
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
Collection<BlogPostItem> posts = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
Collection<BlogPostHeader> header =
|
||||||
|
blogManager.getPostHeaders(groupId);
|
||||||
|
for (BlogPostHeader h : header) {
|
||||||
|
posts.add(new BlogPostItem(h));
|
||||||
|
}
|
||||||
|
} catch (NoSuchGroupException e) {
|
||||||
|
// Continue
|
||||||
|
}
|
||||||
|
displayBlogPosts(posts);
|
||||||
|
long duration = System.currentTimeMillis() - now;
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Post header load took " + duration + " ms");
|
||||||
|
} catch (DbException e) {
|
||||||
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayBlogPosts(final Collection<BlogPostItem> items) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (items.size() == 0) {
|
||||||
|
list.showData();
|
||||||
|
} else {
|
||||||
|
adapter.addAll(items);
|
||||||
|
if (scrollToTop) list.scrollToPosition(0);
|
||||||
|
}
|
||||||
|
scrollToTop = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO listen to events and add new blog posts as they come in
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
package org.briarproject.android.blogs;
|
package org.briarproject.android.blogs;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
import android.support.v4.app.ActivityOptionsCompat;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v7.util.SortedList;
|
import android.support.v7.util.SortedList;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
@@ -13,12 +17,17 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import org.briarproject.R;
|
import org.briarproject.R;
|
||||||
import org.briarproject.android.util.TextAvatarView;
|
import org.briarproject.android.util.TextAvatarView;
|
||||||
|
import org.briarproject.api.blogs.Blog;
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
|
import static org.briarproject.android.BriarActivity.GROUP_ID;
|
||||||
|
import static org.briarproject.android.blogs.BlogActivity.BLOG_NAME;
|
||||||
|
import static org.briarproject.android.blogs.BlogActivity.IS_MY_BLOG;
|
||||||
|
|
||||||
class BlogListAdapter extends
|
class BlogListAdapter extends
|
||||||
RecyclerView.Adapter<BlogListAdapter.BlogViewHolder> {
|
RecyclerView.Adapter<BlogListAdapter.BlogViewHolder> {
|
||||||
@@ -72,9 +81,9 @@ class BlogListAdapter extends
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
private final Context ctx;
|
private final Activity ctx;
|
||||||
|
|
||||||
BlogListAdapter(Context ctx) {
|
BlogListAdapter(Activity ctx) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,13 +131,16 @@ class BlogListAdapter extends
|
|||||||
ui.layout.setOnClickListener(new View.OnClickListener() {
|
ui.layout.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
// TODO #415
|
Intent i = new Intent(ctx, BlogActivity.class);
|
||||||
/* Intent i = new Intent(ctx, BlogActivity.class);
|
|
||||||
Blog b = item.getBlog();
|
Blog b = item.getBlog();
|
||||||
i.putExtra(GROUP_ID, b.getId().getBytes());
|
i.putExtra(GROUP_ID, b.getId().getBytes());
|
||||||
i.putExtra(BLOG_NAME, b.getName());
|
i.putExtra(BLOG_NAME, b.getName());
|
||||||
ctx.startActivity(i);
|
i.putExtra(IS_MY_BLOG, item.isOurs());
|
||||||
*/ }
|
ActivityOptionsCompat options = ActivityOptionsCompat
|
||||||
|
.makeCustomAnimation(ctx, android.R.anim.fade_in,
|
||||||
|
android.R.anim.fade_out);
|
||||||
|
ActivityCompat.startActivity(ctx, i, options.toBundle());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ class BlogListItem {
|
|||||||
private final int postCount;
|
private final int postCount;
|
||||||
private final long timestamp;
|
private final long timestamp;
|
||||||
private final int unread;
|
private final int unread;
|
||||||
|
private final boolean ours;
|
||||||
|
|
||||||
BlogListItem(Blog blog, Collection<BlogPostHeader> headers) {
|
BlogListItem(Blog blog, Collection<BlogPostHeader> headers, boolean ours) {
|
||||||
this.blog = blog;
|
this.blog = blog;
|
||||||
if (headers.isEmpty()) {
|
if (headers.isEmpty()) {
|
||||||
postCount = 0;
|
postCount = 0;
|
||||||
@@ -33,6 +34,7 @@ class BlogListItem {
|
|||||||
this.timestamp = newest.getTimestamp();
|
this.timestamp = newest.getTimestamp();
|
||||||
this.unread = unread;
|
this.unread = unread;
|
||||||
}
|
}
|
||||||
|
this.ours = ours;
|
||||||
}
|
}
|
||||||
|
|
||||||
Blog getBlog() {
|
Blog getBlog() {
|
||||||
@@ -58,4 +60,8 @@ class BlogListItem {
|
|||||||
int getUnreadCount() {
|
int getUnreadCount() {
|
||||||
return unread;
|
return unread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isOurs() {
|
||||||
|
return ours;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
package org.briarproject.android.blogs;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v7.util.SortedList;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.briarproject.R;
|
||||||
|
import org.briarproject.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import static android.view.View.GONE;
|
||||||
|
import static android.view.View.VISIBLE;
|
||||||
|
|
||||||
|
class BlogPostAdapter extends
|
||||||
|
RecyclerView.Adapter<BlogPostAdapter.BlogPostHolder> {
|
||||||
|
|
||||||
|
private SortedList<BlogPostItem> posts = new SortedList<>(
|
||||||
|
BlogPostItem.class, new SortedList.Callback<BlogPostItem>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(BlogPostItem a, BlogPostItem b) {
|
||||||
|
if (a == b) return 0;
|
||||||
|
// The blog with the newest message comes first
|
||||||
|
long aTime = a.getTimestamp(), bTime = b.getTimestamp();
|
||||||
|
if (aTime > bTime) return -1;
|
||||||
|
if (aTime < bTime) return 1;
|
||||||
|
// Break ties by post title
|
||||||
|
if (a.getTitle() != null && b.getTitle() != null) {
|
||||||
|
return String.CASE_INSENSITIVE_ORDER
|
||||||
|
.compare(a.getTitle(), b.getTitle());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInserted(int position, int count) {
|
||||||
|
notifyItemRangeInserted(position, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemoved(int position, int count) {
|
||||||
|
notifyItemRangeRemoved(position, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMoved(int fromPosition, int toPosition) {
|
||||||
|
notifyItemMoved(fromPosition, toPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(int position, int count) {
|
||||||
|
notifyItemRangeChanged(position, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areContentsTheSame(BlogPostItem a, BlogPostItem b) {
|
||||||
|
return a.isRead() == b.isRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areItemsTheSame(BlogPostItem a, BlogPostItem b) {
|
||||||
|
return a.getId().equals(b.getId());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
private final Context ctx;
|
||||||
|
private final String blogTitle;
|
||||||
|
|
||||||
|
BlogPostAdapter(Context ctx, String blogTitle) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.blogTitle = blogTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlogPostHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
View v = LayoutInflater.from(ctx).inflate(
|
||||||
|
R.layout.list_item_blog_post, parent, false);
|
||||||
|
return new BlogPostHolder(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(BlogPostHolder ui, int position) {
|
||||||
|
final BlogPostItem item = getItem(position);
|
||||||
|
|
||||||
|
// title
|
||||||
|
if (item.getTitle() != null) {
|
||||||
|
ui.title.setText(item.getTitle());
|
||||||
|
ui.title.setVisibility(VISIBLE);
|
||||||
|
} else {
|
||||||
|
ui.title.setVisibility(GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// post body
|
||||||
|
ui.body.setText(StringUtils.fromUtf8(item.getBody()));
|
||||||
|
|
||||||
|
// date
|
||||||
|
ui.date.setText(
|
||||||
|
DateUtils.getRelativeTimeSpanString(ctx, item.getTimestamp()));
|
||||||
|
|
||||||
|
// new tag
|
||||||
|
if (item.isRead()) ui.unread.setVisibility(GONE);
|
||||||
|
else ui.unread.setVisibility(VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return posts.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlogPostItem getItem(int position) {
|
||||||
|
return posts.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAll(Collection<BlogPostItem> items) {
|
||||||
|
posts.addAll(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(BlogPostItem item) {
|
||||||
|
posts.remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
posts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return posts.size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BlogPostHolder extends RecyclerView.ViewHolder {
|
||||||
|
private final ViewGroup layout;
|
||||||
|
private final TextView title;
|
||||||
|
private final TextView unread;
|
||||||
|
private final TextView date;
|
||||||
|
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);
|
||||||
|
date = (TextView) v.findViewById(R.id.dateView);
|
||||||
|
body = (TextView) v.findViewById(R.id.bodyView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package org.briarproject.android.blogs;
|
||||||
|
|
||||||
|
import org.briarproject.api.blogs.BlogPostHeader;
|
||||||
|
import org.briarproject.api.identity.Author;
|
||||||
|
import org.briarproject.api.identity.Author.Status;
|
||||||
|
import org.briarproject.api.sync.MessageId;
|
||||||
|
|
||||||
|
// This class is not thread-safe
|
||||||
|
class BlogPostItem {
|
||||||
|
|
||||||
|
private final BlogPostHeader header;
|
||||||
|
private final byte[] body;
|
||||||
|
private boolean read;
|
||||||
|
|
||||||
|
BlogPostItem(BlogPostHeader header, byte[] body) {
|
||||||
|
this.header = header;
|
||||||
|
this.body = body;
|
||||||
|
read = header.isRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageId getId() {
|
||||||
|
return header.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return header.getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimestamp() {
|
||||||
|
return header.getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Author getAuthor() {
|
||||||
|
return header.getAuthor();
|
||||||
|
}
|
||||||
|
|
||||||
|
Status getAuthorStatus() {
|
||||||
|
return header.getAuthorStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRead(boolean read) {
|
||||||
|
this.read = read;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRead() {
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
package org.briarproject.android.blogs;
|
package org.briarproject.android.blogs;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.design.widget.TextInputEditText;
|
import android.support.design.widget.TextInputEditText;
|
||||||
import android.support.design.widget.TextInputLayout;
|
import android.support.design.widget.TextInputLayout;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
import android.support.v4.app.ActivityOptionsCompat;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
@@ -29,11 +32,15 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static android.widget.Toast.LENGTH_LONG;
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static org.briarproject.android.blogs.BlogActivity.BLOG_NAME;
|
||||||
|
import static org.briarproject.android.blogs.BlogActivity.IS_MY_BLOG;
|
||||||
|
import static org.briarproject.android.blogs.BlogActivity.IS_NEW_BLOG;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_DESC_LENGTH;
|
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_DESC_LENGTH;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_TITLE_LENGTH;
|
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_TITLE_LENGTH;
|
||||||
|
|
||||||
@@ -169,14 +176,18 @@ public class CreateBlogActivity extends BriarActivity
|
|||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// TODO
|
Intent i =
|
||||||
/* Intent i = new Intent(CreateBlogActivity.this,
|
new Intent(CreateBlogActivity.this, BlogActivity.class);
|
||||||
BlogActivity.class);
|
|
||||||
i.putExtra(GROUP_ID, b.getId().getBytes());
|
i.putExtra(GROUP_ID, b.getId().getBytes());
|
||||||
i.putExtra(BLOG_NAME, b.getName());
|
i.putExtra(BLOG_NAME, b.getName());
|
||||||
startActivity(i);
|
i.putExtra(IS_MY_BLOG, true);
|
||||||
*/ Toast.makeText(CreateBlogActivity.this,
|
i.putExtra(IS_NEW_BLOG, true);
|
||||||
R.string.blogs_my_blogs_created, LENGTH_LONG).show();
|
ActivityOptionsCompat options =
|
||||||
|
makeCustomAnimation(CreateBlogActivity.this,
|
||||||
|
android.R.anim.fade_in,
|
||||||
|
android.R.anim.fade_out);
|
||||||
|
ActivityCompat.startActivity(CreateBlogActivity.this, i,
|
||||||
|
options.toBundle());
|
||||||
supportFinishAfterTransition();
|
supportFinishAfterTransition();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ public class MyBlogsFragment extends BaseFragment {
|
|||||||
try {
|
try {
|
||||||
Collection<BlogPostHeader> headers =
|
Collection<BlogPostHeader> headers =
|
||||||
blogManager.getPostHeaders(b.getId());
|
blogManager.getPostHeaders(b.getId());
|
||||||
blogs.add(new BlogListItem(b, headers));
|
blogs.add(new BlogListItem(b, headers, true));
|
||||||
} catch (NoSuchGroupException e) {
|
} catch (NoSuchGroupException e) {
|
||||||
// Continue
|
// Continue
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user