diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml index 6b12578c8ce86fa3baf171ca742e1740819d6be8..5dbdd9771386846f1dcc3426e70ab1cdb7366949 100644 --- a/briar-android/AndroidManifest.xml +++ b/briar-android/AndroidManifest.xml @@ -52,5 +52,9 @@ android:name=".android.messages.ReadMessageActivity" android:label="@string/messages_title" > </activity> + <activity + android:name=".android.messages.WriteMessageActivity" + android:label="@string/compose_title" > + </activity> </application> </manifest> diff --git a/briar-android/res/drawable-hdpi/social_send_now.png b/briar-android/res/drawable-hdpi/social_send_now.png new file mode 100644 index 0000000000000000000000000000000000000000..6bdd9585fcfe188ec4a81c81b2861f2be0883499 Binary files /dev/null and b/briar-android/res/drawable-hdpi/social_send_now.png differ diff --git a/briar-android/res/drawable-mdpi/social_send_now.png b/briar-android/res/drawable-mdpi/social_send_now.png new file mode 100644 index 0000000000000000000000000000000000000000..515668a6b6c37f0c93f737def79a203e083b7d61 Binary files /dev/null and b/briar-android/res/drawable-mdpi/social_send_now.png differ diff --git a/briar-android/res/drawable-xhdpi/social_send_now.png b/briar-android/res/drawable-xhdpi/social_send_now.png new file mode 100644 index 0000000000000000000000000000000000000000..0c870d2ce19d9c4469dccaaeee0d1d635232803f Binary files /dev/null and b/briar-android/res/drawable-xhdpi/social_send_now.png differ diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index df481efc4dd7d18c1cdef0b7fddb741de288109a..df2deb07c7a3308841e6488e1982b9e7a19ae1b0 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -43,4 +43,5 @@ <string name="done_button">Done</string> <string name="messages_title">Messages</string> <string name="compose_button">New Message</string> + <string name="compose_title">New Message</string> </resources> diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java b/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java index c28abac256fb2bc37e7aa1857deb830808942396..f178060fda377a80bceb642c1237064da6f22fe7 100644 --- a/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java +++ b/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java @@ -165,13 +165,17 @@ implements DatabaseListener, OnClickListener, OnItemClickListener { } public void onClick(View view) { - // FIXME: Hook this button up to an activity + Intent i = new Intent(this, WriteMessageActivity.class); + i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt()); + i.putExtra("net.sf.briar.CONTACT_NAME", contactName); + startActivity(i); } public void onItemClick(AdapterView<?> parent, View view, int position, long id) { PrivateMessageHeader item = adapter.getItem(position); Intent i = new Intent(this, ReadMessageActivity.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()); diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationItem.java b/briar-android/src/net/sf/briar/android/messages/ConversationItem.java deleted file mode 100644 index 2640bc105efcf064bd501251b0e8fd1fe81086cb..0000000000000000000000000000000000000000 --- a/briar-android/src/net/sf/briar/android/messages/ConversationItem.java +++ /dev/null @@ -1,67 +0,0 @@ -package net.sf.briar.android.messages; - -import net.sf.briar.api.db.PrivateMessageHeader; -import net.sf.briar.api.messaging.MessageId; - -class ConversationItem { - - private final PrivateMessageHeader header; - private final byte[] body; - private final boolean expanded; - - ConversationItem(PrivateMessageHeader header) { - this.header = header; - body = null; - expanded = false; - } - - // Collapse an existing item - ConversationItem(ConversationItem item) { - this.header = item.header; - body = null; - expanded = false; - } - - // Expand an existing item - ConversationItem(ConversationItem item, byte[] body) { - this.header = item.header; - this.body = body; - expanded = true; - } - - MessageId getId() { - return header.getId(); - } - - String getContentType() { - return header.getContentType(); - } - - String getSubject() { - return header.getSubject(); - } - - long getTimestamp() { - return header.getTimestamp(); - } - - boolean isRead() { - return header.isRead(); - } - - boolean isStarred() { - return header.isStarred(); - } - - boolean isIncoming() { - return header.isIncoming(); - } - - byte[] getBody() { - return body; - } - - boolean isExpanded() { - return expanded; - } -} diff --git a/briar-android/src/net/sf/briar/android/messages/ReadMessageActivity.java b/briar-android/src/net/sf/briar/android/messages/ReadMessageActivity.java index 9a2ec2b492b37c91b661621b2a8a97ba05f4bd30..e998249754d8205e6b36c5911852e6e8cd951298 100644 --- a/briar-android/src/net/sf/briar/android/messages/ReadMessageActivity.java +++ b/briar-android/src/net/sf/briar/android/messages/ReadMessageActivity.java @@ -18,6 +18,7 @@ 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.api.ContactId; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseExecutor; import net.sf.briar.api.db.DbException; @@ -47,6 +48,8 @@ implements OnClickListener { @Inject private DatabaseComponent db; @Inject @DatabaseExecutor private Executor dbExecutor; + private ContactId contactId = null; + private String contactName = null; private MessageId messageId = null; private boolean starred = false; private ImageButton starButton = null, replyButton = null; @@ -57,12 +60,15 @@ implements OnClickListener { super.onCreate(null); Intent i = getIntent(); - String contactName = i.getStringExtra("net.sf.briar.CONTACT_NAME"); + 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"); if(contactName == null) throw new IllegalStateException(); setTitle(contactName); - byte[] id = i.getByteArrayExtra("net.sf.briar.MESSAGE_ID"); - if(id == null) throw new IllegalStateException(); - messageId = new MessageId(id); + byte[] mid = i.getByteArrayExtra("net.sf.briar.MESSAGE_ID"); + if(mid == null) throw new IllegalStateException(); + messageId = new MessageId(mid); String contentType = i.getStringExtra("net.sf.briar.CONTENT_TYPE"); if(contentType == null) throw new IllegalStateException(); long timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1); @@ -181,7 +187,6 @@ implements OnClickListener { unbindService(serviceConnection); } - @Override public void onClick(View view) { if(view == starButton) { final MessageId id = messageId; @@ -201,7 +206,11 @@ implements OnClickListener { starButton.setImageResource(R.drawable.rating_important); else starButton.setImageResource(R.drawable.rating_not_important); } else if(view == replyButton) { - // FIXME: Hook this up to an activity + Intent i = new Intent(this, WriteMessageActivity.class); + i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt()); + i.putExtra("net.sf.briar.CONTACT_NAME", contactName); + i.putExtra("net.sf.briar.PARENT_ID", messageId.getBytes()); + startActivity(i); } } } diff --git a/briar-android/src/net/sf/briar/android/messages/WriteMessageActivity.java b/briar-android/src/net/sf/briar/android/messages/WriteMessageActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..92d0dbfdfffb19e1845e226a7f3ee7246bf14767 --- /dev/null +++ b/briar-android/src/net/sf/briar/android/messages/WriteMessageActivity.java @@ -0,0 +1,164 @@ +package net.sf.briar.android.messages; + +import static android.view.Gravity.CENTER_VERTICAL; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +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.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.api.ContactId; +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.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.EditText; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; +import android.widget.ScrollView; + +import com.google.inject.Inject; + +public class WriteMessageActivity extends BriarActivity +implements OnClickListener { + + private static final Logger LOG = + Logger.getLogger(WriteMessageActivity.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 ContactId contactId = null; + private String contactName = null; + private MessageId parentId = null; + private ImageButton cancelButton = null, sendButton = null; + private EditText content = null; + + @Override + public void onCreate(Bundle state) { + super.onCreate(null); + + Intent i = getIntent(); + 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"); + if(contactName == null) throw new IllegalStateException(); + byte[] pid = i.getByteArrayExtra("net.sf.briar.MESSAGE_ID"); + if(pid != null) parentId = new MessageId(pid); + + LinearLayout layout = new LinearLayout(this); + layout.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT)); + layout.setOrientation(VERTICAL); + + LinearLayout header = new LinearLayout(this); + header.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT)); + header.setOrientation(HORIZONTAL); + header.setGravity(CENTER_VERTICAL); + + cancelButton = new ImageButton(this); + cancelButton.setPadding(5, 5, 5, 5); + cancelButton.setBackgroundResource(0); + cancelButton.setImageResource(R.drawable.navigation_cancel); + cancelButton.setOnClickListener(this); + header.addView(cancelButton); + + sendButton = new ImageButton(this); + sendButton.setPadding(5, 5, 5, 5); + sendButton.setBackgroundResource(0); + sendButton.setImageResource(R.drawable.social_send_now); + sendButton.setOnClickListener(this); + header.addView(sendButton); + layout.addView(header); + + ScrollView scrollView = new ScrollView(this); + 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); + } + scrollView.addView(content); + layout.addView(scrollView); + + setContentView(layout); + + // 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 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(view == cancelButton) { + finish(); + } else if(view == sendButton) { + final Message m; + try { + byte[] body = content.getText().toString().getBytes("UTF-8"); + m = messageFactory.createPrivateMessage(parentId, + "text/plain", body); + } catch(UnsupportedEncodingException e) { + throw new RuntimeException(e); + } catch(IOException e) { + throw new RuntimeException(e); + } catch(GeneralSecurityException e) { + throw new RuntimeException(e); + } + final ContactId contactId = this.contactId; + dbExecutor.execute(new Runnable() { + public void run() { + try { + serviceConnection.waitForStartup(); + db.addLocalPrivateMessage(m, contactId); + } 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(); + } + } + }); + finish(); + } + } +}