diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml
index 2d8dc0223..53ce958da 100644
--- a/briar-android/AndroidManifest.xml
+++ b/briar-android/AndroidManifest.xml
@@ -149,6 +149,16 @@
/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/briar-android/res/layout/activity_create_forum.xml b/briar-android/res/layout/activity_create_forum.xml
index b48bce90f..96afb47a9 100644
--- a/briar-android/res/layout/activity_create_forum.xml
+++ b/briar-android/res/layout/activity_create_forum.xml
@@ -32,9 +32,6 @@
-
diff --git a/briar-android/res/layout/activity_create_identity.xml b/briar-android/res/layout/activity_create_identity.xml
index 23309bfa8..5919662cf 100644
--- a/briar-android/res/layout/activity_create_identity.xml
+++ b/briar-android/res/layout/activity_create_identity.xml
@@ -34,8 +34,6 @@
diff --git a/briar-android/res/layout/fragment_blogs_list.xml b/briar-android/res/layout/fragment_blogs_list.xml
new file mode 100644
index 000000000..a552dc0fc
--- /dev/null
+++ b/briar-android/res/layout/fragment_blogs_list.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/briar-android/res/layout/introduction_message.xml b/briar-android/res/layout/introduction_message.xml
index 943847991..409f4afac 100644
--- a/briar-android/res/layout/introduction_message.xml
+++ b/briar-android/res/layout/introduction_message.xml
@@ -88,10 +88,7 @@
+ android:text="@string/introduction_button"/>
diff --git a/briar-android/res/layout/share_forum_message.xml b/briar-android/res/layout/share_forum_message.xml
index 522a0472d..33863fc13 100644
--- a/briar-android/res/layout/share_forum_message.xml
+++ b/briar-android/res/layout/share_forum_message.xml
@@ -34,10 +34,7 @@
+ android:text="@string/forum_share_button"/>
diff --git a/briar-android/res/menu/blogs_my_actions.xml b/briar-android/res/menu/blogs_my_actions.xml
new file mode 100644
index 000000000..9a3ce5b49
--- /dev/null
+++ b/briar-android/res/menu/blogs_my_actions.xml
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index 1618f97e3..3447cf1ec 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -249,7 +249,15 @@
Blogs
Feed
+
My Blogs
+ Create Blog
+ Add new Blog
+ Blog title (cannot be changed later)
+ A short description of your new blog
+ Potential readers may or may not subscribe to your blog based on the content of the description.
+ Blog created
+
Blog List
Available Blogs
Drafts
diff --git a/briar-android/res/values/styles.xml b/briar-android/res/values/styles.xml
index 9251475da..1ecc1494c 100644
--- a/briar-android/res/values/styles.xml
+++ b/briar-android/res/values/styles.xml
@@ -23,6 +23,8 @@
@@ -109,4 +111,9 @@
- @color/briar_text_primary_inverse
+
+
+
\ No newline at end of file
diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java
index 4d82fe74d..50aa911d3 100644
--- a/briar-android/src/org/briarproject/android/ActivityComponent.java
+++ b/briar-android/src/org/briarproject/android/ActivityComponent.java
@@ -2,6 +2,7 @@ package org.briarproject.android;
import android.app.Activity;
+import org.briarproject.android.blogs.CreateBlogActivity;
import org.briarproject.android.blogs.MyBlogsFragment;
import org.briarproject.android.contact.ContactListFragment;
import org.briarproject.android.contact.ConversationActivity;
@@ -64,6 +65,8 @@ public interface ActivityComponent {
void inject(ForumActivity activity);
+ void inject(CreateBlogActivity activity);
+
void inject(SettingsActivity activity);
void inject(ChangePasswordActivity activity);
diff --git a/briar-android/src/org/briarproject/android/AndroidComponent.java b/briar-android/src/org/briarproject/android/AndroidComponent.java
index 5bc21c1cc..d6a495eb0 100644
--- a/briar-android/src/org/briarproject/android/AndroidComponent.java
+++ b/briar-android/src/org/briarproject/android/AndroidComponent.java
@@ -7,6 +7,8 @@ import org.briarproject.android.api.AndroidNotificationManager;
import org.briarproject.android.api.ReferenceManager;
import org.briarproject.android.forum.ForumPersistentData;
import org.briarproject.android.report.BriarReportSender;
+import org.briarproject.api.blogs.BlogManager;
+import org.briarproject.api.blogs.BlogPostFactory;
import org.briarproject.api.contact.ContactExchangeTask;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
@@ -96,6 +98,10 @@ public interface AndroidComponent extends CoreEagerSingletons {
ForumPostFactory forumPostFactory();
+ BlogManager blogManager();
+
+ BlogPostFactory blogPostFactory();
+
SettingsManager settingsManager();
ContactExchangeTask contactExchangeTask();
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogListFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogListFragment.java
new file mode 100644
index 000000000..61f3dff54
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BlogListFragment.java
@@ -0,0 +1,53 @@
+package org.briarproject.android.blogs;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.fragment.BaseFragment;
+
+public class BlogListFragment extends BaseFragment {
+
+ public final static String TAG = BlogListFragment.class.getName();
+
+ static BlogListFragment newInstance(int num) {
+ BlogListFragment f = new BlogListFragment();
+
+ Bundle args = new Bundle();
+ args.putInt("num", num);
+ f.setArguments(args);
+
+ return f;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+
+ View v = inflater.inflate(R.layout.fragment_blogs_list, container,
+ false);
+
+ TextView numView = (TextView) v.findViewById(R.id.num);
+ String num = String.valueOf(getArguments().getInt("num"));
+ numView.setText(num);
+
+ return v;
+ }
+
+ @Override
+ public void injectFragment(ActivityComponent component) {
+ component.inject(this);
+ }
+
+ @Override
+ public String getUniqueTag() {
+ return TAG;
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogsFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogsFragment.java
index a736537ff..312a9d06c 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogsFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogsFragment.java
@@ -78,7 +78,7 @@ public class BlogsFragment extends BaseFragment {
}
- private static class TabAdapter extends FragmentStatePagerAdapter {
+ private class TabAdapter extends FragmentStatePagerAdapter {
private String[] titles;
TabAdapter(FragmentManager fm, String[] titles) {
@@ -94,9 +94,10 @@ public class BlogsFragment extends BaseFragment {
@Override
public Fragment getItem(int position) {
switch (position) {
- // TODO add your fragments here
+ case 1:
+ return new MyBlogsFragment();
default:
- return MyBlogsFragment.newInstance(position);
+ return BlogListFragment.newInstance(position);
}
}
diff --git a/briar-android/src/org/briarproject/android/blogs/CreateBlogActivity.java b/briar-android/src/org/briarproject/android/blogs/CreateBlogActivity.java
new file mode 100644
index 000000000..97934a65f
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/CreateBlogActivity.java
@@ -0,0 +1,184 @@
+package org.briarproject.android.blogs;
+
+import android.os.Bundle;
+import android.support.design.widget.TextInputEditText;
+import android.support.design.widget.TextInputLayout;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+import android.widget.Toast;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.BriarActivity;
+import org.briarproject.api.blogs.Blog;
+import org.briarproject.api.blogs.BlogManager;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.util.StringUtils;
+
+import java.util.Collection;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+import static android.widget.Toast.LENGTH_LONG;
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
+import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_DESC_LENGTH;
+import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_TITLE_LENGTH;
+
+public class CreateBlogActivity extends BriarActivity
+ implements OnEditorActionListener, OnClickListener {
+
+ private static final Logger LOG =
+ Logger.getLogger(CreateBlogActivity.class.getName());
+
+ private TextInputEditText titleInput, descInput;
+ private Button button;
+ private ProgressBar progress;
+
+ // Fields that are accessed from background threads must be volatile
+ @Inject
+ protected volatile IdentityManager identityManager;
+ @Inject
+ volatile BlogManager blogManager;
+
+ @Override
+ public void onCreate(Bundle state) {
+ super.onCreate(state);
+
+ setContentView(R.layout.activity_create_blog);
+
+ TextInputLayout titleLayout =
+ (TextInputLayout) findViewById(R.id.titleLayout);
+ if (titleLayout != null) {
+ titleLayout.setCounterMaxLength(MAX_BLOG_TITLE_LENGTH);
+ }
+ titleInput = (TextInputEditText) findViewById(R.id.titleInput);
+ TextWatcher nameEntryWatcher = new TextWatcher() {
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ }
+ @Override
+ public void onTextChanged(CharSequence text, int start,
+ int lengthBefore, int lengthAfter) {
+ enableOrDisableCreateButton();
+ }
+ };
+ titleInput.setOnEditorActionListener(this);
+ titleInput.addTextChangedListener(nameEntryWatcher);
+
+ TextInputLayout descLayout =
+ (TextInputLayout) findViewById(R.id.descLayout);
+ if (descLayout != null) {
+ descLayout.setCounterMaxLength(MAX_BLOG_DESC_LENGTH);
+ }
+ descInput = (TextInputEditText) findViewById(R.id.descInput);
+ if (descInput != null) {
+ descInput.addTextChangedListener(nameEntryWatcher);
+ }
+
+ button = (Button) findViewById(R.id.createBlogButton);
+ if (button != null) {
+ button.setOnClickListener(this);
+ }
+
+ progress = (ProgressBar) findViewById(R.id.createBlogProgressBar);
+ }
+
+ @Override
+ public void injectActivity(ActivityComponent component) {
+ component.inject(this);
+ }
+
+ private void enableOrDisableCreateButton() {
+ if (progress == null) return; // Not created yet
+ button.setEnabled(validateTitle() && validateDescription());
+ }
+
+ @Override
+ public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
+ descInput.requestFocus();
+ return true;
+ }
+
+ private boolean validateTitle() {
+ String name = titleInput.getText().toString();
+ int length = StringUtils.toUtf8(name).length;
+ return length <= MAX_BLOG_TITLE_LENGTH && length > 0;
+ }
+
+ private boolean validateDescription() {
+ String name = descInput.getText().toString();
+ int length = StringUtils.toUtf8(name).length;
+ return length <= MAX_BLOG_DESC_LENGTH && length > 0;
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view == button) {
+ hideSoftKeyboard(view);
+ if (!validateTitle()) return;
+ button.setVisibility(GONE);
+ progress.setVisibility(VISIBLE);
+ addBlog(titleInput.getText().toString(),
+ descInput.getText().toString());
+ }
+ }
+
+ private void addBlog(final String title, final String description) {
+ runOnDbThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ long now = System.currentTimeMillis();
+ Collection authors =
+ identityManager.getLocalAuthors();
+ // take first identity, don't support more for now
+ LocalAuthor author = authors.iterator().next();
+ Blog f = blogManager.addBlog(author, title, description);
+ long duration = System.currentTimeMillis() - now;
+ if (LOG.isLoggable(INFO))
+ LOG.info("Storing blog took " + duration + " ms");
+ displayBlog(f);
+ } catch (DbException e) {
+ // TODO show error, e.g. blog with same title exists
+ if (LOG.isLoggable(WARNING))
+ LOG.log(WARNING, e.toString(), e);
+ finishOnUiThread();
+ }
+ }
+ });
+ }
+
+ private void displayBlog(final Blog b) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ // TODO
+/* Intent i = new Intent(CreateBlogActivity.this,
+ BlogActivity.class);
+ i.putExtra(GROUP_ID, b.getId().getBytes());
+ i.putExtra(BLOG_NAME, b.getName());
+ startActivity(i);
+*/ Toast.makeText(CreateBlogActivity.this,
+ R.string.blogs_my_blogs_created, LENGTH_LONG).show();
+ supportFinishAfterTransition();
+ }
+ });
+ }
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/MyBlogsFragment.java b/briar-android/src/org/briarproject/android/blogs/MyBlogsFragment.java
index 572bc516e..cb28dd0ea 100644
--- a/briar-android/src/org/briarproject/android/blogs/MyBlogsFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/MyBlogsFragment.java
@@ -1,8 +1,14 @@
package org.briarproject.android.blogs;
+import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.ActivityOptionsCompat;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
@@ -13,6 +19,8 @@ import org.briarproject.android.fragment.BaseFragment;
import javax.inject.Inject;
+import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
+
public class MyBlogsFragment extends BaseFragment {
public final static String TAG = MyBlogsFragment.class.getName();
@@ -21,31 +29,52 @@ public class MyBlogsFragment extends BaseFragment {
public MyBlogsFragment() {
}
- static MyBlogsFragment newInstance(int num) {
- MyBlogsFragment f = new MyBlogsFragment();
-
- Bundle args = new Bundle();
- args.putInt("num", num);
- 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_blogs_my, container,
false);
TextView numView = (TextView) v.findViewById(R.id.num);
- String num = String.valueOf(getArguments().getInt("num"));
- numView.setText(num);
+ numView.setText("My Blogs");
return v;
}
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ listener.getActivityComponent().inject(this);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ inflater.inflate(R.menu.blogs_my_actions, menu);
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ // Handle presses on the action bar items
+ switch (item.getItemId()) {
+ case R.id.action_create_blog:
+ Intent intent =
+ new Intent(getContext(), CreateBlogActivity.class);
+ ActivityOptionsCompat options =
+ makeCustomAnimation(getActivity(),
+ android.R.anim.slide_in_left,
+ android.R.anim.slide_out_right);
+ ActivityCompat.startActivity(getActivity(), intent,
+ options.toBundle());
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
@Override
public String getUniqueTag() {
return TAG;
diff --git a/briar-android/src/org/briarproject/android/util/AndroidUtils.java b/briar-android/src/org/briarproject/android/util/AndroidUtils.java
index af9cc2ae0..216ba316e 100644
--- a/briar-android/src/org/briarproject/android/util/AndroidUtils.java
+++ b/briar-android/src/org/briarproject/android/util/AndroidUtils.java
@@ -56,6 +56,11 @@ public class AndroidUtils {
til.setError(null);
}
+ public static void setError(TextInputLayout til, int res,
+ boolean condition) {
+ setError(til, til.getContext().getString(res), condition);
+ }
+
public static String getBluetoothAddress(Context ctx,
BluetoothAdapter adapter) {
// Return the adapter's address if it's valid and not fake
diff --git a/briar-android/src/org/briarproject/android/util/TextAvatarView.java b/briar-android/src/org/briarproject/android/util/TextAvatarView.java
index cc4ab9148..6033021eb 100644
--- a/briar-android/src/org/briarproject/android/util/TextAvatarView.java
+++ b/briar-android/src/org/briarproject/android/util/TextAvatarView.java
@@ -38,7 +38,7 @@ public class TextAvatarView extends FrameLayout {
}
public void setText(String text) {
- character.setText(text);
+ character.setText(text.toUpperCase());
}
public void setUnreadCount(int count) {