mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 11:19:04 +01:00
Android UI for group messages (anonymous text only, no moderation yet).
This commit is contained in:
@@ -40,6 +40,22 @@
|
||||
android:name=".android.contact.ContactListActivity"
|
||||
android:label="@string/contact_list_title" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.groups.GroupActivity"
|
||||
android:label="@string/groups_title" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.groups.GroupListActivity"
|
||||
android:label="@string/groups_title" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.groups.ReadGroupMessageActivity"
|
||||
android:label="@string/groups_title" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.groups.WriteGroupMessageActivity"
|
||||
android:label="@string/compose_group_title" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.invitation.AddContactActivity"
|
||||
android:label="@string/add_contact_title" >
|
||||
@@ -53,12 +69,12 @@
|
||||
android:label="@string/messages_title" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.messages.ReadMessageActivity"
|
||||
android:name="net.sf.briar.android.messages.ReadPrivateMessageActivity"
|
||||
android:label="@string/messages_title" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.messages.WriteMessageActivity"
|
||||
android:label="@string/compose_title" >
|
||||
android:name="net.sf.briar.android.messages.WritePrivateMessageActivity"
|
||||
android:label="@string/compose_message_title" >
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
BIN
briar-android/res/drawable-hdpi/social_new_chat.png
Normal file
BIN
briar-android/res/drawable-hdpi/social_new_chat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 716 B |
BIN
briar-android/res/drawable-hdpi/social_reply_all.png
Normal file
BIN
briar-android/res/drawable-hdpi/social_reply_all.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
briar-android/res/drawable-mdpi/social_new_chat.png
Normal file
BIN
briar-android/res/drawable-mdpi/social_new_chat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 583 B |
BIN
briar-android/res/drawable-mdpi/social_reply_all.png
Normal file
BIN
briar-android/res/drawable-mdpi/social_reply_all.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
briar-android/res/drawable-xhdpi/social_new_chat.png
Normal file
BIN
briar-android/res/drawable-xhdpi/social_new_chat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 830 B |
BIN
briar-android/res/drawable-xhdpi/social_reply_all.png
Normal file
BIN
briar-android/res/drawable-xhdpi/social_reply_all.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
@@ -1,4 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="HorizontalBorder">#CCCCCC</color>
|
||||
<color name="horizontal_border">#CCCCCC</color>
|
||||
<color name="anonymous_author">#999999</color>
|
||||
<color name="pseudonymous_author">#000000</color>
|
||||
</resources>
|
||||
@@ -11,14 +11,13 @@
|
||||
<string name="quit_button">Quit</string>
|
||||
<string name="contact_list_title">Contacts</string>
|
||||
<string name="contact_connected">Connected</string>
|
||||
<string name="contact_last_connected">Last connected <br /> %1$s</string>
|
||||
<string name="search_button">Search</string>
|
||||
<string name="format_contact_last_connected">Last connected <br /> %1$s</string>
|
||||
<string name="add_contact_title">Add a Contact</string>
|
||||
<string name="same_network">Briar can add contacts via Wi-Fi or Bluetooth. To use Wi-Fi you must both be connected to the same network.</string>
|
||||
<string name="wifi_not_available">Wi-Fi is not available on this device</string>
|
||||
<string name="wifi_disabled">Wi-Fi is OFF</string>
|
||||
<string name="wifi_disconnected">Wi-Fi is DISCONNECTED</string>
|
||||
<string name="wifi_connected">Wi-Fi is CONNECTED to %1$s</string>
|
||||
<string name="format_wifi_connected">Wi-Fi is CONNECTED to %1$s</string>
|
||||
<string name="bluetooth_not_available">Bluetooth is not available on this device</string>
|
||||
<string name="bluetooth_disabled">Bluetooth is OFF</string>
|
||||
<string name="bluetooth_not_discoverable">Bluetooth is NOT DISCOVERABLE</string>
|
||||
@@ -26,7 +25,7 @@
|
||||
<string name="continue_button">Continue</string>
|
||||
<string name="your_invitation_code">Your invitation code is</string>
|
||||
<string name="enter_invitation_code">Please enter your contact\'s invitation code:</string>
|
||||
<string name="connecting_wifi">Connecting via %1$s\u2026</string>
|
||||
<string name="format_connecting_wifi">Connecting via %1$s\u2026</string>
|
||||
<string name="connecting_bluetooth">Connecting via Bluetooth\u2026</string>
|
||||
<string name="connection_failed">Connection failed</string>
|
||||
<string name="check_same_network">Please check that you are both using the same network.</string>
|
||||
@@ -41,7 +40,11 @@
|
||||
<string name="enter_nickname">Please enter a nickname for this contact:</string>
|
||||
<string name="done_button">Done</string>
|
||||
<string name="messages_title">Messages</string>
|
||||
<string name="message_from">From: %1$s</string>
|
||||
<string name="compose_title">New Message</string>
|
||||
<string name="message_to">To:</string>
|
||||
<string name="format_from">From: %1$s</string>
|
||||
<string name="format_to">To: %1$s</string>
|
||||
<string name="compose_message_title">New Message</string>
|
||||
<string name="to">To:</string>
|
||||
<string name="groups_title">Groups</string>
|
||||
<string name="anonymous">(Anonymous)</string>
|
||||
<string name="compose_group_title">New Post</string>
|
||||
</resources>
|
||||
|
||||
@@ -12,6 +12,7 @@ import net.sf.briar.R;
|
||||
import net.sf.briar.android.BriarService.BriarBinder;
|
||||
import net.sf.briar.android.BriarService.BriarServiceConnection;
|
||||
import net.sf.briar.android.contact.ContactListActivity;
|
||||
import net.sf.briar.android.groups.GroupListActivity;
|
||||
import net.sf.briar.android.messages.ConversationListActivity;
|
||||
import net.sf.briar.android.widgets.CommonLayoutParams;
|
||||
import android.content.Intent;
|
||||
@@ -88,7 +89,8 @@ public class HomeScreenActivity extends BriarActivity {
|
||||
groupsButton.setText(R.string.groups_button);
|
||||
groupsButton.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View view) {
|
||||
// FIXME: Hook this button up to an activity
|
||||
startActivity(new Intent(HomeScreenActivity.this,
|
||||
GroupListActivity.class));
|
||||
}
|
||||
});
|
||||
buttons.add(groupsButton);
|
||||
|
||||
@@ -7,7 +7,7 @@ class ContactComparator implements Comparator<ContactListItem> {
|
||||
static final ContactComparator INSTANCE = new ContactComparator();
|
||||
|
||||
public int compare(ContactListItem a, ContactListItem b) {
|
||||
return String.CASE_INSENSITIVE_ORDER.compare(a.contact.getName(),
|
||||
b.contact.getName());
|
||||
return String.CASE_INSENSITIVE_ORDER.compare(a.getContactName(),
|
||||
b.getContactName());
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import java.util.ArrayList;
|
||||
import net.sf.briar.R;
|
||||
import net.sf.briar.android.widgets.CommonLayoutParams;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.text.Html;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.View;
|
||||
@@ -46,8 +45,9 @@ implements OnItemClickListener {
|
||||
// Give me all the unused width
|
||||
name.setLayoutParams(CommonLayoutParams.WRAP_WRAP_1);
|
||||
name.setTextSize(18);
|
||||
name.setMaxLines(1);
|
||||
name.setPadding(0, 10, 10, 10);
|
||||
name.setText(item.getName());
|
||||
name.setText(item.getContactName());
|
||||
layout.addView(name);
|
||||
|
||||
TextView connected = new TextView(ctx);
|
||||
@@ -56,8 +56,8 @@ implements OnItemClickListener {
|
||||
if(item.isConnected()) {
|
||||
connected.setText(R.string.contact_connected);
|
||||
} else {
|
||||
Resources res = ctx.getResources();
|
||||
String format = res.getString(R.string.contact_last_connected);
|
||||
String format = ctx.getResources().getString(
|
||||
R.string.format_contact_last_connected);
|
||||
long then = item.getLastConnected();
|
||||
CharSequence ago = DateUtils.getRelativeTimeSpanString(then);
|
||||
connected.setText(Html.fromHtml(String.format(format, ago)));
|
||||
|
||||
@@ -6,7 +6,7 @@ import net.sf.briar.api.ContactId;
|
||||
// This class is not thread-safe
|
||||
class ContactListItem {
|
||||
|
||||
final Contact contact;
|
||||
private final Contact contact;
|
||||
private boolean connected;
|
||||
|
||||
ContactListItem(Contact contact, boolean connected) {
|
||||
@@ -18,7 +18,7 @@ class ContactListItem {
|
||||
return contact.getId();
|
||||
}
|
||||
|
||||
String getName() {
|
||||
String getContactName() {
|
||||
return contact.getName();
|
||||
}
|
||||
|
||||
|
||||
226
briar-android/src/net/sf/briar/android/groups/GroupActivity.java
Normal file
226
briar-android/src/net/sf/briar/android/groups/GroupActivity.java
Normal file
@@ -0,0 +1,226 @@
|
||||
package net.sf.briar.android.groups;
|
||||
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
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.BriarActivity;
|
||||
import net.sf.briar.android.BriarService;
|
||||
import net.sf.briar.android.BriarService.BriarServiceConnection;
|
||||
import net.sf.briar.android.widgets.CommonLayoutParams;
|
||||
import net.sf.briar.android.widgets.HorizontalBorder;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.GroupMessageHeader;
|
||||
import net.sf.briar.api.db.event.DatabaseEvent;
|
||||
import net.sf.briar.api.db.event.DatabaseListener;
|
||||
import net.sf.briar.api.db.event.MessageAddedEvent;
|
||||
import net.sf.briar.api.db.event.MessageExpiredEvent;
|
||||
import net.sf.briar.api.db.event.SubscriptionAddedEvent;
|
||||
import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
|
||||
import net.sf.briar.api.messaging.Author;
|
||||
import net.sf.briar.api.messaging.GroupId;
|
||||
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 GroupActivity extends BriarActivity implements DatabaseListener,
|
||||
OnClickListener, OnItemClickListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(GroupActivity.class.getName());
|
||||
|
||||
private final BriarServiceConnection serviceConnection =
|
||||
new BriarServiceConnection();
|
||||
|
||||
@Inject private DatabaseComponent db;
|
||||
@Inject @DatabaseExecutor private Executor dbExecutor;
|
||||
|
||||
private GroupId groupId = null;
|
||||
private String groupName = null;
|
||||
private GroupAdapter adapter = null;
|
||||
private ListView list = null;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
super.onCreate(null);
|
||||
|
||||
Intent i = getIntent();
|
||||
byte[] id = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
|
||||
if(id == null) throw new IllegalStateException();
|
||||
groupId = new GroupId(id);
|
||||
groupName = i.getStringExtra("net.sf.briar.GROUP_NAME");
|
||||
if(groupName == null) throw new IllegalStateException();
|
||||
setTitle(groupName);
|
||||
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(CommonLayoutParams.MATCH_MATCH);
|
||||
layout.setOrientation(VERTICAL);
|
||||
layout.setGravity(CENTER_HORIZONTAL);
|
||||
|
||||
adapter = new GroupAdapter(this);
|
||||
list = new ListView(this);
|
||||
// Give me all the width and all the unused height
|
||||
list.setLayoutParams(CommonLayoutParams.MATCH_WRAP_1);
|
||||
list.setAdapter(adapter);
|
||||
list.setOnItemClickListener(this);
|
||||
layout.addView(list);
|
||||
|
||||
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);
|
||||
|
||||
// Listen for messages and groups being added or removed
|
||||
db.addListener(this);
|
||||
// Bind to the service so we can wait for the DB to be opened
|
||||
bindService(new Intent(BriarService.class.getName()),
|
||||
serviceConnection, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
reloadMessageHeaders();
|
||||
}
|
||||
|
||||
private void reloadMessageHeaders() {
|
||||
final DatabaseComponent db = this.db;
|
||||
final GroupId groupId = this.groupId;
|
||||
dbExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
// Wait for the service to be bound and started
|
||||
serviceConnection.waitForStartup();
|
||||
// Load the message headers from the database
|
||||
Collection<GroupMessageHeader> headers =
|
||||
db.getMessageHeaders(groupId);
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Loaded " + headers.size() + " headers");
|
||||
// Update the conversation
|
||||
updateConversation(headers);
|
||||
} 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 service");
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateConversation(
|
||||
final Collection<GroupMessageHeader> headers) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
List<GroupMessageHeader> sort =
|
||||
new ArrayList<GroupMessageHeader>(headers);
|
||||
Collections.sort(sort, AscendingHeaderComparator.INSTANCE);
|
||||
int firstUnread = -1;
|
||||
adapter.clear();
|
||||
for(GroupMessageHeader h : sort) {
|
||||
if(firstUnread == -1 && !h.isRead())
|
||||
firstUnread = adapter.getCount();
|
||||
adapter.add(h);
|
||||
}
|
||||
if(firstUnread == -1) list.setSelection(adapter.getCount() - 1);
|
||||
else list.setSelection(firstUnread);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
db.removeListener(this);
|
||||
unbindService(serviceConnection);
|
||||
}
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
if(e instanceof MessageAddedEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
|
||||
reloadMessageHeaders();
|
||||
} else if(e instanceof MessageExpiredEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message removed, reloading");
|
||||
reloadMessageHeaders();
|
||||
} else if(e instanceof SubscriptionAddedEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group added, reloading");
|
||||
reloadMessageHeaders();
|
||||
} else if(e instanceof SubscriptionRemovedEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
|
||||
reloadMessageHeaders();
|
||||
}
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
Intent i = new Intent(this, WriteGroupMessageActivity.class);
|
||||
i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
|
||||
i.putExtra("net.sf.briar.GROUP_NAME", groupName);
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position,
|
||||
long id) {
|
||||
showMessage(position);
|
||||
}
|
||||
|
||||
private void showMessage(int position) {
|
||||
GroupMessageHeader item = adapter.getItem(position);
|
||||
Intent i = new Intent(this, ReadGroupMessageActivity.class);
|
||||
i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
|
||||
i.putExtra("net.sf.briar.GROUP_NAME", groupName);
|
||||
i.putExtra("net.sf.briar.MESSAGE_ID", item.getId().getBytes());
|
||||
Author author = item.getAuthor();
|
||||
if(author == null) {
|
||||
i.putExtra("net.sf.briar.ANONYMOUS", true);
|
||||
} else {
|
||||
i.putExtra("net.sf.briar.ANONYMOUS", false);
|
||||
i.putExtra("net.sf.briar.AUTHOR_ID", author.getId().getBytes());
|
||||
i.putExtra("net.sf.briar.AUTHOR_NAME", author.getName());
|
||||
}
|
||||
i.putExtra("net.sf.briar.CONTENT_TYPE", item.getContentType());
|
||||
i.putExtra("net.sf.briar.TIMESTAMP", item.getTimestamp());
|
||||
i.putExtra("net.sf.briar.FIRST", position == 0);
|
||||
i.putExtra("net.sf.briar.LAST", position == adapter.getCount() - 1);
|
||||
startActivityForResult(i, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int request, int result, Intent data) {
|
||||
if(result == ReadGroupMessageActivity.RESULT_PREV) {
|
||||
int position = request - 1;
|
||||
if(position >= 0 && position < adapter.getCount())
|
||||
showMessage(position);
|
||||
} else if(result == ReadGroupMessageActivity.RESULT_NEXT) {
|
||||
int position = request + 1;
|
||||
if(position >= 0 && position < adapter.getCount())
|
||||
showMessage(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package net.sf.briar.android.groups;
|
||||
|
||||
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 java.util.ArrayList;
|
||||
|
||||
import net.sf.briar.R;
|
||||
import net.sf.briar.android.widgets.CommonLayoutParams;
|
||||
import net.sf.briar.android.widgets.HorizontalSpace;
|
||||
import net.sf.briar.api.db.GroupMessageHeader;
|
||||
import net.sf.briar.api.messaging.Author;
|
||||
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 GroupAdapter extends ArrayAdapter<GroupMessageHeader> {
|
||||
|
||||
GroupAdapter(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();
|
||||
// FIXME: Use a RelativeLayout
|
||||
LinearLayout layout = new LinearLayout(ctx);
|
||||
layout.setOrientation(HORIZONTAL);
|
||||
layout.setGravity(CENTER_VERTICAL);
|
||||
|
||||
LinearLayout innerLayout = new LinearLayout(ctx);
|
||||
// Give me all the unused width
|
||||
innerLayout.setLayoutParams(CommonLayoutParams.WRAP_WRAP_1);
|
||||
innerLayout.setOrientation(VERTICAL);
|
||||
|
||||
Author author = item.getAuthor();
|
||||
|
||||
TextView name = new TextView(ctx);
|
||||
name.setTextSize(18);
|
||||
name.setMaxLines(1);
|
||||
name.setPadding(10, 10, 10, 10);
|
||||
Resources res = ctx.getResources();
|
||||
if(author == null) {
|
||||
name.setTextColor(res.getColor(R.color.anonymous_author));
|
||||
name.setText(R.string.anonymous);
|
||||
} else {
|
||||
name.setTextColor(res.getColor(R.color.pseudonymous_author));
|
||||
name.setText(author.getName());
|
||||
}
|
||||
innerLayout.addView(name);
|
||||
|
||||
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);
|
||||
subject.setText(item.getSubject());
|
||||
innerLayout.addView(subject);
|
||||
} else {
|
||||
LinearLayout innerInnerLayout = new LinearLayout(ctx);
|
||||
innerInnerLayout.setOrientation(HORIZONTAL);
|
||||
ImageView attachment = new ImageView(ctx);
|
||||
attachment.setPadding(10, 0, 10, 10);
|
||||
attachment.setImageResource(R.drawable.content_attachment);
|
||||
innerInnerLayout.addView(attachment);
|
||||
innerInnerLayout.addView(new HorizontalSpace(ctx));
|
||||
innerLayout.addView(innerInnerLayout);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
package net.sf.briar.android.groups;
|
||||
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
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.R;
|
||||
import net.sf.briar.android.BriarActivity;
|
||||
import net.sf.briar.android.BriarService;
|
||||
import net.sf.briar.android.BriarService.BriarServiceConnection;
|
||||
import net.sf.briar.android.DescendingHeaderComparator;
|
||||
import net.sf.briar.android.widgets.CommonLayoutParams;
|
||||
import net.sf.briar.android.widgets.HorizontalBorder;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
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.MessageAddedEvent;
|
||||
import net.sf.briar.api.db.event.MessageExpiredEvent;
|
||||
import net.sf.briar.api.db.event.SubscriptionAddedEvent;
|
||||
import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
|
||||
import net.sf.briar.api.messaging.Author;
|
||||
import net.sf.briar.api.messaging.AuthorFactory;
|
||||
import net.sf.briar.api.messaging.Group;
|
||||
import net.sf.briar.api.messaging.GroupFactory;
|
||||
import net.sf.briar.api.messaging.Message;
|
||||
import net.sf.briar.api.messaging.MessageFactory;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class GroupListActivity extends BriarActivity
|
||||
implements OnClickListener, DatabaseListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(GroupListActivity.class.getName());
|
||||
|
||||
private final BriarServiceConnection serviceConnection =
|
||||
new BriarServiceConnection();
|
||||
|
||||
@Inject private CryptoComponent crypto;
|
||||
@Inject private DatabaseComponent db;
|
||||
@Inject @DatabaseExecutor private Executor dbExecutor;
|
||||
@Inject private AuthorFactory authorFactory;
|
||||
@Inject private GroupFactory groupFactory;
|
||||
@Inject private MessageFactory messageFactory;
|
||||
|
||||
private GroupListAdapter adapter = null;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
super.onCreate(null);
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(CommonLayoutParams.MATCH_MATCH);
|
||||
layout.setOrientation(VERTICAL);
|
||||
layout.setGravity(CENTER_HORIZONTAL);
|
||||
|
||||
adapter = new GroupListAdapter(this);
|
||||
ListView list = new ListView(this);
|
||||
// Give me all the width and all the unused height
|
||||
list.setLayoutParams(CommonLayoutParams.MATCH_WRAP_1);
|
||||
list.setAdapter(adapter);
|
||||
list.setOnItemClickListener(adapter);
|
||||
layout.addView(list);
|
||||
|
||||
layout.addView(new HorizontalBorder(this));
|
||||
|
||||
ImageButton newGroupButton = new ImageButton(this);
|
||||
newGroupButton.setBackgroundResource(0);
|
||||
newGroupButton.setImageResource(R.drawable.social_new_chat);
|
||||
newGroupButton.setOnClickListener(this);
|
||||
layout.addView(newGroupButton);
|
||||
|
||||
setContentView(layout);
|
||||
|
||||
// Listen for messages and groups being added or removed
|
||||
db.addListener(this);
|
||||
// Bind to the service so we can wait for the DB to be opened
|
||||
bindService(new Intent(BriarService.class.getName()),
|
||||
serviceConnection, 0);
|
||||
|
||||
// Add some fake messages to the database in a background thread
|
||||
insertFakeMessages();
|
||||
}
|
||||
|
||||
// FIXME: Remove this
|
||||
private void insertFakeMessages() {
|
||||
final DatabaseComponent db = this.db;
|
||||
final GroupFactory groupFactory = this.groupFactory;
|
||||
final MessageFactory messageFactory = this.messageFactory;
|
||||
dbExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
// Wait for the service to be bound and started
|
||||
serviceConnection.waitForStartup();
|
||||
// If there are no groups in the DB, create some fake ones
|
||||
Collection<Group> groups = db.getSubscriptions();
|
||||
if(!groups.isEmpty()) return;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Inserting fake groups and messages");
|
||||
// We'll also need a contact to receive messages from
|
||||
ContactId contactId = db.addContact("Dave");
|
||||
// Finally, we'll need some authors for the messages
|
||||
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
||||
byte[] publicKey = keyPair.getPublic().getEncoded();
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
Author author = authorFactory.createAuthor("Batman",
|
||||
publicKey);
|
||||
Author author1 = authorFactory.createAuthor("Duckman",
|
||||
publicKey);
|
||||
// Insert some fake groups and make them visible
|
||||
Group group = groupFactory.createGroup("DisneyLeaks");
|
||||
db.subscribe(group);
|
||||
db.setVisibility(group.getId(), Arrays.asList(contactId));
|
||||
Group group1 = groupFactory.createGroup("Godwin's Lore");
|
||||
db.subscribe(group1);
|
||||
db.setVisibility(group1.getId(), Arrays.asList(contactId));
|
||||
// Insert some text messages to the groups
|
||||
for(int i = 0; i < 20; i++) {
|
||||
String body;
|
||||
if(i % 3 == 0) {
|
||||
body = "Message " + i + " is short.";
|
||||
} else {
|
||||
body = "Message " + i + " is long enough to wrap"
|
||||
+ " onto a second line on some screens.";
|
||||
}
|
||||
Group g = i % 2 == 0 ? group : group1;
|
||||
Message m;
|
||||
if(i % 5 == 0) {
|
||||
m = messageFactory.createAnonymousMessage(null, g,
|
||||
"text/plain", body.getBytes("UTF-8"));
|
||||
} else if(i % 5 == 2) {
|
||||
m = messageFactory.createPseudonymousMessage(null,
|
||||
g, author, privateKey, "text/plain",
|
||||
body.getBytes("UTF-8"));
|
||||
} else {
|
||||
m = messageFactory.createPseudonymousMessage(null,
|
||||
g, author1, privateKey, "text/plain",
|
||||
body.getBytes("UTF-8"));
|
||||
}
|
||||
if(Math.random() < 0.5) db.addLocalGroupMessage(m);
|
||||
else db.receiveMessage(contactId, m);
|
||||
db.setReadFlag(m.getId(), i % 4 == 0);
|
||||
}
|
||||
// Insert a non-text message
|
||||
Message m = messageFactory.createAnonymousMessage(null,
|
||||
group, "image/jpeg", new byte[1000]);
|
||||
db.receiveMessage(contactId, m);
|
||||
// Insert a long text message
|
||||
StringBuilder s = new StringBuilder();
|
||||
for(int i = 0; i < 100; i++)
|
||||
s.append("This is a very tedious message. ");
|
||||
String body = s.toString();
|
||||
m = messageFactory.createAnonymousMessage(m.getId(),
|
||||
group1, "text/plain", body.getBytes("UTF-8"));
|
||||
db.addLocalGroupMessage(m);
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
} catch(GeneralSecurityException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
} catch(InterruptedException e) {
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Interrupted while waiting for service");
|
||||
Thread.currentThread().interrupt();
|
||||
} catch(IOException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
reloadGroupList();
|
||||
}
|
||||
|
||||
private void reloadGroupList() {
|
||||
final DatabaseComponent db = this.db;
|
||||
dbExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
// Wait for the service to be bound and started
|
||||
serviceConnection.waitForStartup();
|
||||
// Load the groups and message headers from the DB
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Loading groups");
|
||||
Collection<Group> groups = db.getSubscriptions();
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Loaded " + groups.size() + " groups");
|
||||
List<GroupListItem> items = new ArrayList<GroupListItem>();
|
||||
for(Group g : groups) {
|
||||
// Filter out restricted groups
|
||||
if(g.getPublicKey() != null) continue;
|
||||
Collection<GroupMessageHeader> headers;
|
||||
try {
|
||||
headers = db.getMessageHeaders(g.getId());
|
||||
} catch(NoSuchSubscriptionException e) {
|
||||
// We'll reload the list when we get the event
|
||||
continue;
|
||||
}
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Loaded " + headers.size() + " headers");
|
||||
if(!headers.isEmpty())
|
||||
items.add(createItem(g, headers));
|
||||
}
|
||||
// Update the group list
|
||||
updateGroupList(Collections.unmodifiableList(items));
|
||||
} 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 service");
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private GroupListItem createItem(Group group,
|
||||
Collection<GroupMessageHeader> headers) {
|
||||
List<GroupMessageHeader> sort =
|
||||
new ArrayList<GroupMessageHeader>(headers);
|
||||
Collections.sort(sort, DescendingHeaderComparator.INSTANCE);
|
||||
return new GroupListItem(group, sort);
|
||||
}
|
||||
|
||||
private void updateGroupList(final Collection<GroupListItem> items) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
adapter.clear();
|
||||
for(GroupListItem i : items) adapter.add(i);
|
||||
adapter.sort(GroupComparator.INSTANCE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
db.removeListener(this);
|
||||
unbindService(serviceConnection);
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
startActivity(new Intent(this, WriteGroupMessageActivity.class));
|
||||
}
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
if(e instanceof MessageAddedEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
|
||||
reloadGroupList();
|
||||
} else if(e instanceof MessageExpiredEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message removed, reloading");
|
||||
reloadGroupList();
|
||||
} else if(e instanceof SubscriptionAddedEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group added, reloading");
|
||||
reloadGroupList();
|
||||
} else if(e instanceof SubscriptionRemovedEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
|
||||
reloadGroupList();
|
||||
}
|
||||
}
|
||||
|
||||
private static class GroupComparator implements Comparator<GroupListItem> {
|
||||
|
||||
private static final GroupComparator INSTANCE = new GroupComparator();
|
||||
|
||||
public int compare(GroupListItem a, GroupListItem b) {
|
||||
return String.CASE_INSENSITIVE_ORDER.compare(a.getGroupName(),
|
||||
b.getGroupName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package net.sf.briar.android.groups;
|
||||
|
||||
import static android.graphics.Typeface.BOLD;
|
||||
import static android.view.Gravity.CENTER_VERTICAL;
|
||||
import static android.view.Gravity.LEFT;
|
||||
import static android.widget.LinearLayout.HORIZONTAL;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import static java.text.DateFormat.SHORT;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.sf.briar.android.widgets.CommonLayoutParams;
|
||||
import net.sf.briar.util.StringUtils;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
class GroupListAdapter extends ArrayAdapter<GroupListItem>
|
||||
implements OnItemClickListener {
|
||||
|
||||
GroupListAdapter(Context ctx) {
|
||||
super(ctx, android.R.layout.simple_expandable_list_item_1,
|
||||
new ArrayList<GroupListItem>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
GroupListItem item = getItem(position);
|
||||
Context ctx = getContext();
|
||||
LinearLayout layout = new LinearLayout(ctx);
|
||||
layout.setOrientation(HORIZONTAL);
|
||||
layout.setGravity(CENTER_VERTICAL);
|
||||
|
||||
LinearLayout innerLayout = new LinearLayout(ctx);
|
||||
// Give me all the unused width
|
||||
innerLayout.setLayoutParams(CommonLayoutParams.WRAP_WRAP_1);
|
||||
innerLayout.setOrientation(VERTICAL);
|
||||
innerLayout.setGravity(LEFT);
|
||||
|
||||
TextView name = new TextView(ctx);
|
||||
name.setTextSize(18);
|
||||
name.setMaxLines(1);
|
||||
name.setPadding(10, 10, 10, 10);
|
||||
int unread = item.getUnreadCount();
|
||||
if(unread > 0) name.setText(item.getGroupName() + " (" + unread + ")");
|
||||
else name.setText(item.getGroupName());
|
||||
innerLayout.addView(name);
|
||||
|
||||
if(!StringUtils.isNullOrEmpty(item.getSubject())) {
|
||||
TextView subject = new TextView(ctx);
|
||||
subject.setTextSize(14);
|
||||
subject.setMaxLines(2);
|
||||
subject.setPadding(10, 0, 10, 10);
|
||||
if(unread > 0) subject.setTypeface(null, BOLD);
|
||||
subject.setText(item.getSubject());
|
||||
innerLayout.addView(subject);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position,
|
||||
long id) {
|
||||
GroupListItem item = getItem(position);
|
||||
Intent i = new Intent(getContext(), GroupActivity.class);
|
||||
i.putExtra("net.sf.briar.GROUP_ID", item.getGroupId().getBytes());
|
||||
i.putExtra("net.sf.briar.GROUP_NAME", item.getGroupName());
|
||||
getContext().startActivity(i);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package net.sf.briar.android.groups;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.briar.android.DescendingHeaderComparator;
|
||||
import net.sf.briar.api.db.GroupMessageHeader;
|
||||
import net.sf.briar.api.messaging.Author;
|
||||
import net.sf.briar.api.messaging.Group;
|
||||
import net.sf.briar.api.messaging.GroupId;
|
||||
|
||||
class GroupListItem {
|
||||
|
||||
private final Group group;
|
||||
private final String author, subject;
|
||||
private final long timestamp;
|
||||
private final int unread;
|
||||
|
||||
GroupListItem(Group group, List<GroupMessageHeader> headers) {
|
||||
if(headers.isEmpty()) throw new IllegalArgumentException();
|
||||
this.group = group;
|
||||
Collections.sort(headers, DescendingHeaderComparator.INSTANCE);
|
||||
GroupMessageHeader newest = headers.get(0);
|
||||
Author a = newest.getAuthor();
|
||||
if(a == null) author = null;
|
||||
else author = a.getName();
|
||||
subject = newest.getSubject();
|
||||
timestamp = newest.getTimestamp();
|
||||
int unread = 0;
|
||||
for(GroupMessageHeader h : headers) if(!h.isRead()) unread++;
|
||||
this.unread = unread;
|
||||
}
|
||||
|
||||
GroupId getGroupId() {
|
||||
return group.getId();
|
||||
}
|
||||
|
||||
String getGroupName() {
|
||||
return group.getName();
|
||||
}
|
||||
|
||||
String getAuthorName() {
|
||||
return author;
|
||||
}
|
||||
|
||||
String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
int getUnreadCount() {
|
||||
return unread;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package net.sf.briar.android.groups;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.sf.briar.api.messaging.Group;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.SpinnerAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
class GroupNameSpinnerAdapter extends ArrayAdapter<Group>
|
||||
implements SpinnerAdapter {
|
||||
|
||||
GroupNameSpinnerAdapter(Context context) {
|
||||
super(context, android.R.layout.simple_spinner_item,
|
||||
new ArrayList<Group>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
TextView name = new TextView(getContext());
|
||||
name.setTextSize(18);
|
||||
name.setMaxLines(1);
|
||||
name.setPadding(10, 10, 10, 10);
|
||||
name.setText(getItem(position).getName());
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getDropDownView(int position, View convertView,
|
||||
ViewGroup parent) {
|
||||
return getView(position, convertView, parent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
package net.sf.briar.android.groups;
|
||||
|
||||
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 java.io.UnsupportedEncodingException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.sf.briar.R;
|
||||
import net.sf.briar.android.BriarActivity;
|
||||
import net.sf.briar.android.BriarService;
|
||||
import net.sf.briar.android.BriarService.BriarServiceConnection;
|
||||
import net.sf.briar.android.widgets.CommonLayoutParams;
|
||||
import net.sf.briar.android.widgets.HorizontalBorder;
|
||||
import net.sf.briar.android.widgets.HorizontalSpace;
|
||||
import net.sf.briar.api.android.BundleEncrypter;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.messaging.AuthorId;
|
||||
import net.sf.briar.api.messaging.GroupId;
|
||||
import net.sf.briar.api.messaging.MessageId;
|
||||
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.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class ReadGroupMessageActivity extends BriarActivity
|
||||
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(ReadGroupMessageActivity.class.getName());
|
||||
|
||||
private final BriarServiceConnection serviceConnection =
|
||||
new BriarServiceConnection();
|
||||
|
||||
@Inject private BundleEncrypter bundleEncrypter;
|
||||
@Inject private DatabaseComponent db;
|
||||
@Inject @DatabaseExecutor private Executor dbExecutor;
|
||||
|
||||
private GroupId groupId = null;
|
||||
private MessageId messageId = null;
|
||||
private AuthorId authorId = null;
|
||||
private String authorName = null;
|
||||
private boolean read;
|
||||
private ImageButton readButton = null, prevButton = null, nextButton = null;
|
||||
private ImageButton replyButton = null;
|
||||
private TextView content = null;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
super.onCreate(null);
|
||||
|
||||
Intent i = getIntent();
|
||||
byte[] id = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
|
||||
if(id == null) throw new IllegalStateException();
|
||||
groupId = new GroupId(id);
|
||||
String groupName = i.getStringExtra("net.sf.briar.GROUP_NAME");
|
||||
if(groupName == null) throw new IllegalStateException();
|
||||
setTitle(groupName);
|
||||
id = i.getByteArrayExtra("net.sf.briar.MESSAGE_ID");
|
||||
if(id == null) throw new IllegalStateException();
|
||||
messageId = new MessageId(id);
|
||||
boolean anonymous = i.getBooleanExtra("net.sf.briar.ANONYMOUS", false);
|
||||
if(!anonymous) {
|
||||
id = i.getByteArrayExtra("net.sf.briar.AUTHOR_ID");
|
||||
if(id == null) throw new IllegalStateException();
|
||||
authorId = new AuthorId(id);
|
||||
authorName = i.getStringExtra("net.sf.briar.AUTHOR_NAME");
|
||||
if(authorName == null) throw new IllegalStateException();
|
||||
}
|
||||
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();
|
||||
boolean first = i.getBooleanExtra("net.sf.briar.FIRST", false);
|
||||
boolean last = i.getBooleanExtra("net.sf.briar.LAST", false);
|
||||
|
||||
if(state != null && bundleEncrypter.decrypt(state)) {
|
||||
read = state.getBoolean("net.sf.briar.READ");
|
||||
} else {
|
||||
read = false;
|
||||
setReadInDatabase(true);
|
||||
}
|
||||
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(CommonLayoutParams.MATCH_WRAP);
|
||||
layout.setOrientation(VERTICAL);
|
||||
|
||||
ScrollView scrollView = new ScrollView(this);
|
||||
// Give me all the width and all the unused height
|
||||
scrollView.setLayoutParams(CommonLayoutParams.MATCH_WRAP_1);
|
||||
|
||||
LinearLayout message = new LinearLayout(this);
|
||||
message.setOrientation(VERTICAL);
|
||||
|
||||
LinearLayout header = new LinearLayout(this);
|
||||
header.setLayoutParams(CommonLayoutParams.MATCH_WRAP);
|
||||
header.setOrientation(HORIZONTAL);
|
||||
header.setGravity(CENTER_VERTICAL);
|
||||
|
||||
TextView author = new TextView(this);
|
||||
// Give me all the unused width
|
||||
author.setLayoutParams(CommonLayoutParams.WRAP_WRAP_1);
|
||||
author.setTextSize(18);
|
||||
author.setMaxLines(1);
|
||||
author.setPadding(10, 10, 10, 10);
|
||||
Resources res = getResources();
|
||||
if(authorName == null) {
|
||||
author.setTextColor(res.getColor(R.color.anonymous_author));
|
||||
author.setText(R.string.anonymous);
|
||||
} else {
|
||||
author.setTextColor(res.getColor(R.color.pseudonymous_author));
|
||||
author.setText(authorName);
|
||||
}
|
||||
header.addView(author);
|
||||
|
||||
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(CommonLayoutParams.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);
|
||||
prevButton.setEnabled(!first);
|
||||
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);
|
||||
nextButton.setEnabled(!last);
|
||||
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);
|
||||
|
||||
// Bind to the service so we can wait for the DB to be opened
|
||||
bindService(new Intent(BriarService.class.getName()),
|
||||
serviceConnection, 0);
|
||||
}
|
||||
|
||||
private void setReadInDatabase(final boolean read) {
|
||||
final DatabaseComponent db = this.db;
|
||||
final MessageId messageId = this.messageId;
|
||||
dbExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
serviceConnection.waitForStartup();
|
||||
db.setReadFlag(messageId, read);
|
||||
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 service");
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setReadInUi(final boolean read) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
ReadGroupMessageActivity.this.read = read;
|
||||
if(read) readButton.setImageResource(R.drawable.content_unread);
|
||||
else readButton.setImageResource(R.drawable.content_read);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void loadMessageBody() {
|
||||
final DatabaseComponent db = this.db;
|
||||
final MessageId messageId = this.messageId;
|
||||
dbExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
serviceConnection.waitForStartup();
|
||||
byte[] body = db.getMessageBody(messageId);
|
||||
final String text = new String(body, "UTF-8");
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
content.setText(text);
|
||||
}
|
||||
});
|
||||
} 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 service");
|
||||
Thread.currentThread().interrupt();
|
||||
} catch(UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle state) {
|
||||
state.putBoolean("net.sf.briar.READ", read);
|
||||
bundleEncrypter.encrypt(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
unbindService(serviceConnection);
|
||||
}
|
||||
|
||||
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) {
|
||||
Intent i = new Intent(this, WriteGroupMessageActivity.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
package net.sf.briar.android.groups;
|
||||
|
||||
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 java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
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.BriarActivity;
|
||||
import net.sf.briar.android.BriarService;
|
||||
import net.sf.briar.android.BriarService.BriarServiceConnection;
|
||||
import net.sf.briar.android.widgets.CommonLayoutParams;
|
||||
import net.sf.briar.android.widgets.HorizontalSpace;
|
||||
import net.sf.briar.api.android.BundleEncrypter;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.messaging.Group;
|
||||
import net.sf.briar.api.messaging.GroupId;
|
||||
import net.sf.briar.api.messaging.Message;
|
||||
import net.sf.briar.api.messaging.MessageFactory;
|
||||
import net.sf.briar.api.messaging.MessageId;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
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 WriteGroupMessageActivity extends BriarActivity
|
||||
implements OnClickListener, OnItemSelectedListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(WriteGroupMessageActivity.class.getName());
|
||||
|
||||
private final BriarServiceConnection serviceConnection =
|
||||
new BriarServiceConnection();
|
||||
|
||||
@Inject private BundleEncrypter bundleEncrypter;
|
||||
@Inject private DatabaseComponent db;
|
||||
@Inject @DatabaseExecutor private Executor dbExecutor;
|
||||
@Inject private MessageFactory messageFactory;
|
||||
|
||||
private Group group = null;
|
||||
private GroupId groupId = null;
|
||||
private MessageId parentId = null;
|
||||
private GroupNameSpinnerAdapter adapter = null;
|
||||
private Spinner spinner = null;
|
||||
private ImageButton sendButton = null;
|
||||
private EditText content = null;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
super.onCreate(null);
|
||||
|
||||
Intent i = getIntent();
|
||||
byte[] id = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
|
||||
if(id != null) groupId = new GroupId(id);
|
||||
id = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
|
||||
if(id != null) parentId = new MessageId(id);
|
||||
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(CommonLayoutParams.MATCH_WRAP);
|
||||
layout.setOrientation(VERTICAL);
|
||||
|
||||
LinearLayout actionBar = new LinearLayout(this);
|
||||
actionBar.setLayoutParams(CommonLayoutParams.MATCH_WRAP);
|
||||
actionBar.setOrientation(HORIZONTAL);
|
||||
actionBar.setGravity(CENTER_VERTICAL);
|
||||
|
||||
TextView to = new TextView(this);
|
||||
to.setTextSize(18);
|
||||
to.setPadding(10, 10, 10, 10);
|
||||
to.setText(R.string.to);
|
||||
actionBar.addView(to);
|
||||
|
||||
adapter = new GroupNameSpinnerAdapter(this);
|
||||
spinner = new Spinner(this);
|
||||
spinner.setAdapter(adapter);
|
||||
spinner.setOnItemSelectedListener(this);
|
||||
loadContactNames();
|
||||
actionBar.addView(spinner);
|
||||
|
||||
actionBar.addView(new HorizontalSpace(this));
|
||||
|
||||
sendButton = new ImageButton(this);
|
||||
sendButton.setBackgroundResource(0);
|
||||
sendButton.setImageResource(R.drawable.social_send_now);
|
||||
sendButton.setEnabled(false);
|
||||
sendButton.setOnClickListener(this);
|
||||
actionBar.addView(sendButton);
|
||||
layout.addView(actionBar);
|
||||
|
||||
content = new EditText(this);
|
||||
content.setPadding(10, 10, 10, 10);
|
||||
if(state != null && bundleEncrypter.decrypt(state)) {
|
||||
Parcelable p = state.getParcelable("net.sf.briar.CONTENT");
|
||||
if(p != null) content.onRestoreInstanceState(p);
|
||||
}
|
||||
layout.addView(content);
|
||||
|
||||
setContentView(layout);
|
||||
|
||||
// Bind to the service so we can wait for the DB to be opened
|
||||
bindService(new Intent(BriarService.class.getName()),
|
||||
serviceConnection, 0);
|
||||
}
|
||||
|
||||
private void loadContactNames() {
|
||||
final DatabaseComponent db = this.db;
|
||||
dbExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
serviceConnection.waitForStartup();
|
||||
final Collection<Group> groups = db.getSubscriptions();
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
for(Group g : groups) {
|
||||
if(g.getId().equals(groupId)) {
|
||||
group = g;
|
||||
spinner.setSelection(adapter.getCount());
|
||||
}
|
||||
adapter.add(g);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
} catch(InterruptedException e) {
|
||||
LOG.info("Interrupted while waiting for service");
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle state) {
|
||||
Parcelable p = content.onSaveInstanceState();
|
||||
state.putParcelable("net.sf.briar.CONTENT", p);
|
||||
bundleEncrypter.encrypt(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
unbindService(serviceConnection);
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
if(group == null) throw new IllegalStateException();
|
||||
try {
|
||||
storeMessage(content.getText().toString().getBytes("UTF-8"));
|
||||
} catch(UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
private void storeMessage(final byte[] body) {
|
||||
final DatabaseComponent db = this.db;
|
||||
final MessageFactory messageFactory = this.messageFactory;
|
||||
final Group group = this.group;
|
||||
final MessageId parentId = this.parentId;
|
||||
dbExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
serviceConnection.waitForStartup();
|
||||
Message m = messageFactory.createAnonymousMessage(parentId,
|
||||
group, "text/plain", body);
|
||||
db.addLocalGroupMessage(m);
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
} catch(GeneralSecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch(InterruptedException e) {
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Interrupted while waiting for service");
|
||||
Thread.currentThread().interrupt();
|
||||
} catch(IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position,
|
||||
long id) {
|
||||
group = adapter.getItem(position);
|
||||
groupId = group.getId();
|
||||
sendButton.setEnabled(true);
|
||||
}
|
||||
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
group = null;
|
||||
groupId = null;
|
||||
sendButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import net.sf.briar.R;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
@@ -45,9 +44,9 @@ public class ConnectionView extends AddContactView {
|
||||
innerLayout.addView(progress);
|
||||
|
||||
TextView connecting = new TextView(ctx);
|
||||
Resources res = getResources();
|
||||
String connectingVia = res.getString(R.string.connecting_wifi);
|
||||
connecting.setText(String.format(connectingVia, networkName));
|
||||
String format = getResources().getString(
|
||||
R.string.format_connecting_wifi);
|
||||
connecting.setText(String.format(format, networkName));
|
||||
innerLayout.addView(connecting);
|
||||
|
||||
addView(innerLayout);
|
||||
|
||||
@@ -7,7 +7,6 @@ import net.sf.briar.R;
|
||||
import net.sf.briar.android.widgets.CommonLayoutParams;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.view.View;
|
||||
@@ -70,9 +69,9 @@ public class WifiWidget extends LinearLayout implements OnClickListener {
|
||||
ok.setImageResource(R.drawable.navigation_accept);
|
||||
ok.setPadding(10, 10, 10, 10);
|
||||
addView(ok);
|
||||
Resources res = getResources();
|
||||
String connected = res.getString(R.string.wifi_connected);
|
||||
status.setText(String.format(connected, networkName));
|
||||
String format = getResources().getString(
|
||||
R.string.format_wifi_connected);
|
||||
status.setText(String.format(format, networkName));
|
||||
addView(status);
|
||||
ImageButton settings = new ImageButton(ctx);
|
||||
settings.setImageResource(R.drawable.action_settings);
|
||||
|
||||
@@ -22,6 +22,7 @@ implements SpinnerAdapter {
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
TextView name = new TextView(getContext());
|
||||
name.setTextSize(18);
|
||||
name.setMaxLines(1);
|
||||
name.setPadding(10, 10, 10, 10);
|
||||
name.setText(getItem(position).getName());
|
||||
return name;
|
||||
|
||||
@@ -67,6 +67,7 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
|
||||
contactId = new ContactId(id);
|
||||
contactName = i.getStringExtra("net.sf.briar.CONTACT_NAME");
|
||||
if(contactName == null) throw new IllegalStateException();
|
||||
setTitle(contactName);
|
||||
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(CommonLayoutParams.MATCH_MATCH);
|
||||
@@ -104,23 +105,6 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
|
||||
reloadMessageHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
db.removeListener(this);
|
||||
unbindService(serviceConnection);
|
||||
}
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
if(e instanceof MessageAddedEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
|
||||
reloadMessageHeaders();
|
||||
} else if(e instanceof MessageExpiredEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message removed, reloading");
|
||||
reloadMessageHeaders();
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadMessageHeaders() {
|
||||
final DatabaseComponent db = this.db;
|
||||
final ContactId contactId = this.contactId;
|
||||
@@ -168,8 +152,38 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int request, int result, Intent data) {
|
||||
if(result == ReadPrivateMessageActivity.RESULT_PREV) {
|
||||
int position = request - 1;
|
||||
if(position >= 0 && position < adapter.getCount())
|
||||
showMessage(position);
|
||||
} else if(result == ReadPrivateMessageActivity.RESULT_NEXT) {
|
||||
int position = request + 1;
|
||||
if(position >= 0 && position < adapter.getCount())
|
||||
showMessage(position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
db.removeListener(this);
|
||||
unbindService(serviceConnection);
|
||||
}
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
if(e instanceof MessageAddedEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
|
||||
reloadMessageHeaders();
|
||||
} else if(e instanceof MessageExpiredEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message removed, reloading");
|
||||
reloadMessageHeaders();
|
||||
}
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
Intent i = new Intent(this, WriteMessageActivity.class);
|
||||
Intent i = new Intent(this, WritePrivateMessageActivity.class);
|
||||
i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt());
|
||||
startActivity(i);
|
||||
}
|
||||
@@ -181,28 +195,15 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
|
||||
|
||||
private void showMessage(int position) {
|
||||
PrivateMessageHeader item = adapter.getItem(position);
|
||||
Intent i = new Intent(this, ReadMessageActivity.class);
|
||||
Intent i = new Intent(this, ReadPrivateMessageActivity.class);
|
||||
i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt());
|
||||
i.putExtra("net.sf.briar.CONTACT_NAME", contactName);
|
||||
i.putExtra("net.sf.briar.MESSAGE_ID", item.getId().getBytes());
|
||||
i.putExtra("net.sf.briar.CONTENT_TYPE", item.getContentType());
|
||||
i.putExtra("net.sf.briar.TIMESTAMP", item.getTimestamp());
|
||||
i.putExtra("net.sf.briar.INCOMING", item.isIncoming());
|
||||
i.putExtra("net.sf.briar.FIRST", position == 0);
|
||||
i.putExtra("net.sf.briar.LAST", position == adapter.getCount() - 1);
|
||||
i.putExtra("net.sf.briar.STARRED", item.isStarred());
|
||||
startActivityForResult(i, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int request, int result, Intent data) {
|
||||
if(result == ReadMessageActivity.RESULT_PREV) {
|
||||
int position = request - 1;
|
||||
if(position >= 0 && position < adapter.getCount())
|
||||
showMessage(position);
|
||||
} else if(result == ReadMessageActivity.RESULT_NEXT) {
|
||||
int position = request + 1;
|
||||
if(position >= 0 && position < adapter.getCount())
|
||||
showMessage(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.ArrayList;
|
||||
|
||||
import net.sf.briar.R;
|
||||
import net.sf.briar.android.widgets.CommonLayoutParams;
|
||||
import net.sf.briar.android.widgets.HorizontalSpace;
|
||||
import net.sf.briar.api.db.PrivateMessageHeader;
|
||||
import android.content.Context;
|
||||
import android.text.format.DateUtils;
|
||||
@@ -34,23 +35,24 @@ class ConversationAdapter extends ArrayAdapter<PrivateMessageHeader> {
|
||||
layout.setOrientation(HORIZONTAL);
|
||||
layout.setGravity(CENTER_VERTICAL);
|
||||
|
||||
if(!item.getContentType().equals("text/plain")) {
|
||||
if(item.getContentType().equals("text/plain")) {
|
||||
TextView subject = new TextView(ctx);
|
||||
// Give me all the unused width
|
||||
subject.setLayoutParams(CommonLayoutParams.WRAP_WRAP_1);
|
||||
subject.setTextSize(14);
|
||||
subject.setMaxLines(2);
|
||||
subject.setPadding(10, 10, 10, 10);
|
||||
if(!item.isRead()) subject.setTypeface(null, BOLD);
|
||||
subject.setText(item.getSubject());
|
||||
layout.addView(subject);
|
||||
} else {
|
||||
ImageView attachment = new ImageView(ctx);
|
||||
attachment.setPadding(10, 10, 10, 10);
|
||||
attachment.setImageResource(R.drawable.content_attachment);
|
||||
layout.addView(attachment);
|
||||
layout.addView(new HorizontalSpace(ctx));
|
||||
}
|
||||
|
||||
TextView subject = new TextView(ctx);
|
||||
// Give me all the unused width
|
||||
subject.setLayoutParams(CommonLayoutParams.WRAP_WRAP_1);
|
||||
subject.setTextSize(14);
|
||||
subject.setMaxLines(2);
|
||||
subject.setPadding(10, 10, 10, 10);
|
||||
if(!item.isRead()) subject.setTypeface(null, BOLD);
|
||||
subject.setText(item.getSubject());
|
||||
layout.addView(subject);
|
||||
|
||||
TextView date = new TextView(ctx);
|
||||
date.setTextSize(14);
|
||||
date.setPadding(0, 10, 10, 10);
|
||||
|
||||
@@ -238,7 +238,7 @@ implements OnClickListener, DatabaseListener {
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
startActivity(new Intent(this, WriteMessageActivity.class));
|
||||
startActivity(new Intent(this, WritePrivateMessageActivity.class));
|
||||
}
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
|
||||
@@ -46,10 +46,12 @@ implements OnItemClickListener {
|
||||
|
||||
TextView name = new TextView(ctx);
|
||||
name.setTextSize(18);
|
||||
name.setMaxLines(1);
|
||||
name.setPadding(10, 10, 10, 10);
|
||||
int unread = item.getUnreadCount();
|
||||
if(unread > 0) name.setText(item.getName() + " (" + unread + ")");
|
||||
else name.setText(item.getName());
|
||||
String contactName = item.getContactName();
|
||||
if(unread > 0) name.setText(contactName + " (" + unread + ")");
|
||||
else name.setText(contactName);
|
||||
innerLayout.addView(name);
|
||||
|
||||
if(!StringUtils.isNullOrEmpty(item.getSubject())) {
|
||||
@@ -78,7 +80,7 @@ implements OnItemClickListener {
|
||||
ConversationListItem item = getItem(position);
|
||||
Intent i = new Intent(getContext(), ConversationActivity.class);
|
||||
i.putExtra("net.sf.briar.CONTACT_ID", item.getContactId().getInt());
|
||||
i.putExtra("net.sf.briar.CONTACT_NAME", item.getName());
|
||||
i.putExtra("net.sf.briar.CONTACT_NAME", item.getContactName());
|
||||
getContext().startActivity(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class ConversationListItem {
|
||||
return contact.getId();
|
||||
}
|
||||
|
||||
String getName() {
|
||||
String getContactName() {
|
||||
return contact.getName();
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ import android.widget.TextView;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class ReadMessageActivity extends BriarActivity
|
||||
public class ReadPrivateMessageActivity extends BriarActivity
|
||||
implements OnClickListener {
|
||||
|
||||
static final int RESULT_REPLY = RESULT_FIRST_USER;
|
||||
@@ -45,7 +45,7 @@ implements OnClickListener {
|
||||
static final int RESULT_NEXT = RESULT_FIRST_USER + 2;
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ReadMessageActivity.class.getName());
|
||||
Logger.getLogger(ReadPrivateMessageActivity.class.getName());
|
||||
|
||||
private final BriarServiceConnection serviceConnection =
|
||||
new BriarServiceConnection();
|
||||
@@ -55,11 +55,9 @@ implements OnClickListener {
|
||||
@Inject @DatabaseExecutor private Executor dbExecutor;
|
||||
|
||||
private ContactId contactId = null;
|
||||
private String contactName = null;
|
||||
private MessageId messageId = null;
|
||||
private boolean first, last, starred, read;
|
||||
private ImageButton readButton = null;
|
||||
private ImageButton prevButton = null, nextButton = null;
|
||||
private boolean read;
|
||||
private ImageButton readButton = null, prevButton = null, nextButton = null;
|
||||
private ImageButton replyButton = null;
|
||||
private TextView content = null;
|
||||
|
||||
@@ -71,8 +69,9 @@ implements OnClickListener {
|
||||
int cid = i.getIntExtra("net.sf.briar.CONTACT_ID", -1);
|
||||
if(cid == -1) throw new IllegalStateException();
|
||||
contactId = new ContactId(cid);
|
||||
contactName = i.getStringExtra("net.sf.briar.CONTACT_NAME");
|
||||
String contactName = i.getStringExtra("net.sf.briar.CONTACT_NAME");
|
||||
if(contactName == null) throw new IllegalStateException();
|
||||
setTitle(contactName);
|
||||
byte[] mid = i.getByteArrayExtra("net.sf.briar.MESSAGE_ID");
|
||||
if(mid == null) throw new IllegalStateException();
|
||||
messageId = new MessageId(mid);
|
||||
@@ -80,14 +79,13 @@ implements OnClickListener {
|
||||
if(contentType == null) throw new IllegalStateException();
|
||||
long timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
|
||||
if(timestamp == -1) throw new IllegalStateException();
|
||||
first = i.getBooleanExtra("net.sf.briar.FIRST", false);
|
||||
last = i.getBooleanExtra("net.sf.briar.LAST", false);
|
||||
boolean incoming = i.getBooleanExtra("net.sf.briar.INCOMING", false);
|
||||
boolean first = i.getBooleanExtra("net.sf.briar.FIRST", false);
|
||||
boolean last = i.getBooleanExtra("net.sf.briar.LAST", false);
|
||||
|
||||
if(state != null && bundleEncrypter.decrypt(state)) {
|
||||
starred = state.getBoolean("net.sf.briar.STARRED");
|
||||
read = state.getBoolean("net.sf.briar.READ");
|
||||
} else {
|
||||
starred = i.getBooleanExtra("net.sf.briar.STARRED", false);
|
||||
read = false;
|
||||
setReadInDatabase(true);
|
||||
}
|
||||
@@ -112,8 +110,11 @@ implements OnClickListener {
|
||||
// Give me all the unused width
|
||||
name.setLayoutParams(CommonLayoutParams.WRAP_WRAP_1);
|
||||
name.setTextSize(18);
|
||||
name.setMaxLines(1);
|
||||
name.setPadding(10, 10, 10, 10);
|
||||
String format = getResources().getString(R.string.message_from);
|
||||
String format;
|
||||
if(incoming) format = getResources().getString(R.string.format_from);
|
||||
else format = getResources().getString(R.string.format_to);
|
||||
name.setText(String.format(format, contactName));
|
||||
header.addView(name);
|
||||
|
||||
@@ -204,7 +205,7 @@ implements OnClickListener {
|
||||
private void setReadInUi(final boolean read) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
ReadMessageActivity.this.read = read;
|
||||
ReadPrivateMessageActivity.this.read = read;
|
||||
if(read) readButton.setImageResource(R.drawable.content_unread);
|
||||
else readButton.setImageResource(R.drawable.content_read);
|
||||
}
|
||||
@@ -241,7 +242,6 @@ implements OnClickListener {
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle state) {
|
||||
state.putBoolean("net.sf.briar.STARRED", starred);
|
||||
state.putBoolean("net.sf.briar.READ", read);
|
||||
bundleEncrypter.encrypt(state);
|
||||
}
|
||||
@@ -262,7 +262,7 @@ implements OnClickListener {
|
||||
setResult(RESULT_NEXT);
|
||||
finish();
|
||||
} else if(view == replyButton) {
|
||||
Intent i = new Intent(this, WriteMessageActivity.class);
|
||||
Intent i = new Intent(this, WritePrivateMessageActivity.class);
|
||||
i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt());
|
||||
i.putExtra("net.sf.briar.PARENT_ID", messageId.getBytes());
|
||||
startActivity(i);
|
||||
@@ -7,6 +7,7 @@ import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -42,11 +43,11 @@ import android.widget.TextView;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class WriteMessageActivity extends BriarActivity
|
||||
public class WritePrivateMessageActivity extends BriarActivity
|
||||
implements OnClickListener, OnItemSelectedListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(WriteMessageActivity.class.getName());
|
||||
Logger.getLogger(WritePrivateMessageActivity.class.getName());
|
||||
|
||||
private final BriarServiceConnection serviceConnection =
|
||||
new BriarServiceConnection();
|
||||
@@ -85,7 +86,7 @@ implements OnClickListener, OnItemSelectedListener {
|
||||
TextView to = new TextView(this);
|
||||
to.setTextSize(18);
|
||||
to.setPadding(10, 10, 10, 10);
|
||||
to.setText(R.string.message_to);
|
||||
to.setText(R.string.to);
|
||||
actionBar.addView(to);
|
||||
|
||||
adapter = new ContactNameSpinnerAdapter(this);
|
||||
@@ -163,32 +164,36 @@ implements OnClickListener, OnItemSelectedListener {
|
||||
public void onClick(View view) {
|
||||
if(contactId == null) throw new IllegalStateException();
|
||||
try {
|
||||
byte[] body = content.getText().toString().getBytes("UTF-8");
|
||||
storeMessage(messageFactory.createPrivateMessage(parentId,
|
||||
"text/plain", body));
|
||||
} catch(IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch(GeneralSecurityException e) {
|
||||
storeMessage(content.getText().toString().getBytes("UTF-8"));
|
||||
} catch(UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
private void storeMessage(final Message m) {
|
||||
private void storeMessage(final byte[] body) {
|
||||
final DatabaseComponent db = this.db;
|
||||
final MessageFactory messageFactory = this.messageFactory;
|
||||
final ContactId contactId = this.contactId;
|
||||
final MessageId parentId = this.parentId;
|
||||
dbExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
serviceConnection.waitForStartup();
|
||||
Message m = messageFactory.createPrivateMessage(parentId,
|
||||
"text/plain", body);
|
||||
db.addLocalPrivateMessage(m, contactId);
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
} catch(GeneralSecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch(InterruptedException e) {
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Interrupted while waiting for service");
|
||||
Thread.currentThread().interrupt();
|
||||
} catch(IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -13,6 +13,6 @@ public class HorizontalBorder extends View {
|
||||
public HorizontalBorder(Context ctx) {
|
||||
super(ctx);
|
||||
setLayoutParams(new LayoutParams(MATCH_PARENT, LINE_WIDTH));
|
||||
setBackgroundColor(getResources().getColor(R.color.HorizontalBorder));
|
||||
setBackgroundColor(getResources().getColor(R.color.horizontal_border));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user