mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 13:19:52 +01:00
Removed blogs from the Android UI.
This commit is contained in:
@@ -45,34 +45,6 @@
|
|||||||
android:name=".android.contact.ContactListActivity"
|
android:name=".android.contact.ContactListActivity"
|
||||||
android:label="@string/contact_list_title" >
|
android:label="@string/contact_list_title" >
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
|
||||||
android:name=".android.blogs.BlogActivity"
|
|
||||||
android:label="@string/app_name" >
|
|
||||||
</activity>
|
|
||||||
<activity
|
|
||||||
android:name=".android.blogs.BlogListActivity"
|
|
||||||
android:label="@string/blogs_title" >
|
|
||||||
</activity>
|
|
||||||
<activity
|
|
||||||
android:name=".android.blogs.ConfigureBlogActivity"
|
|
||||||
android:label="@string/app_name" >
|
|
||||||
</activity>
|
|
||||||
<activity
|
|
||||||
android:name=".android.blogs.CreateBlogActivity"
|
|
||||||
android:label="@string/create_blog_title" >
|
|
||||||
</activity>
|
|
||||||
<activity
|
|
||||||
android:name=".android.blogs.ManageBlogsActivity"
|
|
||||||
android:label="@string/manage_subscriptions_title" >
|
|
||||||
</activity>
|
|
||||||
<activity
|
|
||||||
android:name=".android.blogs.ReadBlogPostActivity"
|
|
||||||
android:label="@string/app_name" >
|
|
||||||
</activity>
|
|
||||||
<activity
|
|
||||||
android:name=".android.blogs.WriteBlogPostActivity"
|
|
||||||
android:label="@string/new_post_title" >
|
|
||||||
</activity>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.groups.ConfigureGroupActivity"
|
android:name=".android.groups.ConfigureGroupActivity"
|
||||||
android:label="@string/app_name" >
|
android:label="@string/app_name" >
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
<string name="contact_list_button">Contacts</string>
|
<string name="contact_list_button">Contacts</string>
|
||||||
<string name="messages_button">Messages</string>
|
<string name="messages_button">Messages</string>
|
||||||
<string name="groups_button">Groups</string>
|
<string name="groups_button">Groups</string>
|
||||||
<string name="blogs_button">Blogs</string>
|
|
||||||
<string name="synchronize_button">Synchronize</string>
|
<string name="synchronize_button">Synchronize</string>
|
||||||
<string name="quit_button">Quit</string>
|
<string name="quit_button">Quit</string>
|
||||||
<string name="new_identity_item">New identity\u2026</string>
|
<string name="new_identity_item">New identity\u2026</string>
|
||||||
@@ -74,23 +73,9 @@
|
|||||||
<string name="new_group_item">New group\u2026</string>
|
<string name="new_group_item">New group\u2026</string>
|
||||||
<string name="manage_subscriptions_title">Manage Subscriptions</string>
|
<string name="manage_subscriptions_title">Manage Subscriptions</string>
|
||||||
<string name="no_groups_available">No groups available from contacts</string>
|
<string name="no_groups_available">No groups available from contacts</string>
|
||||||
<string name="blogs_title">Blogs</string>
|
|
||||||
<plurals name="blogs_available">
|
|
||||||
<item quantity="one">%1$d blog available from contacts</item>
|
|
||||||
<item quantity="other">%1$d blogs available from contacts</item>
|
|
||||||
</plurals>
|
|
||||||
<string name="no_blogs_available">No blogs available from contacts</string>
|
|
||||||
<string name="subscribed_all">Subscribed, shared with all contacts</string>
|
<string name="subscribed_all">Subscribed, shared with all contacts</string>
|
||||||
<string name="subscribed_some">Subscribed, shared with chosen contacts</string>
|
<string name="subscribed_some">Subscribed, shared with chosen contacts</string>
|
||||||
<string name="not_subscribed">Not subscribed</string>
|
<string name="not_subscribed">Not subscribed</string>
|
||||||
<string name="subscribe_to_this_blog">Subscribe to this blog</string>
|
|
||||||
<string name="create_blog_title">New Blog</string>
|
|
||||||
<string name="choose_blog_name">Choose a name for your blog:</string>
|
|
||||||
<string name="blog_visible_to_all">Share this blog with all contacts</string>
|
|
||||||
<string name="blog_visible_to_some">Share this blog with chosen contacts</string>
|
|
||||||
<string name="not_your_blog">Only the creator of this blog can write posts</string>
|
|
||||||
<string name="ok_button">OK</string>
|
|
||||||
<string name="new_blog_item">New blog\u2026</string>
|
|
||||||
<string name="create_nickname_item">New nickname\u2026</string>
|
<string name="create_nickname_item">New nickname\u2026</string>
|
||||||
<string name="create_identity_title">Create an Identity</string>
|
<string name="create_identity_title">Create an Identity</string>
|
||||||
<string name="create_button">Create</string>
|
<string name="create_button">Create</string>
|
||||||
@@ -98,5 +83,4 @@
|
|||||||
<string name="add_button">Add</string>
|
<string name="add_button">Add</string>
|
||||||
<string name="cancel_button">Cancel</string>
|
<string name="cancel_button">Cancel</string>
|
||||||
<string name="no_groups">You aren\'t subscribed to any groups. Create a group now?</string>
|
<string name="no_groups">You aren\'t subscribed to any groups. Create a group now?</string>
|
||||||
<string name="no_blogs">You don\'t have any blogs. Create a blog now?</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import java.util.logging.Logger;
|
|||||||
import net.sf.briar.R;
|
import net.sf.briar.R;
|
||||||
import net.sf.briar.android.BriarService.BriarBinder;
|
import net.sf.briar.android.BriarService.BriarBinder;
|
||||||
import net.sf.briar.android.BriarService.BriarServiceConnection;
|
import net.sf.briar.android.BriarService.BriarServiceConnection;
|
||||||
import net.sf.briar.android.blogs.BlogListActivity;
|
|
||||||
import net.sf.briar.android.contact.ContactListActivity;
|
import net.sf.briar.android.contact.ContactListActivity;
|
||||||
import net.sf.briar.android.groups.GroupListActivity;
|
import net.sf.briar.android.groups.GroupListActivity;
|
||||||
import net.sf.briar.android.messages.ConversationListActivity;
|
import net.sf.briar.android.messages.ConversationListActivity;
|
||||||
@@ -62,8 +61,8 @@ import com.google.inject.Inject;
|
|||||||
|
|
||||||
public class HomeScreenActivity extends RoboActivity {
|
public class HomeScreenActivity extends RoboActivity {
|
||||||
|
|
||||||
// This build expires on 15 August 2013
|
// This build expires on 30 September 2013
|
||||||
private static final long EXPIRY_DATE = 1376524800 * 1000L;
|
private static final long EXPIRY_DATE = 1380499200 * 1000L;
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(HomeScreenActivity.class.getName());
|
Logger.getLogger(HomeScreenActivity.class.getName());
|
||||||
@@ -346,20 +345,6 @@ public class HomeScreenActivity extends RoboActivity {
|
|||||||
});
|
});
|
||||||
buttons.add(groupsButton);
|
buttons.add(groupsButton);
|
||||||
|
|
||||||
Button blogsButton = new Button(this);
|
|
||||||
blogsButton.setLayoutParams(matchMatch);
|
|
||||||
blogsButton.setBackgroundResource(0);
|
|
||||||
blogsButton.setCompoundDrawablesWithIntrinsicBounds(0,
|
|
||||||
R.drawable.social_blog, 0, 0);
|
|
||||||
blogsButton.setText(R.string.blogs_button);
|
|
||||||
blogsButton.setOnClickListener(new OnClickListener() {
|
|
||||||
public void onClick(View view) {
|
|
||||||
startActivity(new Intent(HomeScreenActivity.this,
|
|
||||||
BlogListActivity.class));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
buttons.add(blogsButton);
|
|
||||||
|
|
||||||
Button syncButton = new Button(this);
|
Button syncButton = new Button(this);
|
||||||
syncButton.setLayoutParams(matchMatch);
|
syncButton.setLayoutParams(matchMatch);
|
||||||
syncButton.setBackgroundResource(0);
|
syncButton.setBackgroundResource(0);
|
||||||
|
|||||||
@@ -1,250 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
|
||||||
import static android.view.View.GONE;
|
|
||||||
import static android.view.View.VISIBLE;
|
|
||||||
import static android.widget.LinearLayout.VERTICAL;
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static net.sf.briar.android.blogs.ReadBlogPostActivity.RESULT_NEXT;
|
|
||||||
import static net.sf.briar.android.blogs.ReadBlogPostActivity.RESULT_PREV;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_WRAP_1;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import net.sf.briar.R;
|
|
||||||
import net.sf.briar.android.AscendingHeaderComparator;
|
|
||||||
import net.sf.briar.android.util.HorizontalBorder;
|
|
||||||
import net.sf.briar.android.util.ListLoadingProgressBar;
|
|
||||||
import net.sf.briar.api.Author;
|
|
||||||
import net.sf.briar.api.android.DatabaseUiExecutor;
|
|
||||||
import net.sf.briar.api.db.DatabaseComponent;
|
|
||||||
import net.sf.briar.api.db.DbException;
|
|
||||||
import net.sf.briar.api.db.GroupMessageHeader;
|
|
||||||
import net.sf.briar.api.db.NoSuchSubscriptionException;
|
|
||||||
import net.sf.briar.api.db.event.DatabaseEvent;
|
|
||||||
import net.sf.briar.api.db.event.DatabaseListener;
|
|
||||||
import net.sf.briar.api.db.event.GroupMessageAddedEvent;
|
|
||||||
import net.sf.briar.api.db.event.MessageExpiredEvent;
|
|
||||||
import net.sf.briar.api.db.event.RatingChangedEvent;
|
|
||||||
import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
|
|
||||||
import net.sf.briar.api.lifecycle.LifecycleManager;
|
|
||||||
import net.sf.briar.api.messaging.GroupId;
|
|
||||||
import roboguice.activity.RoboFragmentActivity;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.AdapterView.OnItemClickListener;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.ListView;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
public class BlogActivity extends RoboFragmentActivity
|
|
||||||
implements DatabaseListener, OnClickListener, OnItemClickListener {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(BlogActivity.class.getName());
|
|
||||||
|
|
||||||
private String groupName = null;
|
|
||||||
private boolean postable = false;
|
|
||||||
private BlogAdapter adapter = null;
|
|
||||||
private ListView list = null;
|
|
||||||
private ListLoadingProgressBar loading = null;
|
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
|
||||||
@Inject private volatile DatabaseComponent db;
|
|
||||||
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
|
||||||
@Inject private volatile LifecycleManager lifecycleManager;
|
|
||||||
private volatile GroupId groupId = null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle state) {
|
|
||||||
super.onCreate(state);
|
|
||||||
|
|
||||||
Intent i = getIntent();
|
|
||||||
byte[] b = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
|
|
||||||
if(b == null) throw new IllegalStateException();
|
|
||||||
groupId = new GroupId(b);
|
|
||||||
groupName = i.getStringExtra("net.sf.briar.GROUP_NAME");
|
|
||||||
if(groupName == null) throw new IllegalStateException();
|
|
||||||
setTitle(groupName);
|
|
||||||
postable = i.getBooleanExtra("net.sf.briar.POSTABLE", false);
|
|
||||||
|
|
||||||
LinearLayout layout = new LinearLayout(this);
|
|
||||||
layout.setLayoutParams(MATCH_MATCH);
|
|
||||||
layout.setOrientation(VERTICAL);
|
|
||||||
layout.setGravity(CENTER_HORIZONTAL);
|
|
||||||
|
|
||||||
adapter = new BlogAdapter(this);
|
|
||||||
list = new ListView(this);
|
|
||||||
// Give me all the width and all the unused height
|
|
||||||
list.setLayoutParams(MATCH_WRAP_1);
|
|
||||||
list.setAdapter(adapter);
|
|
||||||
list.setOnItemClickListener(this);
|
|
||||||
layout.addView(list);
|
|
||||||
|
|
||||||
// Show a progress bar while the list is loading
|
|
||||||
list.setVisibility(GONE);
|
|
||||||
loading = new ListLoadingProgressBar(this);
|
|
||||||
layout.addView(loading);
|
|
||||||
|
|
||||||
layout.addView(new HorizontalBorder(this));
|
|
||||||
|
|
||||||
ImageButton composeButton = new ImageButton(this);
|
|
||||||
composeButton.setBackgroundResource(0);
|
|
||||||
composeButton.setImageResource(R.drawable.content_new_email);
|
|
||||||
composeButton.setOnClickListener(this);
|
|
||||||
layout.addView(composeButton);
|
|
||||||
|
|
||||||
setContentView(layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
db.addListener(this);
|
|
||||||
loadHeaders();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadHeaders() {
|
|
||||||
dbUiExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
lifecycleManager.waitForDatabase();
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
Collection<GroupMessageHeader> headers =
|
|
||||||
db.getGroupMessageHeaders(groupId);
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Load took " + duration + " ms");
|
|
||||||
displayHeaders(headers);
|
|
||||||
} catch(NoSuchSubscriptionException e) {
|
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Subscription removed");
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Interrupted while waiting for database");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayHeaders(final Collection<GroupMessageHeader> headers) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
list.setVisibility(VISIBLE);
|
|
||||||
loading.setVisibility(GONE);
|
|
||||||
adapter.clear();
|
|
||||||
for(GroupMessageHeader h : headers) adapter.add(h);
|
|
||||||
adapter.sort(AscendingHeaderComparator.INSTANCE);
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
selectFirstUnread();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void selectFirstUnread() {
|
|
||||||
int firstUnread = -1, count = adapter.getCount();
|
|
||||||
for(int i = 0; i < count; i++) {
|
|
||||||
if(!adapter.getItem(i).isRead()) {
|
|
||||||
firstUnread = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(firstUnread == -1) list.setSelection(count - 1);
|
|
||||||
else list.setSelection(firstUnread);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityResult(int request, int result, Intent data) {
|
|
||||||
if(result == RESULT_PREV) {
|
|
||||||
int position = request - 1;
|
|
||||||
if(position >= 0 && position < adapter.getCount())
|
|
||||||
displayMessage(position);
|
|
||||||
} else if(result == RESULT_NEXT) {
|
|
||||||
int position = request + 1;
|
|
||||||
if(position >= 0 && position < adapter.getCount())
|
|
||||||
displayMessage(position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
db.removeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void eventOccurred(DatabaseEvent e) {
|
|
||||||
if(e instanceof GroupMessageAddedEvent) {
|
|
||||||
GroupMessageAddedEvent g = (GroupMessageAddedEvent) e;
|
|
||||||
if(g.getGroup().getId().equals(groupId)) {
|
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
|
|
||||||
loadHeaders();
|
|
||||||
}
|
|
||||||
} else if(e instanceof MessageExpiredEvent) {
|
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Message expired, reloading");
|
|
||||||
loadHeaders();
|
|
||||||
} else if(e instanceof RatingChangedEvent) {
|
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Rating changed, reloading");
|
|
||||||
loadHeaders();
|
|
||||||
} else if(e instanceof SubscriptionRemovedEvent) {
|
|
||||||
SubscriptionRemovedEvent s = (SubscriptionRemovedEvent) e;
|
|
||||||
if(s.getGroup().getId().equals(groupId)) {
|
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Subscription removed");
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onClick(View view) {
|
|
||||||
if(postable) {
|
|
||||||
Intent i = new Intent(this, WriteBlogPostActivity.class);
|
|
||||||
i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
|
|
||||||
startActivity(i);
|
|
||||||
} else {
|
|
||||||
NotYourBlogDialog dialog = new NotYourBlogDialog();
|
|
||||||
dialog.show(getSupportFragmentManager(), "NotYourBlogDialog");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position,
|
|
||||||
long id) {
|
|
||||||
displayMessage(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayMessage(int position) {
|
|
||||||
GroupMessageHeader item = adapter.getItem(position);
|
|
||||||
Intent i = new Intent(this, ReadBlogPostActivity.class);
|
|
||||||
i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
|
|
||||||
i.putExtra("net.sf.briar.GROUP_NAME", groupName);
|
|
||||||
i.putExtra("net.sf.briar.POSTABLE", postable);
|
|
||||||
i.putExtra("net.sf.briar.MESSAGE_ID", item.getId().getBytes());
|
|
||||||
Author author = item.getAuthor();
|
|
||||||
if(author != null) {
|
|
||||||
i.putExtra("net.sf.briar.AUTHOR_NAME", author.getName());
|
|
||||||
i.putExtra("net.sf.briar.RATING", item.getRating().toString());
|
|
||||||
}
|
|
||||||
i.putExtra("net.sf.briar.CONTENT_TYPE", item.getContentType());
|
|
||||||
i.putExtra("net.sf.briar.TIMESTAMP", item.getTimestamp());
|
|
||||||
startActivityForResult(i, position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import static android.graphics.Typeface.BOLD;
|
|
||||||
import static android.view.Gravity.CENTER_VERTICAL;
|
|
||||||
import static android.widget.LinearLayout.HORIZONTAL;
|
|
||||||
import static android.widget.LinearLayout.VERTICAL;
|
|
||||||
import static java.text.DateFormat.SHORT;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP_1;
|
|
||||||
import static net.sf.briar.api.messaging.Rating.BAD;
|
|
||||||
import static net.sf.briar.api.messaging.Rating.GOOD;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import net.sf.briar.R;
|
|
||||||
import net.sf.briar.android.util.HorizontalSpace;
|
|
||||||
import net.sf.briar.api.Author;
|
|
||||||
import net.sf.briar.api.db.GroupMessageHeader;
|
|
||||||
import net.sf.briar.api.messaging.Rating;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
class BlogAdapter extends ArrayAdapter<GroupMessageHeader> {
|
|
||||||
|
|
||||||
BlogAdapter(Context ctx) {
|
|
||||||
super(ctx, android.R.layout.simple_expandable_list_item_1,
|
|
||||||
new ArrayList<GroupMessageHeader>());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
GroupMessageHeader item = getItem(position);
|
|
||||||
Context ctx = getContext();
|
|
||||||
Resources res = ctx.getResources();
|
|
||||||
|
|
||||||
LinearLayout layout = new LinearLayout(ctx);
|
|
||||||
layout.setOrientation(HORIZONTAL);
|
|
||||||
if(!item.isRead())
|
|
||||||
layout.setBackgroundColor(res.getColor(R.color.unread_background));
|
|
||||||
|
|
||||||
LinearLayout innerLayout = new LinearLayout(ctx);
|
|
||||||
// Give me all the unused width
|
|
||||||
innerLayout.setLayoutParams(WRAP_WRAP_1);
|
|
||||||
innerLayout.setOrientation(VERTICAL);
|
|
||||||
|
|
||||||
LinearLayout authorLayout = new LinearLayout(ctx);
|
|
||||||
authorLayout.setOrientation(HORIZONTAL);
|
|
||||||
authorLayout.setGravity(CENTER_VERTICAL);
|
|
||||||
|
|
||||||
ImageView thumb = new ImageView(ctx);
|
|
||||||
thumb.setPadding(5, 5, 5, 5);
|
|
||||||
Rating rating = item.getRating();
|
|
||||||
if(rating == GOOD) thumb.setImageResource(R.drawable.rating_good);
|
|
||||||
else if(rating == BAD) thumb.setImageResource(R.drawable.rating_bad);
|
|
||||||
else thumb.setImageResource(R.drawable.rating_unrated);
|
|
||||||
authorLayout.addView(thumb);
|
|
||||||
|
|
||||||
TextView name = new TextView(ctx);
|
|
||||||
// Give me all the unused width
|
|
||||||
name.setLayoutParams(WRAP_WRAP_1);
|
|
||||||
name.setTextSize(18);
|
|
||||||
name.setMaxLines(1);
|
|
||||||
name.setPadding(0, 10, 10, 10);
|
|
||||||
Author author = item.getAuthor();
|
|
||||||
if(author == null) {
|
|
||||||
name.setTextColor(res.getColor(R.color.anonymous_author));
|
|
||||||
name.setText(R.string.anonymous);
|
|
||||||
} else {
|
|
||||||
name.setText(author.getName());
|
|
||||||
}
|
|
||||||
authorLayout.addView(name);
|
|
||||||
innerLayout.addView(authorLayout);
|
|
||||||
|
|
||||||
if(item.getContentType().equals("text/plain")) {
|
|
||||||
TextView subject = new TextView(ctx);
|
|
||||||
subject.setTextSize(14);
|
|
||||||
subject.setMaxLines(2);
|
|
||||||
subject.setPadding(10, 0, 10, 10);
|
|
||||||
if(!item.isRead()) subject.setTypeface(null, BOLD);
|
|
||||||
String s = item.getSubject();
|
|
||||||
subject.setText(s == null ? "" : s);
|
|
||||||
innerLayout.addView(subject);
|
|
||||||
} else {
|
|
||||||
LinearLayout attachmentLayout = new LinearLayout(ctx);
|
|
||||||
attachmentLayout.setOrientation(HORIZONTAL);
|
|
||||||
ImageView attachment = new ImageView(ctx);
|
|
||||||
attachment.setPadding(5, 0, 5, 5);
|
|
||||||
attachment.setImageResource(R.drawable.content_attachment);
|
|
||||||
attachmentLayout.addView(attachment);
|
|
||||||
attachmentLayout.addView(new HorizontalSpace(ctx));
|
|
||||||
innerLayout.addView(attachmentLayout);
|
|
||||||
}
|
|
||||||
layout.addView(innerLayout);
|
|
||||||
|
|
||||||
TextView date = new TextView(ctx);
|
|
||||||
date.setTextSize(14);
|
|
||||||
date.setPadding(0, 10, 10, 10);
|
|
||||||
long then = item.getTimestamp(), now = System.currentTimeMillis();
|
|
||||||
date.setText(DateUtils.formatSameDayTime(then, now, SHORT, SHORT));
|
|
||||||
layout.addView(date);
|
|
||||||
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,412 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import static android.view.Gravity.CENTER;
|
|
||||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
|
||||||
import static android.view.View.GONE;
|
|
||||||
import static android.view.View.VISIBLE;
|
|
||||||
import static android.widget.LinearLayout.HORIZONTAL;
|
|
||||||
import static android.widget.LinearLayout.VERTICAL;
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static net.sf.briar.android.blogs.BlogListItem.MANAGE;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_WRAP;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_WRAP_1;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import net.sf.briar.R;
|
|
||||||
import net.sf.briar.android.util.HorizontalBorder;
|
|
||||||
import net.sf.briar.android.util.HorizontalSpace;
|
|
||||||
import net.sf.briar.android.util.ListLoadingProgressBar;
|
|
||||||
import net.sf.briar.api.android.DatabaseUiExecutor;
|
|
||||||
import net.sf.briar.api.db.DatabaseComponent;
|
|
||||||
import net.sf.briar.api.db.DbException;
|
|
||||||
import net.sf.briar.api.db.GroupMessageHeader;
|
|
||||||
import net.sf.briar.api.db.NoSuchSubscriptionException;
|
|
||||||
import net.sf.briar.api.db.event.DatabaseEvent;
|
|
||||||
import net.sf.briar.api.db.event.DatabaseListener;
|
|
||||||
import net.sf.briar.api.db.event.GroupMessageAddedEvent;
|
|
||||||
import net.sf.briar.api.db.event.MessageExpiredEvent;
|
|
||||||
import net.sf.briar.api.db.event.RemoteSubscriptionsUpdatedEvent;
|
|
||||||
import net.sf.briar.api.db.event.SubscriptionAddedEvent;
|
|
||||||
import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
|
|
||||||
import net.sf.briar.api.lifecycle.LifecycleManager;
|
|
||||||
import net.sf.briar.api.messaging.Group;
|
|
||||||
import net.sf.briar.api.messaging.GroupId;
|
|
||||||
import net.sf.briar.api.messaging.GroupStatus;
|
|
||||||
import roboguice.activity.RoboFragmentActivity;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.AdapterView.OnItemClickListener;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.ListView;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
public class BlogListActivity extends RoboFragmentActivity
|
|
||||||
implements DatabaseListener, OnClickListener, NoBlogsDialog.Listener,
|
|
||||||
OnItemClickListener {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(BlogListActivity.class.getName());
|
|
||||||
|
|
||||||
private BlogListAdapter adapter = null;
|
|
||||||
private ListView list = null;
|
|
||||||
private ListLoadingProgressBar loading = null;
|
|
||||||
private ImageButton newBlogButton = null, composeButton = null;
|
|
||||||
private ImageButton manageBlogsButton = null;
|
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
|
||||||
@Inject private volatile DatabaseComponent db;
|
|
||||||
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
|
||||||
@Inject private volatile LifecycleManager lifecycleManager;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle state) {
|
|
||||||
super.onCreate(state);
|
|
||||||
LinearLayout layout = new LinearLayout(this);
|
|
||||||
layout.setLayoutParams(MATCH_MATCH);
|
|
||||||
layout.setOrientation(VERTICAL);
|
|
||||||
layout.setGravity(CENTER_HORIZONTAL);
|
|
||||||
|
|
||||||
adapter = new BlogListAdapter(this);
|
|
||||||
list = new ListView(this);
|
|
||||||
// Give me all the width and all the unused height
|
|
||||||
list.setLayoutParams(MATCH_WRAP_1);
|
|
||||||
list.setAdapter(adapter);
|
|
||||||
list.setOnItemClickListener(this);
|
|
||||||
layout.addView(list);
|
|
||||||
|
|
||||||
// Show a progress bar while the list is loading
|
|
||||||
list.setVisibility(GONE);
|
|
||||||
loading = new ListLoadingProgressBar(this);
|
|
||||||
layout.addView(loading);
|
|
||||||
|
|
||||||
layout.addView(new HorizontalBorder(this));
|
|
||||||
|
|
||||||
LinearLayout footer = new LinearLayout(this);
|
|
||||||
footer.setLayoutParams(MATCH_WRAP);
|
|
||||||
footer.setOrientation(HORIZONTAL);
|
|
||||||
footer.setGravity(CENTER);
|
|
||||||
footer.addView(new HorizontalSpace(this));
|
|
||||||
|
|
||||||
newBlogButton = new ImageButton(this);
|
|
||||||
newBlogButton.setBackgroundResource(0);
|
|
||||||
newBlogButton.setImageResource(R.drawable.social_new_blog);
|
|
||||||
newBlogButton.setOnClickListener(this);
|
|
||||||
footer.addView(newBlogButton);
|
|
||||||
footer.addView(new HorizontalSpace(this));
|
|
||||||
|
|
||||||
composeButton = new ImageButton(this);
|
|
||||||
composeButton.setBackgroundResource(0);
|
|
||||||
composeButton.setImageResource(R.drawable.content_new_email);
|
|
||||||
composeButton.setOnClickListener(this);
|
|
||||||
footer.addView(composeButton);
|
|
||||||
footer.addView(new HorizontalSpace(this));
|
|
||||||
|
|
||||||
manageBlogsButton = new ImageButton(this);
|
|
||||||
manageBlogsButton.setBackgroundResource(0);
|
|
||||||
manageBlogsButton.setImageResource(R.drawable.action_settings);
|
|
||||||
manageBlogsButton.setOnClickListener(this);
|
|
||||||
footer.addView(manageBlogsButton);
|
|
||||||
footer.addView(new HorizontalSpace(this));
|
|
||||||
layout.addView(footer);
|
|
||||||
|
|
||||||
setContentView(layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
db.addListener(this);
|
|
||||||
loadHeaders();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadHeaders() {
|
|
||||||
clearHeaders();
|
|
||||||
dbUiExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
lifecycleManager.waitForDatabase();
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
Set<GroupId> local = new HashSet<GroupId>();
|
|
||||||
for(Group g : db.getLocalGroups()) local.add(g.getId());
|
|
||||||
int available = 0;
|
|
||||||
for(GroupStatus s : db.getAvailableGroups()) {
|
|
||||||
Group g = s.getGroup();
|
|
||||||
if(!g.isRestricted()) continue;
|
|
||||||
if(s.isSubscribed()) {
|
|
||||||
boolean postable = local.contains(g.getId());
|
|
||||||
try {
|
|
||||||
Collection<GroupMessageHeader> headers =
|
|
||||||
db.getGroupMessageHeaders(g.getId());
|
|
||||||
displayHeaders(g, postable, headers);
|
|
||||||
} catch(NoSuchSubscriptionException e) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Subscription removed");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
available++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Full load took " + duration + " ms");
|
|
||||||
displayAvailable(available);
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Interrupted while waiting for database");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearHeaders() {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
list.setVisibility(GONE);
|
|
||||||
loading.setVisibility(VISIBLE);
|
|
||||||
adapter.clear();
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayHeaders(final Group g, final boolean postable,
|
|
||||||
final Collection<GroupMessageHeader> headers) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
list.setVisibility(VISIBLE);
|
|
||||||
loading.setVisibility(GONE);
|
|
||||||
// Remove the old item, if any
|
|
||||||
BlogListItem item = findGroup(g.getId());
|
|
||||||
if(item != null) adapter.remove(item);
|
|
||||||
// Add a new item
|
|
||||||
adapter.add(new BlogListItem(g, postable, headers));
|
|
||||||
adapter.sort(ItemComparator.INSTANCE);
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
selectFirstUnread();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayAvailable(final int available) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
list.setVisibility(VISIBLE);
|
|
||||||
loading.setVisibility(GONE);
|
|
||||||
adapter.setAvailable(available);
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private BlogListItem findGroup(GroupId g) {
|
|
||||||
int count = adapter.getCount();
|
|
||||||
for(int i = 0; i < count; i++) {
|
|
||||||
BlogListItem item = adapter.getItem(i);
|
|
||||||
if(item == MANAGE) continue;
|
|
||||||
if(item.getGroup().getId().equals(g)) return item;
|
|
||||||
}
|
|
||||||
return null; // Not found
|
|
||||||
}
|
|
||||||
|
|
||||||
private void selectFirstUnread() {
|
|
||||||
int firstUnread = -1, count = adapter.getCount();
|
|
||||||
for(int i = 0; i < count; i++) {
|
|
||||||
BlogListItem item = adapter.getItem(i);
|
|
||||||
if(item == MANAGE) continue;
|
|
||||||
if(item.getUnreadCount() > 0) {
|
|
||||||
firstUnread = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(firstUnread == -1) list.setSelection(count - 1);
|
|
||||||
else list.setSelection(firstUnread);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
db.removeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void eventOccurred(DatabaseEvent e) {
|
|
||||||
if(e instanceof GroupMessageAddedEvent) {
|
|
||||||
Group g = ((GroupMessageAddedEvent) e).getGroup();
|
|
||||||
if(g.isRestricted()) {
|
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
|
|
||||||
loadHeaders(g);
|
|
||||||
}
|
|
||||||
} else if(e instanceof MessageExpiredEvent) {
|
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Message expired, reloading");
|
|
||||||
loadHeaders();
|
|
||||||
} else if(e instanceof RemoteSubscriptionsUpdatedEvent) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Remote subscriptions changed, reloading");
|
|
||||||
loadAvailable();
|
|
||||||
} else if(e instanceof SubscriptionAddedEvent) {
|
|
||||||
Group g = ((SubscriptionAddedEvent) e).getGroup();
|
|
||||||
if(g.isRestricted()) {
|
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Group added, reloading");
|
|
||||||
loadHeaders();
|
|
||||||
}
|
|
||||||
} else if(e instanceof SubscriptionRemovedEvent) {
|
|
||||||
Group g = ((SubscriptionRemovedEvent) e).getGroup();
|
|
||||||
if(g.isRestricted()) {
|
|
||||||
// Reload the group, expecting NoSuchSubscriptionException
|
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
|
|
||||||
loadHeaders(g);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadHeaders(final Group g) {
|
|
||||||
dbUiExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
lifecycleManager.waitForDatabase();
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
Collection<GroupMessageHeader> headers =
|
|
||||||
db.getGroupMessageHeaders(g.getId());
|
|
||||||
boolean postable = db.getLocalGroups().contains(g);
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Partial load took " + duration + " ms");
|
|
||||||
displayHeaders(g, postable, headers);
|
|
||||||
} catch(NoSuchSubscriptionException e) {
|
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Subscription removed");
|
|
||||||
removeGroup(g.getId());
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Interrupted while waiting for database");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeGroup(final GroupId g) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
BlogListItem item = findGroup(g);
|
|
||||||
if(item != null) {
|
|
||||||
adapter.remove(item);
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
selectFirstUnread();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadAvailable() {
|
|
||||||
dbUiExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
lifecycleManager.waitForDatabase();
|
|
||||||
int available = 0;
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
for(GroupStatus s : db.getAvailableGroups()) {
|
|
||||||
if(s.getGroup().isRestricted() && !s.isSubscribed())
|
|
||||||
available++;
|
|
||||||
}
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Loading available took " + duration + " ms");
|
|
||||||
displayAvailable(available);
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Interrupted while waiting for database");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onClick(View view) {
|
|
||||||
if(view == newBlogButton) {
|
|
||||||
startActivity(new Intent(this, CreateBlogActivity.class));
|
|
||||||
} else if(view == composeButton) {
|
|
||||||
if(countPostableGroups() == 0) {
|
|
||||||
NoBlogsDialog dialog = new NoBlogsDialog();
|
|
||||||
dialog.setListener(this);
|
|
||||||
dialog.show(getSupportFragmentManager(), "NoBlogsDialog");
|
|
||||||
} else {
|
|
||||||
startActivity(new Intent(this, WriteBlogPostActivity.class));
|
|
||||||
}
|
|
||||||
} else if(view == manageBlogsButton) {
|
|
||||||
startActivity(new Intent(this, ManageBlogsActivity.class));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int countPostableGroups() {
|
|
||||||
int postable = 0, count = adapter.getCount();
|
|
||||||
for(int i = 0; i < count; i++) {
|
|
||||||
BlogListItem item = adapter.getItem(i);
|
|
||||||
if(item == MANAGE) continue;
|
|
||||||
if(item.isPostable()) postable++;
|
|
||||||
}
|
|
||||||
return postable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void blogCreationSelected() {
|
|
||||||
startActivity(new Intent(this, CreateBlogActivity.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void blogCreationCancelled() {}
|
|
||||||
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position,
|
|
||||||
long id) {
|
|
||||||
BlogListItem item = adapter.getItem(position);
|
|
||||||
if(item == MANAGE) {
|
|
||||||
startActivity(new Intent(this, ManageBlogsActivity.class));
|
|
||||||
} else {
|
|
||||||
Intent i = new Intent(this, BlogActivity.class);
|
|
||||||
i.putExtra("net.sf.briar.GROUP_ID",
|
|
||||||
item.getGroup().getId().getBytes());
|
|
||||||
i.putExtra("net.sf.briar.GROUP_NAME", item.getGroup().getName());
|
|
||||||
i.putExtra("net.sf.briar.POSTABLE", item.isPostable());
|
|
||||||
startActivity(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ItemComparator implements Comparator<BlogListItem> {
|
|
||||||
|
|
||||||
private static final ItemComparator INSTANCE = new ItemComparator();
|
|
||||||
|
|
||||||
public int compare(BlogListItem a, BlogListItem b) {
|
|
||||||
if(a == b) return 0;
|
|
||||||
// The manage blogs item comes last
|
|
||||||
if(a == MANAGE) return 1;
|
|
||||||
if(b == MANAGE) return -1;
|
|
||||||
// The item 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 group name
|
|
||||||
String aName = a.getGroup().getName();
|
|
||||||
String bName = b.getGroup().getName();
|
|
||||||
return String.CASE_INSENSITIVE_ORDER.compare(aName, bName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import static android.graphics.Typeface.BOLD;
|
|
||||||
import static android.view.Gravity.CENTER;
|
|
||||||
import static android.widget.LinearLayout.HORIZONTAL;
|
|
||||||
import static android.widget.LinearLayout.VERTICAL;
|
|
||||||
import static java.text.DateFormat.SHORT;
|
|
||||||
import static net.sf.briar.android.blogs.BlogListItem.MANAGE;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP_1;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.sf.briar.R;
|
|
||||||
import net.sf.briar.android.util.HorizontalSpace;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.BaseAdapter;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
class BlogListAdapter extends BaseAdapter {
|
|
||||||
|
|
||||||
private final Context ctx;
|
|
||||||
private final List<BlogListItem> list = new ArrayList<BlogListItem>();
|
|
||||||
private int available = 0;
|
|
||||||
|
|
||||||
BlogListAdapter(Context ctx) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAvailable(int available) {
|
|
||||||
this.available = available;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(BlogListItem item) {
|
|
||||||
list.add(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
list.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCount() {
|
|
||||||
return available == 0 ? list.size() : list.size() + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlogListItem getItem(int position) {
|
|
||||||
return position == list.size() ? MANAGE : list.get(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return android.R.layout.simple_expandable_list_item_1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
BlogListItem item = getItem(position);
|
|
||||||
Resources res = ctx.getResources();
|
|
||||||
|
|
||||||
if(item == MANAGE) {
|
|
||||||
TextView manage = new TextView(ctx);
|
|
||||||
manage.setGravity(CENTER);
|
|
||||||
manage.setTextSize(18);
|
|
||||||
manage.setPadding(10, 10, 10, 10);
|
|
||||||
String format = res.getQuantityString(R.plurals.blogs_available,
|
|
||||||
available);
|
|
||||||
manage.setText(String.format(format, available));
|
|
||||||
return manage;
|
|
||||||
}
|
|
||||||
|
|
||||||
LinearLayout layout = new LinearLayout(ctx);
|
|
||||||
layout.setOrientation(HORIZONTAL);
|
|
||||||
if(item.getUnreadCount() > 0)
|
|
||||||
layout.setBackgroundColor(res.getColor(R.color.unread_background));
|
|
||||||
|
|
||||||
LinearLayout innerLayout = new LinearLayout(ctx);
|
|
||||||
// Give me all the unused width
|
|
||||||
innerLayout.setLayoutParams(WRAP_WRAP_1);
|
|
||||||
innerLayout.setOrientation(VERTICAL);
|
|
||||||
|
|
||||||
TextView name = new TextView(ctx);
|
|
||||||
name.setTextSize(18);
|
|
||||||
name.setMaxLines(1);
|
|
||||||
name.setPadding(10, 10, 10, 10);
|
|
||||||
int unread = item.getUnreadCount();
|
|
||||||
String groupName = item.getGroup().getName();
|
|
||||||
if(unread > 0) name.setText(groupName + " (" + unread + ")");
|
|
||||||
else name.setText(groupName);
|
|
||||||
innerLayout.addView(name);
|
|
||||||
|
|
||||||
if(item.isEmpty()) {
|
|
||||||
TextView noPosts = new TextView(ctx);
|
|
||||||
noPosts.setTextSize(14);
|
|
||||||
noPosts.setPadding(10, 0, 10, 10);
|
|
||||||
noPosts.setTextColor(res.getColor(R.color.no_posts));
|
|
||||||
noPosts.setText(R.string.no_posts);
|
|
||||||
innerLayout.addView(noPosts);
|
|
||||||
layout.addView(innerLayout);
|
|
||||||
} else {
|
|
||||||
if(item.getContentType().equals("text/plain")) {
|
|
||||||
TextView subject = new TextView(ctx);
|
|
||||||
subject.setTextSize(14);
|
|
||||||
subject.setMaxLines(2);
|
|
||||||
subject.setPadding(10, 0, 10, 10);
|
|
||||||
if(item.getUnreadCount() > 0) subject.setTypeface(null, BOLD);
|
|
||||||
String s = item.getSubject();
|
|
||||||
subject.setText(s == null ? "" : s);
|
|
||||||
innerLayout.addView(subject);
|
|
||||||
} else {
|
|
||||||
LinearLayout attachmentLayout = new LinearLayout(ctx);
|
|
||||||
attachmentLayout.setOrientation(HORIZONTAL);
|
|
||||||
ImageView attachment = new ImageView(ctx);
|
|
||||||
attachment.setPadding(5, 0, 5, 5);
|
|
||||||
attachment.setImageResource(R.drawable.content_attachment);
|
|
||||||
attachmentLayout.addView(attachment);
|
|
||||||
attachmentLayout.addView(new HorizontalSpace(ctx));
|
|
||||||
innerLayout.addView(attachmentLayout);
|
|
||||||
}
|
|
||||||
layout.addView(innerLayout);
|
|
||||||
|
|
||||||
TextView date = new TextView(ctx);
|
|
||||||
date.setTextSize(14);
|
|
||||||
date.setPadding(0, 10, 10, 10);
|
|
||||||
long then = item.getTimestamp(), now = System.currentTimeMillis();
|
|
||||||
date.setText(DateUtils.formatSameDayTime(then, now, SHORT, SHORT));
|
|
||||||
layout.addView(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return list.isEmpty() && available == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove(BlogListItem item) {
|
|
||||||
list.remove(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sort(Comparator<BlogListItem> comparator) {
|
|
||||||
Collections.sort(list, comparator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.sf.briar.android.DescendingHeaderComparator;
|
|
||||||
import net.sf.briar.api.Author;
|
|
||||||
import net.sf.briar.api.db.GroupMessageHeader;
|
|
||||||
import net.sf.briar.api.messaging.Group;
|
|
||||||
|
|
||||||
class BlogListItem {
|
|
||||||
|
|
||||||
static final BlogListItem MANAGE = new BlogListItem(null, false,
|
|
||||||
Collections.<GroupMessageHeader>emptyList());
|
|
||||||
|
|
||||||
private final Group group;
|
|
||||||
private final boolean postable, empty;
|
|
||||||
private final String authorName, contentType, subject;
|
|
||||||
private final long timestamp;
|
|
||||||
private final int unread;
|
|
||||||
|
|
||||||
BlogListItem(Group group, boolean postable,
|
|
||||||
Collection<GroupMessageHeader> headers) {
|
|
||||||
this.group = group;
|
|
||||||
this.postable = postable;
|
|
||||||
empty = headers.isEmpty();
|
|
||||||
if(empty) {
|
|
||||||
authorName = null;
|
|
||||||
contentType = null;
|
|
||||||
subject = null;
|
|
||||||
timestamp = 0;
|
|
||||||
unread = 0;
|
|
||||||
} else {
|
|
||||||
List<GroupMessageHeader> list =
|
|
||||||
new ArrayList<GroupMessageHeader>(headers);
|
|
||||||
Collections.sort(list, DescendingHeaderComparator.INSTANCE);
|
|
||||||
GroupMessageHeader newest = list.get(0);
|
|
||||||
Author a = newest.getAuthor();
|
|
||||||
if(a == null) authorName = null;
|
|
||||||
else authorName = a.getName();
|
|
||||||
contentType = newest.getContentType();
|
|
||||||
subject = newest.getSubject();
|
|
||||||
timestamp = newest.getTimestamp();
|
|
||||||
int unread = 0;
|
|
||||||
for(GroupMessageHeader h : list) if(!h.isRead()) unread++;
|
|
||||||
this.unread = unread;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Group getGroup() {
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isPostable() {
|
|
||||||
return postable;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isEmpty() {
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getAuthorName() {
|
|
||||||
return authorName;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getContentType() {
|
|
||||||
return contentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getSubject() {
|
|
||||||
return subject;
|
|
||||||
}
|
|
||||||
|
|
||||||
long getTimestamp() {
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getUnreadCount() {
|
|
||||||
return unread;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
|
||||||
import static android.view.View.GONE;
|
|
||||||
import static android.view.View.VISIBLE;
|
|
||||||
import static android.widget.LinearLayout.VERTICAL;
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import net.sf.briar.R;
|
|
||||||
import net.sf.briar.android.contact.SelectContactsDialog;
|
|
||||||
import net.sf.briar.android.invitation.AddContactActivity;
|
|
||||||
import net.sf.briar.android.messages.NoContactsDialog;
|
|
||||||
import net.sf.briar.api.Contact;
|
|
||||||
import net.sf.briar.api.ContactId;
|
|
||||||
import net.sf.briar.api.android.DatabaseUiExecutor;
|
|
||||||
import net.sf.briar.api.db.DatabaseComponent;
|
|
||||||
import net.sf.briar.api.db.DbException;
|
|
||||||
import net.sf.briar.api.lifecycle.LifecycleManager;
|
|
||||||
import net.sf.briar.api.messaging.Group;
|
|
||||||
import net.sf.briar.api.messaging.GroupId;
|
|
||||||
import roboguice.activity.RoboFragmentActivity;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.app.FragmentManager;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.ProgressBar;
|
|
||||||
import android.widget.RadioButton;
|
|
||||||
import android.widget.RadioGroup;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
public class ConfigureBlogActivity extends RoboFragmentActivity
|
|
||||||
implements OnClickListener, NoContactsDialog.Listener,
|
|
||||||
SelectContactsDialog.Listener {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(ConfigureBlogActivity.class.getName());
|
|
||||||
|
|
||||||
private boolean subscribed = false;
|
|
||||||
private CheckBox subscribeCheckBox = null;
|
|
||||||
private RadioGroup radioGroup = null;
|
|
||||||
private RadioButton visibleToAll = null, visibleToSome = null;
|
|
||||||
private Button doneButton = null;
|
|
||||||
private ProgressBar progress = null;
|
|
||||||
private NoContactsDialog noContactsDialog = null;
|
|
||||||
private SelectContactsDialog selectContactsDialog = null;
|
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
|
||||||
@Inject private volatile DatabaseComponent db;
|
|
||||||
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
|
||||||
@Inject private volatile LifecycleManager lifecycleManager;
|
|
||||||
private volatile Group group = null;
|
|
||||||
private volatile Collection<ContactId> selected = Collections.emptyList();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle state) {
|
|
||||||
super.onCreate(state);
|
|
||||||
|
|
||||||
Intent i = getIntent();
|
|
||||||
byte[] b = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
|
|
||||||
if(b == null) throw new IllegalStateException();
|
|
||||||
GroupId id = new GroupId(b);
|
|
||||||
String name = i.getStringExtra("net.sf.briar.GROUP_NAME");
|
|
||||||
if(name == null) throw new IllegalStateException();
|
|
||||||
setTitle(name);
|
|
||||||
byte[] publicKey = i.getByteArrayExtra("net.sf.briar.PUBLIC_KEY");
|
|
||||||
if(publicKey == null) throw new IllegalStateException();
|
|
||||||
group = new Group(id, name, publicKey);
|
|
||||||
subscribed = i.getBooleanExtra("net.sf.briar.SUBSCRIBED", false);
|
|
||||||
boolean all = i.getBooleanExtra("net.sf.briar.VISIBLE_TO_ALL", false);
|
|
||||||
|
|
||||||
LinearLayout layout = new LinearLayout(this);
|
|
||||||
layout.setLayoutParams(MATCH_MATCH);
|
|
||||||
layout.setOrientation(VERTICAL);
|
|
||||||
layout.setGravity(CENTER_HORIZONTAL);
|
|
||||||
|
|
||||||
subscribeCheckBox = new CheckBox(this);
|
|
||||||
subscribeCheckBox.setId(1);
|
|
||||||
subscribeCheckBox.setText(R.string.subscribe_to_this_blog);
|
|
||||||
subscribeCheckBox.setChecked(subscribed);
|
|
||||||
subscribeCheckBox.setOnClickListener(this);
|
|
||||||
layout.addView(subscribeCheckBox);
|
|
||||||
|
|
||||||
radioGroup = new RadioGroup(this);
|
|
||||||
radioGroup.setOrientation(VERTICAL);
|
|
||||||
|
|
||||||
visibleToAll = new RadioButton(this);
|
|
||||||
visibleToAll.setId(2);
|
|
||||||
visibleToAll.setText(R.string.blog_visible_to_all);
|
|
||||||
visibleToAll.setEnabled(subscribed);
|
|
||||||
visibleToAll.setOnClickListener(this);
|
|
||||||
radioGroup.addView(visibleToAll);
|
|
||||||
|
|
||||||
visibleToSome = new RadioButton(this);
|
|
||||||
visibleToSome.setId(3);
|
|
||||||
visibleToSome.setText(R.string.blog_visible_to_some);
|
|
||||||
visibleToSome.setEnabled(subscribed);
|
|
||||||
visibleToSome.setOnClickListener(this);
|
|
||||||
radioGroup.addView(visibleToSome);
|
|
||||||
|
|
||||||
if(!subscribed || all) radioGroup.check(visibleToAll.getId());
|
|
||||||
else radioGroup.check(visibleToSome.getId());
|
|
||||||
layout.addView(radioGroup);
|
|
||||||
|
|
||||||
doneButton = new Button(this);
|
|
||||||
doneButton.setLayoutParams(WRAP_WRAP);
|
|
||||||
doneButton.setText(R.string.done_button);
|
|
||||||
doneButton.setOnClickListener(this);
|
|
||||||
layout.addView(doneButton);
|
|
||||||
|
|
||||||
progress = new ProgressBar(this);
|
|
||||||
progress.setLayoutParams(WRAP_WRAP);
|
|
||||||
progress.setIndeterminate(true);
|
|
||||||
progress.setVisibility(GONE);
|
|
||||||
layout.addView(progress);
|
|
||||||
|
|
||||||
setContentView(layout);
|
|
||||||
|
|
||||||
FragmentManager fm = getSupportFragmentManager();
|
|
||||||
Fragment f = fm.findFragmentByTag("NoContactsDialog");
|
|
||||||
if(f == null) noContactsDialog = new NoContactsDialog();
|
|
||||||
else noContactsDialog = (NoContactsDialog) f;
|
|
||||||
noContactsDialog.setListener(this);
|
|
||||||
|
|
||||||
f = fm.findFragmentByTag("SelectContactsDialog");
|
|
||||||
if(f == null) selectContactsDialog = new SelectContactsDialog();
|
|
||||||
else selectContactsDialog = (SelectContactsDialog) f;
|
|
||||||
selectContactsDialog.setListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onClick(View view) {
|
|
||||||
if(view == subscribeCheckBox) {
|
|
||||||
boolean subscribe = subscribeCheckBox.isChecked();
|
|
||||||
visibleToAll.setEnabled(subscribe);
|
|
||||||
visibleToSome.setEnabled(subscribe);
|
|
||||||
} else if(view == visibleToSome) {
|
|
||||||
loadContacts();
|
|
||||||
} else if(view == doneButton) {
|
|
||||||
boolean subscribe = subscribeCheckBox.isChecked();
|
|
||||||
boolean all = visibleToAll.isChecked();
|
|
||||||
Collection<ContactId> visible =
|
|
||||||
Collections.unmodifiableCollection(selected);
|
|
||||||
// Replace the button with a progress bar
|
|
||||||
doneButton.setVisibility(GONE);
|
|
||||||
progress.setVisibility(VISIBLE);
|
|
||||||
// Update the blog in a background thread
|
|
||||||
if(subscribe || subscribed)
|
|
||||||
updateGroup(subscribe, subscribed, all, visible);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadContacts() {
|
|
||||||
dbUiExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
lifecycleManager.waitForDatabase();
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
Collection<Contact> contacts = db.getContacts();
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Load took " + duration + " ms");
|
|
||||||
displayContacts(contacts);
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Interrupted while waiting for database");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayContacts(final Collection<Contact> contacts) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
FragmentManager fm = getSupportFragmentManager();
|
|
||||||
if(contacts.isEmpty()) {
|
|
||||||
noContactsDialog.show(fm, "NoContactsDialog");
|
|
||||||
} else {
|
|
||||||
selectContactsDialog.setContacts(contacts);
|
|
||||||
selectContactsDialog.show(fm, "SelectContactsDialog");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateGroup(final boolean subscribe,
|
|
||||||
final boolean wasSubscribed, final boolean all,
|
|
||||||
final Collection<ContactId> visible) {
|
|
||||||
dbUiExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
lifecycleManager.waitForDatabase();
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
if(subscribe) {
|
|
||||||
if(!wasSubscribed) db.subscribe(group);
|
|
||||||
db.setVisibleToAll(group.getId(), all);
|
|
||||||
if(!all) db.setVisibility(group.getId(), visible);
|
|
||||||
} else if(wasSubscribed) {
|
|
||||||
db.unsubscribe(group);
|
|
||||||
}
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Update took " + duration + " ms");
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Interrupted while waiting for database");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void contactCreationSelected() {
|
|
||||||
startActivity(new Intent(this, AddContactActivity.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void contactCreationCancelled() {}
|
|
||||||
|
|
||||||
public void contactsSelected(Collection<ContactId> selected) {
|
|
||||||
this.selected = selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void contactSelectionCancelled() {}
|
|
||||||
}
|
|
||||||
@@ -1,290 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import static android.text.InputType.TYPE_CLASS_TEXT;
|
|
||||||
import static android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
|
|
||||||
import static android.view.Gravity.CENTER;
|
|
||||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
|
||||||
import static android.view.View.GONE;
|
|
||||||
import static android.view.View.VISIBLE;
|
|
||||||
import static android.view.inputmethod.InputMethodManager.HIDE_IMPLICIT_ONLY;
|
|
||||||
import static android.widget.LinearLayout.VERTICAL;
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import net.sf.briar.R;
|
|
||||||
import net.sf.briar.android.contact.SelectContactsDialog;
|
|
||||||
import net.sf.briar.android.invitation.AddContactActivity;
|
|
||||||
import net.sf.briar.android.messages.NoContactsDialog;
|
|
||||||
import net.sf.briar.api.Contact;
|
|
||||||
import net.sf.briar.api.ContactId;
|
|
||||||
import net.sf.briar.api.android.DatabaseUiExecutor;
|
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
|
||||||
import net.sf.briar.api.crypto.CryptoExecutor;
|
|
||||||
import net.sf.briar.api.crypto.KeyPair;
|
|
||||||
import net.sf.briar.api.db.DatabaseComponent;
|
|
||||||
import net.sf.briar.api.db.DbException;
|
|
||||||
import net.sf.briar.api.lifecycle.LifecycleManager;
|
|
||||||
import net.sf.briar.api.messaging.GroupFactory;
|
|
||||||
import net.sf.briar.api.messaging.LocalGroup;
|
|
||||||
import roboguice.activity.RoboFragmentActivity;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.app.FragmentManager;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.ProgressBar;
|
|
||||||
import android.widget.RadioButton;
|
|
||||||
import android.widget.RadioGroup;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.TextView.OnEditorActionListener;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
public class CreateBlogActivity extends RoboFragmentActivity
|
|
||||||
implements OnEditorActionListener, OnClickListener, NoContactsDialog.Listener,
|
|
||||||
SelectContactsDialog.Listener {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(CreateBlogActivity.class.getName());
|
|
||||||
|
|
||||||
@Inject @CryptoExecutor private Executor cryptoExecutor;
|
|
||||||
private EditText nameEntry = null;
|
|
||||||
private RadioGroup radioGroup = null;
|
|
||||||
private RadioButton visibleToAll = null, visibleToSome = null;
|
|
||||||
private Button createButton = null;
|
|
||||||
private ProgressBar progress = null;
|
|
||||||
private NoContactsDialog noContactsDialog = null;
|
|
||||||
private SelectContactsDialog selectContactsDialog = null;
|
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
|
||||||
@Inject private volatile CryptoComponent crypto;
|
|
||||||
@Inject private volatile GroupFactory groupFactory;
|
|
||||||
@Inject private volatile DatabaseComponent db;
|
|
||||||
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
|
||||||
@Inject private volatile LifecycleManager lifecycleManager;
|
|
||||||
private volatile Collection<ContactId> selected = Collections.emptyList();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle state) {
|
|
||||||
super.onCreate(state);
|
|
||||||
LinearLayout layout = new LinearLayout(this);
|
|
||||||
layout.setLayoutParams(MATCH_MATCH);
|
|
||||||
layout.setOrientation(VERTICAL);
|
|
||||||
layout.setGravity(CENTER_HORIZONTAL);
|
|
||||||
|
|
||||||
TextView chooseName = new TextView(this);
|
|
||||||
chooseName.setGravity(CENTER);
|
|
||||||
chooseName.setTextSize(18);
|
|
||||||
chooseName.setPadding(10, 10, 10, 0);
|
|
||||||
chooseName.setText(R.string.choose_blog_name);
|
|
||||||
layout.addView(chooseName);
|
|
||||||
|
|
||||||
nameEntry = new EditText(this) {
|
|
||||||
@Override
|
|
||||||
protected void onTextChanged(CharSequence text, int start,
|
|
||||||
int lengthBefore, int lengthAfter) {
|
|
||||||
enableOrDisableCreateButton();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
nameEntry.setId(1);
|
|
||||||
nameEntry.setMaxLines(1);
|
|
||||||
nameEntry.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_SENTENCES);
|
|
||||||
nameEntry.setOnEditorActionListener(this);
|
|
||||||
layout.addView(nameEntry);
|
|
||||||
|
|
||||||
radioGroup = new RadioGroup(this);
|
|
||||||
radioGroup.setOrientation(VERTICAL);
|
|
||||||
|
|
||||||
visibleToAll = new RadioButton(this);
|
|
||||||
visibleToAll.setId(2);
|
|
||||||
visibleToAll.setText(R.string.blog_visible_to_all);
|
|
||||||
visibleToAll.setOnClickListener(this);
|
|
||||||
radioGroup.addView(visibleToAll);
|
|
||||||
|
|
||||||
visibleToSome = new RadioButton(this);
|
|
||||||
visibleToSome.setId(3);
|
|
||||||
visibleToSome.setText(R.string.blog_visible_to_some);
|
|
||||||
visibleToSome.setOnClickListener(this);
|
|
||||||
radioGroup.addView(visibleToSome);
|
|
||||||
layout.addView(radioGroup);
|
|
||||||
|
|
||||||
createButton = new Button(this);
|
|
||||||
createButton.setLayoutParams(WRAP_WRAP);
|
|
||||||
createButton.setText(R.string.create_button);
|
|
||||||
createButton.setEnabled(false);
|
|
||||||
createButton.setOnClickListener(this);
|
|
||||||
layout.addView(createButton);
|
|
||||||
|
|
||||||
progress = new ProgressBar(this);
|
|
||||||
progress.setLayoutParams(WRAP_WRAP);
|
|
||||||
progress.setIndeterminate(true);
|
|
||||||
progress.setVisibility(GONE);
|
|
||||||
layout.addView(progress);
|
|
||||||
|
|
||||||
setContentView(layout);
|
|
||||||
|
|
||||||
FragmentManager fm = getSupportFragmentManager();
|
|
||||||
Fragment f = fm.findFragmentByTag("NoContactsDialog");
|
|
||||||
if(f == null) noContactsDialog = new NoContactsDialog();
|
|
||||||
else noContactsDialog = (NoContactsDialog) f;
|
|
||||||
noContactsDialog.setListener(this);
|
|
||||||
|
|
||||||
f = fm.findFragmentByTag("SelectContactsDialog");
|
|
||||||
if(f == null) selectContactsDialog = new SelectContactsDialog();
|
|
||||||
else selectContactsDialog = (SelectContactsDialog) f;
|
|
||||||
selectContactsDialog.setListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enableOrDisableCreateButton() {
|
|
||||||
if(nameEntry == null || radioGroup == null || createButton == null)
|
|
||||||
return; // Activity not created yet
|
|
||||||
boolean nameNotEmpty = nameEntry.getText().length() > 0;
|
|
||||||
boolean visibilitySelected = radioGroup.getCheckedRadioButtonId() != -1;
|
|
||||||
createButton.setEnabled(nameNotEmpty && visibilitySelected);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
|
|
||||||
validateName();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onClick(View view) {
|
|
||||||
if(view == visibleToAll) {
|
|
||||||
enableOrDisableCreateButton();
|
|
||||||
} else if(view == visibleToSome) {
|
|
||||||
loadContacts();
|
|
||||||
} else if(view == createButton) {
|
|
||||||
if(!validateName()) return;
|
|
||||||
final String name = nameEntry.getText().toString();
|
|
||||||
final boolean all = visibleToAll.isChecked();
|
|
||||||
final Collection<ContactId> visible =
|
|
||||||
Collections.unmodifiableCollection(selected);
|
|
||||||
// Replace the button with a progress bar
|
|
||||||
createButton.setVisibility(GONE);
|
|
||||||
progress.setVisibility(VISIBLE);
|
|
||||||
// Create the blog in a background thread
|
|
||||||
cryptoExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
|
||||||
final byte[] publicKey = keyPair.getPublic().getEncoded();
|
|
||||||
final byte[] privateKey = keyPair.getPrivate().getEncoded();
|
|
||||||
LocalGroup g;
|
|
||||||
try {
|
|
||||||
g = groupFactory.createLocalGroup(name, publicKey,
|
|
||||||
privateKey);
|
|
||||||
} catch(IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
storeLocalGroup(g, all, visible);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadContacts() {
|
|
||||||
dbUiExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
lifecycleManager.waitForDatabase();
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
Collection<Contact> contacts = db.getContacts();
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Load took " + duration + " ms");
|
|
||||||
displayContacts(contacts);
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Interrupted while waiting for database");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayContacts(final Collection<Contact> contacts) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
FragmentManager fm = getSupportFragmentManager();
|
|
||||||
if(contacts.isEmpty()) {
|
|
||||||
noContactsDialog.show(fm, "NoContactsDialog");
|
|
||||||
} else {
|
|
||||||
selectContactsDialog.setContacts(contacts);
|
|
||||||
selectContactsDialog.show(fm, "SelectContactsDialog");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void storeLocalGroup(final LocalGroup g, final boolean all,
|
|
||||||
final Collection<ContactId> visible) {
|
|
||||||
dbUiExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
lifecycleManager.waitForDatabase();
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
db.addLocalGroup(g);
|
|
||||||
db.subscribe(g);
|
|
||||||
if(all) db.setVisibleToAll(g.getId(), true);
|
|
||||||
else db.setVisibility(g.getId(), visible);
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Storing group took " + duration + " ms");
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Interrupted while waiting for database");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean validateName() {
|
|
||||||
if(nameEntry.getText().toString().equals("")) return false;
|
|
||||||
// Hide the soft keyboard
|
|
||||||
Object o = getSystemService(INPUT_METHOD_SERVICE);
|
|
||||||
((InputMethodManager) o).toggleSoftInput(HIDE_IMPLICIT_ONLY, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void contactCreationSelected() {
|
|
||||||
startActivity(new Intent(this, AddContactActivity.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void contactCreationCancelled() {
|
|
||||||
enableOrDisableCreateButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void contactsSelected(Collection<ContactId> selected) {
|
|
||||||
this.selected = selected;
|
|
||||||
enableOrDisableCreateButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void contactSelectionCancelled() {
|
|
||||||
enableOrDisableCreateButton();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import net.sf.briar.api.messaging.LocalGroup;
|
|
||||||
|
|
||||||
class LocalGroupItem {
|
|
||||||
|
|
||||||
static final LocalGroupItem NEW = new LocalGroupItem(null);
|
|
||||||
|
|
||||||
private final LocalGroup localGroup;
|
|
||||||
|
|
||||||
LocalGroupItem(LocalGroup localGroup) {
|
|
||||||
this.localGroup = localGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalGroup getLocalGroup() {
|
|
||||||
return localGroup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import static net.sf.briar.android.blogs.LocalGroupItem.NEW;
|
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
|
|
||||||
class LocalGroupItemComparator implements Comparator<LocalGroupItem> {
|
|
||||||
|
|
||||||
static final LocalGroupItemComparator INSTANCE =
|
|
||||||
new LocalGroupItemComparator();
|
|
||||||
|
|
||||||
public int compare(LocalGroupItem a, LocalGroupItem b) {
|
|
||||||
if(a == b) return 0;
|
|
||||||
if(a == NEW) return 1;
|
|
||||||
if(b == NEW) return -1;
|
|
||||||
String aName = a.getLocalGroup().getName();
|
|
||||||
String bName = b.getLocalGroup().getName();
|
|
||||||
return String.CASE_INSENSITIVE_ORDER.compare(aName, bName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import static net.sf.briar.android.blogs.LocalGroupItem.NEW;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.sf.briar.R;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.BaseAdapter;
|
|
||||||
import android.widget.SpinnerAdapter;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
class LocalGroupSpinnerAdapter extends BaseAdapter implements SpinnerAdapter {
|
|
||||||
|
|
||||||
private final Context ctx;
|
|
||||||
private final List<LocalGroupItem> list = new ArrayList<LocalGroupItem>();
|
|
||||||
|
|
||||||
LocalGroupSpinnerAdapter(Context ctx) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(LocalGroupItem item) {
|
|
||||||
list.add(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
list.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCount() {
|
|
||||||
return list.isEmpty() ? 0 : list.size() + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getDropDownView(int position, View convertView,
|
|
||||||
ViewGroup parent) {
|
|
||||||
return getView(position, convertView, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalGroupItem getItem(int position) {
|
|
||||||
if(position == list.size()) return NEW;
|
|
||||||
return list.get(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return android.R.layout.simple_spinner_item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
TextView name = new TextView(ctx);
|
|
||||||
name.setTextSize(18);
|
|
||||||
name.setMaxLines(1);
|
|
||||||
Resources res = ctx.getResources();
|
|
||||||
int pad = res.getInteger(R.integer.spinner_padding);
|
|
||||||
name.setPadding(pad, pad, pad, pad);
|
|
||||||
LocalGroupItem item = getItem(position);
|
|
||||||
if(item == NEW) name.setText(R.string.new_blog_item);
|
|
||||||
else name.setText(item.getLocalGroup().getName());
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return list.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sort(Comparator<LocalGroupItem> comparator) {
|
|
||||||
Collections.sort(list, comparator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static net.sf.briar.android.blogs.ManageBlogsItem.NONE;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import net.sf.briar.android.util.ListLoadingProgressBar;
|
|
||||||
import net.sf.briar.api.android.DatabaseUiExecutor;
|
|
||||||
import net.sf.briar.api.db.DatabaseComponent;
|
|
||||||
import net.sf.briar.api.db.DbException;
|
|
||||||
import net.sf.briar.api.db.event.DatabaseEvent;
|
|
||||||
import net.sf.briar.api.db.event.DatabaseListener;
|
|
||||||
import net.sf.briar.api.db.event.RemoteSubscriptionsUpdatedEvent;
|
|
||||||
import net.sf.briar.api.db.event.SubscriptionAddedEvent;
|
|
||||||
import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
|
|
||||||
import net.sf.briar.api.lifecycle.LifecycleManager;
|
|
||||||
import net.sf.briar.api.messaging.Group;
|
|
||||||
import net.sf.briar.api.messaging.GroupStatus;
|
|
||||||
import roboguice.activity.RoboFragmentActivity;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.AdapterView.OnItemClickListener;
|
|
||||||
import android.widget.ListView;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
public class ManageBlogsActivity extends RoboFragmentActivity
|
|
||||||
implements DatabaseListener, OnItemClickListener {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(ManageBlogsActivity.class.getName());
|
|
||||||
|
|
||||||
private ManageBlogsAdapter adapter = null;
|
|
||||||
private ListView list = null;
|
|
||||||
private ListLoadingProgressBar loading = null;
|
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
|
||||||
@Inject private volatile DatabaseComponent db;
|
|
||||||
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
|
||||||
@Inject private volatile LifecycleManager lifecycleManager;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle state) {
|
|
||||||
super.onCreate(state);
|
|
||||||
|
|
||||||
adapter = new ManageBlogsAdapter(this);
|
|
||||||
list = new ListView(this);
|
|
||||||
list.setLayoutParams(MATCH_MATCH);
|
|
||||||
list.setAdapter(adapter);
|
|
||||||
list.setOnItemClickListener(this);
|
|
||||||
|
|
||||||
// Show a progress bar while the list is loading
|
|
||||||
loading = new ListLoadingProgressBar(this);
|
|
||||||
setContentView(loading);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
db.addListener(this);
|
|
||||||
loadAvailableGroups();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadAvailableGroups() {
|
|
||||||
dbUiExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
lifecycleManager.waitForDatabase();
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
List<GroupStatus> available = new ArrayList<GroupStatus>();
|
|
||||||
for(GroupStatus s : db.getAvailableGroups())
|
|
||||||
if(s.getGroup().isRestricted()) available.add(s);
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Load took " + duration + " ms");
|
|
||||||
available = Collections.unmodifiableList(available);
|
|
||||||
displayAvailableGroups(available);
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Interrupted while waiting for database");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayAvailableGroups(
|
|
||||||
final Collection<GroupStatus> available) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
setContentView(list);
|
|
||||||
adapter.clear();
|
|
||||||
for(GroupStatus g : available)
|
|
||||||
adapter.add(new ManageBlogsItem(g));
|
|
||||||
adapter.sort(ItemComparator.INSTANCE);
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
db.removeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void eventOccurred(DatabaseEvent e) {
|
|
||||||
if(e instanceof RemoteSubscriptionsUpdatedEvent) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Remote subscriptions changed, reloading");
|
|
||||||
loadAvailableGroups();
|
|
||||||
} else if(e instanceof SubscriptionAddedEvent) {
|
|
||||||
Group g = ((SubscriptionAddedEvent) e).getGroup();
|
|
||||||
if(g.isRestricted()) {
|
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Group added, reloading");
|
|
||||||
loadAvailableGroups();
|
|
||||||
}
|
|
||||||
} else if(e instanceof SubscriptionRemovedEvent) {
|
|
||||||
Group g = ((SubscriptionRemovedEvent) e).getGroup();
|
|
||||||
if(g.isRestricted()) {
|
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
|
|
||||||
loadAvailableGroups();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position,
|
|
||||||
long id) {
|
|
||||||
ManageBlogsItem item = adapter.getItem(position);
|
|
||||||
if(item == NONE) return;
|
|
||||||
GroupStatus s = item.getGroupStatus();
|
|
||||||
Group g = s.getGroup();
|
|
||||||
Intent i = new Intent(this, ConfigureBlogActivity.class);
|
|
||||||
i.putExtra("net.sf.briar.GROUP_ID", g.getId().getBytes());
|
|
||||||
i.putExtra("net.sf.briar.GROUP_NAME", g.getName());
|
|
||||||
i.putExtra("net.sf.briar.PUBLIC_KEY", g.getPublicKey());
|
|
||||||
i.putExtra("net.sf.briar.SUBSCRIBED", s.isSubscribed());
|
|
||||||
i.putExtra("net.sf.briar.VISIBLE_TO_ALL", s.isVisibleToAll());
|
|
||||||
startActivity(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ItemComparator implements Comparator<ManageBlogsItem> {
|
|
||||||
|
|
||||||
private static final ItemComparator INSTANCE = new ItemComparator();
|
|
||||||
|
|
||||||
public int compare(ManageBlogsItem a, ManageBlogsItem b) {
|
|
||||||
if(a == b) return 0;
|
|
||||||
if(a == NONE) return 1;
|
|
||||||
if(b == NONE) return -1;
|
|
||||||
String aName = a.getGroupStatus().getGroup().getName();
|
|
||||||
String bName = b.getGroupStatus().getGroup().getName();
|
|
||||||
return String.CASE_INSENSITIVE_ORDER.compare(aName, bName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import static android.view.Gravity.CENTER;
|
|
||||||
import static android.view.View.INVISIBLE;
|
|
||||||
import static android.widget.LinearLayout.HORIZONTAL;
|
|
||||||
import static android.widget.LinearLayout.VERTICAL;
|
|
||||||
import static net.sf.briar.android.blogs.ManageBlogsItem.NONE;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.sf.briar.R;
|
|
||||||
import net.sf.briar.api.messaging.GroupStatus;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.BaseAdapter;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
class ManageBlogsAdapter extends BaseAdapter {
|
|
||||||
|
|
||||||
private final Context ctx;
|
|
||||||
private final List<ManageBlogsItem> list = new ArrayList<ManageBlogsItem>();
|
|
||||||
|
|
||||||
ManageBlogsAdapter(Context ctx) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(ManageBlogsItem item) {
|
|
||||||
list.add(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
list.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCount() {
|
|
||||||
return list.isEmpty() ? 1 : list.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ManageBlogsItem getItem(int position) {
|
|
||||||
return list.isEmpty() ? NONE : list.get(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return android.R.layout.simple_expandable_list_item_1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
ManageBlogsItem item = getItem(position);
|
|
||||||
|
|
||||||
if(item == NONE) {
|
|
||||||
TextView none = new TextView(ctx);
|
|
||||||
none.setGravity(CENTER);
|
|
||||||
none.setTextSize(18);
|
|
||||||
none.setPadding(10, 10, 10, 10);
|
|
||||||
none.setText(R.string.no_blogs_available);
|
|
||||||
return none;
|
|
||||||
}
|
|
||||||
|
|
||||||
GroupStatus s = item.getGroupStatus();
|
|
||||||
LinearLayout layout = new LinearLayout(ctx);
|
|
||||||
layout.setOrientation(HORIZONTAL);
|
|
||||||
|
|
||||||
ImageView subscribed = new ImageView(ctx);
|
|
||||||
subscribed.setPadding(5, 5, 5, 5);
|
|
||||||
subscribed.setImageResource(R.drawable.navigation_accept);
|
|
||||||
if(!s.isSubscribed()) subscribed.setVisibility(INVISIBLE);
|
|
||||||
layout.addView(subscribed);
|
|
||||||
|
|
||||||
LinearLayout innerLayout = new LinearLayout(ctx);
|
|
||||||
innerLayout.setOrientation(VERTICAL);
|
|
||||||
|
|
||||||
TextView name = new TextView(ctx);
|
|
||||||
name.setTextSize(18);
|
|
||||||
name.setMaxLines(1);
|
|
||||||
name.setPadding(0, 10, 10, 10);
|
|
||||||
name.setText(s.getGroup().getName());
|
|
||||||
innerLayout.addView(name);
|
|
||||||
|
|
||||||
TextView status = new TextView(ctx);
|
|
||||||
status.setTextSize(14);
|
|
||||||
status.setPadding(0, 0, 10, 10);
|
|
||||||
if(s.isSubscribed()) {
|
|
||||||
if(s.isVisibleToAll()) status.setText(R.string.subscribed_all);
|
|
||||||
else status.setText(R.string.subscribed_some);
|
|
||||||
} else {
|
|
||||||
status.setText(R.string.not_subscribed);
|
|
||||||
}
|
|
||||||
innerLayout.addView(status);
|
|
||||||
layout.addView(innerLayout);
|
|
||||||
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove(ManageBlogsItem item) {
|
|
||||||
list.remove(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sort(Comparator<ManageBlogsItem> comparator) {
|
|
||||||
Collections.sort(list, comparator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import net.sf.briar.api.messaging.GroupStatus;
|
|
||||||
|
|
||||||
class ManageBlogsItem {
|
|
||||||
|
|
||||||
static final ManageBlogsItem NONE = new ManageBlogsItem(null);
|
|
||||||
|
|
||||||
private final GroupStatus status;
|
|
||||||
|
|
||||||
ManageBlogsItem(GroupStatus status) {
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
GroupStatus getGroupStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import net.sf.briar.R;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.app.DialogFragment;
|
|
||||||
|
|
||||||
public class NoBlogsDialog extends DialogFragment {
|
|
||||||
|
|
||||||
private Listener listener = null;
|
|
||||||
|
|
||||||
void setListener(Listener listener) {
|
|
||||||
this.listener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle state) {
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
|
||||||
builder.setMessage(R.string.no_blogs);
|
|
||||||
builder.setPositiveButton(R.string.create_button,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
|
||||||
listener.blogCreationSelected();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.setNegativeButton(R.string.cancel_button,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
|
||||||
listener.blogCreationCancelled();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return builder.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Listener {
|
|
||||||
|
|
||||||
void blogCreationSelected();
|
|
||||||
|
|
||||||
void blogCreationCancelled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import net.sf.briar.R;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.app.DialogFragment;
|
|
||||||
|
|
||||||
public class NotYourBlogDialog extends DialogFragment {
|
|
||||||
|
|
||||||
private static final DialogInterface.OnClickListener IGNORE =
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle state) {
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
|
||||||
builder.setMessage(R.string.not_your_blog);
|
|
||||||
builder.setPositiveButton(R.string.ok_button, IGNORE);
|
|
||||||
return builder.create();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,297 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import static android.view.Gravity.CENTER;
|
|
||||||
import static android.view.Gravity.CENTER_VERTICAL;
|
|
||||||
import static android.widget.LinearLayout.HORIZONTAL;
|
|
||||||
import static android.widget.LinearLayout.VERTICAL;
|
|
||||||
import static java.text.DateFormat.SHORT;
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_WRAP;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_WRAP_1;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP_1;
|
|
||||||
import static net.sf.briar.api.messaging.Rating.BAD;
|
|
||||||
import static net.sf.briar.api.messaging.Rating.GOOD;
|
|
||||||
import static net.sf.briar.api.messaging.Rating.UNRATED;
|
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import net.sf.briar.R;
|
|
||||||
import net.sf.briar.android.util.HorizontalBorder;
|
|
||||||
import net.sf.briar.android.util.HorizontalSpace;
|
|
||||||
import net.sf.briar.api.android.DatabaseUiExecutor;
|
|
||||||
import net.sf.briar.api.db.DatabaseComponent;
|
|
||||||
import net.sf.briar.api.db.DbException;
|
|
||||||
import net.sf.briar.api.db.NoSuchMessageException;
|
|
||||||
import net.sf.briar.api.lifecycle.LifecycleManager;
|
|
||||||
import net.sf.briar.api.messaging.GroupId;
|
|
||||||
import net.sf.briar.api.messaging.MessageId;
|
|
||||||
import net.sf.briar.api.messaging.Rating;
|
|
||||||
import roboguice.activity.RoboFragmentActivity;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.ScrollView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
public class ReadBlogPostActivity extends RoboFragmentActivity
|
|
||||||
implements OnClickListener {
|
|
||||||
|
|
||||||
static final int RESULT_REPLY = RESULT_FIRST_USER;
|
|
||||||
static final int RESULT_PREV = RESULT_FIRST_USER + 1;
|
|
||||||
static final int RESULT_NEXT = RESULT_FIRST_USER + 2;
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(ReadBlogPostActivity.class.getName());
|
|
||||||
|
|
||||||
private GroupId groupId = null;
|
|
||||||
private boolean postable = false;
|
|
||||||
private Rating rating = UNRATED;
|
|
||||||
private boolean read;
|
|
||||||
private ImageView thumb = null;
|
|
||||||
private ImageButton readButton = null, prevButton = null, nextButton = null;
|
|
||||||
private ImageButton replyButton = null;
|
|
||||||
private TextView content = null;
|
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
|
||||||
@Inject private volatile DatabaseComponent db;
|
|
||||||
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
|
||||||
@Inject private volatile LifecycleManager lifecycleManager;
|
|
||||||
private volatile MessageId messageId = null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle state) {
|
|
||||||
super.onCreate(state);
|
|
||||||
|
|
||||||
Intent i = getIntent();
|
|
||||||
byte[] b = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
|
|
||||||
if(b == null) throw new IllegalStateException();
|
|
||||||
groupId = new GroupId(b);
|
|
||||||
String groupName = i.getStringExtra("net.sf.briar.GROUP_NAME");
|
|
||||||
if(groupName == null) throw new IllegalStateException();
|
|
||||||
setTitle(groupName);
|
|
||||||
postable = i.getBooleanExtra("net.sf.briar.POSTABLE", false);
|
|
||||||
b = i.getByteArrayExtra("net.sf.briar.MESSAGE_ID");
|
|
||||||
if(b == null) throw new IllegalStateException();
|
|
||||||
messageId = new MessageId(b);
|
|
||||||
String authorName = i.getStringExtra("net.sf.briar.AUTHOR_NAME");
|
|
||||||
String r = i.getStringExtra("net.sf.briar.RATING");
|
|
||||||
if(r != null) rating = Rating.valueOf(r);
|
|
||||||
String contentType = i.getStringExtra("net.sf.briar.CONTENT_TYPE");
|
|
||||||
if(contentType == null) throw new IllegalStateException();
|
|
||||||
long timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
|
|
||||||
if(timestamp == -1) throw new IllegalStateException();
|
|
||||||
|
|
||||||
if(state == null) {
|
|
||||||
read = false;
|
|
||||||
setReadInDatabase(true);
|
|
||||||
} else {
|
|
||||||
read = state.getBoolean("net.sf.briar.READ");
|
|
||||||
}
|
|
||||||
|
|
||||||
LinearLayout layout = new LinearLayout(this);
|
|
||||||
layout.setLayoutParams(MATCH_WRAP);
|
|
||||||
layout.setOrientation(VERTICAL);
|
|
||||||
|
|
||||||
ScrollView scrollView = new ScrollView(this);
|
|
||||||
// Give me all the width and all the unused height
|
|
||||||
scrollView.setLayoutParams(MATCH_WRAP_1);
|
|
||||||
|
|
||||||
LinearLayout message = new LinearLayout(this);
|
|
||||||
message.setOrientation(VERTICAL);
|
|
||||||
Resources res = getResources();
|
|
||||||
message.setBackgroundColor(res.getColor(R.color.content_background));
|
|
||||||
|
|
||||||
LinearLayout header = new LinearLayout(this);
|
|
||||||
header.setLayoutParams(MATCH_WRAP);
|
|
||||||
header.setOrientation(HORIZONTAL);
|
|
||||||
header.setGravity(CENTER_VERTICAL);
|
|
||||||
|
|
||||||
thumb = new ImageView(this);
|
|
||||||
thumb.setPadding(5, 5, 5, 5);
|
|
||||||
if(rating == GOOD) thumb.setImageResource(R.drawable.rating_good);
|
|
||||||
else if(rating == BAD) thumb.setImageResource(R.drawable.rating_bad);
|
|
||||||
else thumb.setImageResource(R.drawable.rating_unrated);
|
|
||||||
header.addView(thumb);
|
|
||||||
|
|
||||||
TextView name = new TextView(this);
|
|
||||||
// Give me all the unused width
|
|
||||||
name.setLayoutParams(WRAP_WRAP_1);
|
|
||||||
name.setTextSize(18);
|
|
||||||
name.setMaxLines(1);
|
|
||||||
name.setPadding(0, 10, 10, 10);
|
|
||||||
if(authorName == null) {
|
|
||||||
name.setTextColor(res.getColor(R.color.anonymous_author));
|
|
||||||
name.setText(R.string.anonymous);
|
|
||||||
} else {
|
|
||||||
name.setText(authorName);
|
|
||||||
}
|
|
||||||
header.addView(name);
|
|
||||||
|
|
||||||
TextView date = new TextView(this);
|
|
||||||
date.setTextSize(14);
|
|
||||||
date.setPadding(0, 10, 10, 10);
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
date.setText(DateUtils.formatSameDayTime(timestamp, now, SHORT, SHORT));
|
|
||||||
header.addView(date);
|
|
||||||
message.addView(header);
|
|
||||||
|
|
||||||
if(contentType.equals("text/plain")) {
|
|
||||||
// Load and display the message body
|
|
||||||
content = new TextView(this);
|
|
||||||
content.setPadding(10, 0, 10, 10);
|
|
||||||
message.addView(content);
|
|
||||||
loadMessageBody();
|
|
||||||
}
|
|
||||||
scrollView.addView(message);
|
|
||||||
layout.addView(scrollView);
|
|
||||||
|
|
||||||
layout.addView(new HorizontalBorder(this));
|
|
||||||
|
|
||||||
LinearLayout footer = new LinearLayout(this);
|
|
||||||
footer.setLayoutParams(MATCH_WRAP);
|
|
||||||
footer.setOrientation(HORIZONTAL);
|
|
||||||
footer.setGravity(CENTER);
|
|
||||||
|
|
||||||
readButton = new ImageButton(this);
|
|
||||||
readButton.setBackgroundResource(0);
|
|
||||||
if(read) readButton.setImageResource(R.drawable.content_unread);
|
|
||||||
else readButton.setImageResource(R.drawable.content_read);
|
|
||||||
readButton.setOnClickListener(this);
|
|
||||||
footer.addView(readButton);
|
|
||||||
footer.addView(new HorizontalSpace(this));
|
|
||||||
|
|
||||||
prevButton = new ImageButton(this);
|
|
||||||
prevButton.setBackgroundResource(0);
|
|
||||||
prevButton.setImageResource(R.drawable.navigation_previous_item);
|
|
||||||
prevButton.setOnClickListener(this);
|
|
||||||
footer.addView(prevButton);
|
|
||||||
footer.addView(new HorizontalSpace(this));
|
|
||||||
|
|
||||||
nextButton = new ImageButton(this);
|
|
||||||
nextButton.setBackgroundResource(0);
|
|
||||||
nextButton.setImageResource(R.drawable.navigation_next_item);
|
|
||||||
nextButton.setOnClickListener(this);
|
|
||||||
footer.addView(nextButton);
|
|
||||||
footer.addView(new HorizontalSpace(this));
|
|
||||||
|
|
||||||
replyButton = new ImageButton(this);
|
|
||||||
replyButton.setBackgroundResource(0);
|
|
||||||
replyButton.setImageResource(R.drawable.social_reply_all);
|
|
||||||
replyButton.setOnClickListener(this);
|
|
||||||
footer.addView(replyButton);
|
|
||||||
layout.addView(footer);
|
|
||||||
|
|
||||||
setContentView(layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setReadInDatabase(final boolean read) {
|
|
||||||
dbUiExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
lifecycleManager.waitForDatabase();
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
db.setReadFlag(messageId, read);
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Setting flag took " + duration + " ms");
|
|
||||||
setReadInUi(read);
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Interrupted while waiting for database");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setReadInUi(final boolean read) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
ReadBlogPostActivity.this.read = read;
|
|
||||||
if(read) readButton.setImageResource(R.drawable.content_unread);
|
|
||||||
else readButton.setImageResource(R.drawable.content_read);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadMessageBody() {
|
|
||||||
dbUiExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
lifecycleManager.waitForDatabase();
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
byte[] body = db.getMessageBody(messageId);
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Loading message took " + duration + " ms");
|
|
||||||
final String text = new String(body, "UTF-8");
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
content.setText(text);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch(NoSuchMessageException e) {
|
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Message removed");
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Interrupted while waiting for database");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
} catch(UnsupportedEncodingException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle state) {
|
|
||||||
super.onSaveInstanceState(state);
|
|
||||||
state.putBoolean("net.sf.briar.READ", read);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onClick(View view) {
|
|
||||||
if(view == readButton) {
|
|
||||||
setReadInDatabase(!read);
|
|
||||||
} else if(view == prevButton) {
|
|
||||||
setResult(RESULT_PREV);
|
|
||||||
finish();
|
|
||||||
} else if(view == nextButton) {
|
|
||||||
setResult(RESULT_NEXT);
|
|
||||||
finish();
|
|
||||||
} else if(view == replyButton) {
|
|
||||||
if(postable) {
|
|
||||||
Intent i = new Intent(this, WriteBlogPostActivity.class);
|
|
||||||
i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
|
|
||||||
i.putExtra("net.sf.briar.PARENT_ID", messageId.getBytes());
|
|
||||||
startActivity(i);
|
|
||||||
setResult(RESULT_REPLY);
|
|
||||||
finish();
|
|
||||||
} else {
|
|
||||||
NotYourBlogDialog dialog = new NotYourBlogDialog();
|
|
||||||
dialog.show(getSupportFragmentManager(), "NotYourBlogDialog");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,357 +0,0 @@
|
|||||||
package net.sf.briar.android.blogs;
|
|
||||||
|
|
||||||
import static android.text.InputType.TYPE_CLASS_TEXT;
|
|
||||||
import static android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
|
|
||||||
import static android.view.Gravity.CENTER_VERTICAL;
|
|
||||||
import static android.widget.LinearLayout.HORIZONTAL;
|
|
||||||
import static android.widget.LinearLayout.VERTICAL;
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_WRAP;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import net.sf.briar.R;
|
|
||||||
import net.sf.briar.android.identity.CreateIdentityActivity;
|
|
||||||
import net.sf.briar.android.identity.LocalAuthorItem;
|
|
||||||
import net.sf.briar.android.identity.LocalAuthorItemComparator;
|
|
||||||
import net.sf.briar.android.identity.LocalAuthorSpinnerAdapter;
|
|
||||||
import net.sf.briar.android.util.HorizontalSpace;
|
|
||||||
import net.sf.briar.api.AuthorId;
|
|
||||||
import net.sf.briar.api.LocalAuthor;
|
|
||||||
import net.sf.briar.api.android.DatabaseUiExecutor;
|
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
|
||||||
import net.sf.briar.api.crypto.KeyParser;
|
|
||||||
import net.sf.briar.api.crypto.PrivateKey;
|
|
||||||
import net.sf.briar.api.db.DatabaseComponent;
|
|
||||||
import net.sf.briar.api.db.DbException;
|
|
||||||
import net.sf.briar.api.lifecycle.LifecycleManager;
|
|
||||||
import net.sf.briar.api.messaging.GroupId;
|
|
||||||
import net.sf.briar.api.messaging.LocalGroup;
|
|
||||||
import net.sf.briar.api.messaging.Message;
|
|
||||||
import net.sf.briar.api.messaging.MessageFactory;
|
|
||||||
import net.sf.briar.api.messaging.MessageId;
|
|
||||||
import roboguice.activity.RoboActivity;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.InputType;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.AdapterView.OnItemSelectedListener;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
public class WriteBlogPostActivity extends RoboActivity
|
|
||||||
implements OnItemSelectedListener, OnClickListener {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(WriteBlogPostActivity.class.getName());
|
|
||||||
|
|
||||||
@Inject private CryptoComponent crypto;
|
|
||||||
@Inject private MessageFactory messageFactory;
|
|
||||||
private LocalAuthorSpinnerAdapter fromAdapter = null;
|
|
||||||
private LocalGroupSpinnerAdapter toAdapter = null;
|
|
||||||
private Spinner fromSpinner = null, toSpinner = null;
|
|
||||||
private ImageButton sendButton = null;
|
|
||||||
private EditText content = null;
|
|
||||||
private AuthorId localAuthorId = null;
|
|
||||||
private GroupId localGroupId = null;
|
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
|
||||||
@Inject private volatile DatabaseComponent db;
|
|
||||||
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
|
||||||
@Inject private volatile LifecycleManager lifecycleManager;
|
|
||||||
private volatile LocalAuthor localAuthor = null;
|
|
||||||
private volatile LocalGroup localGroup = null;
|
|
||||||
private volatile MessageId parentId = null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle state) {
|
|
||||||
super.onCreate(state);
|
|
||||||
|
|
||||||
Intent i = getIntent();
|
|
||||||
byte[] b = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
|
|
||||||
if(b != null) localGroupId = new GroupId(b);
|
|
||||||
b = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
|
|
||||||
if(b != null) parentId = new MessageId(b);
|
|
||||||
|
|
||||||
if(state != null) {
|
|
||||||
b = state.getByteArray("net.sf.briar.LOCAL_AUTHOR_ID");
|
|
||||||
if(b != null) localAuthorId = new AuthorId(b);
|
|
||||||
b = state.getByteArray("net.sf.briar.LOCAL_GROUP_ID");
|
|
||||||
if(b != null) localGroupId = new GroupId(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
LinearLayout layout = new LinearLayout(this);
|
|
||||||
layout.setLayoutParams(MATCH_WRAP);
|
|
||||||
layout.setOrientation(VERTICAL);
|
|
||||||
|
|
||||||
LinearLayout header = new LinearLayout(this);
|
|
||||||
header.setLayoutParams(MATCH_WRAP);
|
|
||||||
header.setOrientation(HORIZONTAL);
|
|
||||||
header.setGravity(CENTER_VERTICAL);
|
|
||||||
|
|
||||||
TextView from = new TextView(this);
|
|
||||||
from.setTextSize(18);
|
|
||||||
from.setPadding(10, 10, 0, 10);
|
|
||||||
from.setText(R.string.from);
|
|
||||||
header.addView(from);
|
|
||||||
|
|
||||||
fromAdapter = new LocalAuthorSpinnerAdapter(this, true);
|
|
||||||
fromSpinner = new Spinner(this);
|
|
||||||
fromSpinner.setAdapter(fromAdapter);
|
|
||||||
fromSpinner.setOnItemSelectedListener(this);
|
|
||||||
header.addView(fromSpinner);
|
|
||||||
|
|
||||||
header.addView(new HorizontalSpace(this));
|
|
||||||
|
|
||||||
sendButton = new ImageButton(this);
|
|
||||||
sendButton.setBackgroundResource(0);
|
|
||||||
sendButton.setImageResource(R.drawable.social_send_now);
|
|
||||||
sendButton.setEnabled(false); // Enabled when a group is selected
|
|
||||||
sendButton.setOnClickListener(this);
|
|
||||||
header.addView(sendButton);
|
|
||||||
layout.addView(header);
|
|
||||||
|
|
||||||
header = new LinearLayout(this);
|
|
||||||
header.setLayoutParams(MATCH_WRAP);
|
|
||||||
header.setOrientation(HORIZONTAL);
|
|
||||||
header.setGravity(CENTER_VERTICAL);
|
|
||||||
|
|
||||||
TextView to = new TextView(this);
|
|
||||||
to.setTextSize(18);
|
|
||||||
to.setPadding(10, 0, 0, 10);
|
|
||||||
to.setText(R.string.to);
|
|
||||||
header.addView(to);
|
|
||||||
|
|
||||||
toAdapter = new LocalGroupSpinnerAdapter(this);
|
|
||||||
toSpinner = new Spinner(this);
|
|
||||||
toSpinner.setAdapter(toAdapter);
|
|
||||||
toSpinner.setOnItemSelectedListener(this);
|
|
||||||
header.addView(toSpinner);
|
|
||||||
layout.addView(header);
|
|
||||||
|
|
||||||
content = new EditText(this);
|
|
||||||
content.setId(1);
|
|
||||||
int inputType = TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE
|
|
||||||
| TYPE_TEXT_FLAG_CAP_SENTENCES;
|
|
||||||
content.setInputType(inputType);
|
|
||||||
layout.addView(content);
|
|
||||||
|
|
||||||
setContentView(layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
loadLocalAuthors();
|
|
||||||
loadLocalGroups();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadLocalAuthors() {
|
|
||||||
dbUiExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
lifecycleManager.waitForDatabase();
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
Collection<LocalAuthor> localAuthors = db.getLocalAuthors();
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Loading authors took " + duration + " ms");
|
|
||||||
displayLocalAuthors(localAuthors);
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
LOG.info("Interrupted while waiting for database");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayLocalAuthors(
|
|
||||||
final Collection<LocalAuthor> localAuthors) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
if(localAuthors.isEmpty()) throw new IllegalStateException();
|
|
||||||
fromAdapter.clear();
|
|
||||||
for(LocalAuthor a : localAuthors)
|
|
||||||
fromAdapter.add(new LocalAuthorItem(a));
|
|
||||||
fromAdapter.sort(LocalAuthorItemComparator.INSTANCE);
|
|
||||||
fromAdapter.notifyDataSetChanged();
|
|
||||||
int count = fromAdapter.getCount();
|
|
||||||
for(int i = 0; i < count; i++) {
|
|
||||||
LocalAuthorItem item = fromAdapter.getItem(i);
|
|
||||||
if(item == LocalAuthorItem.ANONYMOUS) continue;
|
|
||||||
if(item == LocalAuthorItem.NEW) continue;
|
|
||||||
if(item.getLocalAuthor().getId().equals(localAuthorId)) {
|
|
||||||
localAuthor = item.getLocalAuthor();
|
|
||||||
fromSpinner.setSelection(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadLocalGroups() {
|
|
||||||
dbUiExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
lifecycleManager.waitForDatabase();
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
Collection<LocalGroup> groups = db.getLocalGroups();
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Loading groups took " + duration + " ms");
|
|
||||||
displayLocalGroups(groups);
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
LOG.info("Interrupted while waiting for database");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayLocalGroups(final Collection<LocalGroup> groups) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
if(groups.isEmpty()) finish();
|
|
||||||
toAdapter.clear();
|
|
||||||
for(LocalGroup g : groups) toAdapter.add(new LocalGroupItem(g));
|
|
||||||
toAdapter.sort(LocalGroupItemComparator.INSTANCE);
|
|
||||||
toAdapter.notifyDataSetChanged();
|
|
||||||
int count = toAdapter.getCount();
|
|
||||||
for(int i = 0; i < count; i++) {
|
|
||||||
LocalGroupItem item = toAdapter.getItem(i);
|
|
||||||
if(item == LocalGroupItem.NEW) continue;
|
|
||||||
if(item.getLocalGroup().getId().equals(localGroupId)) {
|
|
||||||
localGroup = item.getLocalGroup();
|
|
||||||
toSpinner.setSelection(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle state) {
|
|
||||||
super.onSaveInstanceState(state);
|
|
||||||
if(localAuthorId != null) {
|
|
||||||
byte[] b = localAuthorId.getBytes();
|
|
||||||
state.putByteArray("net.sf.briar.LOCAL_AUTHOR_ID", b);
|
|
||||||
}
|
|
||||||
if(localGroupId != null) {
|
|
||||||
byte[] b = localGroupId.getBytes();
|
|
||||||
state.putByteArray("net.sf.briar.LOCAL_GROUP_ID", b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position,
|
|
||||||
long id) {
|
|
||||||
if(parent == fromSpinner) {
|
|
||||||
LocalAuthorItem item = fromAdapter.getItem(position);
|
|
||||||
if(item == LocalAuthorItem.ANONYMOUS) {
|
|
||||||
localAuthor = null;
|
|
||||||
localAuthorId = null;
|
|
||||||
} else if(item == LocalAuthorItem.NEW) {
|
|
||||||
localAuthor = null;
|
|
||||||
localAuthorId = null;
|
|
||||||
startActivity(new Intent(this, CreateIdentityActivity.class));
|
|
||||||
} else {
|
|
||||||
localAuthor = item.getLocalAuthor();
|
|
||||||
localAuthorId = localAuthor.getId();
|
|
||||||
}
|
|
||||||
} else if(parent == toSpinner) {
|
|
||||||
LocalGroupItem item = toAdapter.getItem(position);
|
|
||||||
if(item == LocalGroupItem.NEW) {
|
|
||||||
localGroup = null;
|
|
||||||
localGroupId = null;
|
|
||||||
startActivity(new Intent(this, CreateBlogActivity.class));
|
|
||||||
} else {
|
|
||||||
localGroup = item.getLocalGroup();
|
|
||||||
localGroupId = localGroup.getId();
|
|
||||||
sendButton.setEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onNothingSelected(AdapterView<?> parent) {
|
|
||||||
if(parent == fromSpinner) {
|
|
||||||
localAuthor = null;
|
|
||||||
localAuthorId = null;
|
|
||||||
} else if(parent == toSpinner) {
|
|
||||||
localGroup = null;
|
|
||||||
localGroupId = null;
|
|
||||||
sendButton.setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onClick(View view) {
|
|
||||||
if(localGroup == null) throw new IllegalStateException();
|
|
||||||
try {
|
|
||||||
byte[] b = content.getText().toString().getBytes("UTF-8");
|
|
||||||
storeMessage(createMessage(b));
|
|
||||||
} catch(GeneralSecurityException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} catch(IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: This should happen on a CryptoExecutor thread
|
|
||||||
private Message createMessage(byte[] body) throws IOException,
|
|
||||||
GeneralSecurityException {
|
|
||||||
KeyParser keyParser = crypto.getSignatureKeyParser();
|
|
||||||
byte[] groupKeyBytes = localGroup.getPrivateKey();
|
|
||||||
PrivateKey groupKey = keyParser.parsePrivateKey(groupKeyBytes);
|
|
||||||
if(localAuthor == null) {
|
|
||||||
return messageFactory.createAnonymousMessage(parentId, localGroup,
|
|
||||||
groupKey, "text/plain", body);
|
|
||||||
} else {
|
|
||||||
byte[] authorKeyBytes = localAuthor.getPrivateKey();
|
|
||||||
PrivateKey authorKey = keyParser.parsePrivateKey(authorKeyBytes);
|
|
||||||
return messageFactory.createPseudonymousMessage(parentId,
|
|
||||||
localGroup, groupKey, localAuthor, authorKey, "text/plain",
|
|
||||||
body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void storeMessage(final Message m) {
|
|
||||||
dbUiExecutor.execute(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
lifecycleManager.waitForDatabase();
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
db.addLocalGroupMessage(m);
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Storing message took " + duration + " ms");
|
|
||||||
} catch(DbException e) {
|
|
||||||
if(LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
if(LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Interrupted while waiting for database");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user