diff --git a/TODO b/TODO index c3b06f9..5ef66a4 100644 --- a/TODO +++ b/TODO @@ -14,6 +14,5 @@ Tasks: Features: - Import/export identities -- Export Destination / make it copyable -- Show contact pictures in recipients dropdown - Add optional CC: and BCC: fields - "Empty trash" option in Trash folder diff --git a/app/src/main/java/i2p/bote/android/NewEmailFragment.java b/app/src/main/java/i2p/bote/android/NewEmailFragment.java index 22e524a..83e6d14 100644 --- a/app/src/main/java/i2p/bote/android/NewEmailFragment.java +++ b/app/src/main/java/i2p/bote/android/NewEmailFragment.java @@ -29,6 +29,7 @@ import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.graphics.Bitmap; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; @@ -40,6 +41,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.EditText; +import android.widget.ImageView; import android.widget.Spinner; import android.widget.TextView; @@ -107,7 +109,9 @@ public class NewEmailFragment extends Fragment { String quoteMsgFolder = getArguments().getString(QUOTE_MSG_FOLDER); String quoteMsgId = getArguments().getString(QUOTE_MSG_ID); Email origEmail = null; + String recipientName = null; String recipientAddr = null; + Bitmap recipientPic = null; String origSubject = null; String origContent = null; String origFrom = null; @@ -116,8 +120,19 @@ public class NewEmailFragment extends Fragment { if (origEmail != null) { mSenderKey = BoteHelper.extractEmailDestination( BoteHelper.getOneLocalRecipient(origEmail).toString()); - recipientAddr = BoteHelper.getNameAndDestination( + String recipient = BoteHelper.getNameAndDestination( origEmail.getReplyAddress(I2PBote.getInstance().getIdentities())); + recipientName = BoteHelper.extractName(recipient); + recipientAddr = BoteHelper.extractEmailDestination(recipient); + if (recipientAddr == null) { // Assume external address + recipientAddr = recipient; + if (recipientName.isEmpty()) + recipientName = recipientAddr; + } else { + if (recipientName.isEmpty()) // Dest with no name + recipientName = recipientAddr.substring(0, 5); + recipientPic = BoteHelper.getPictureForDestination(recipientAddr); + } origSubject = origEmail.getSubject(); origContent = origEmail.getText(); origFrom = BoteHelper.getShortSenderName(origEmail.getOneFromAddress(), 50); @@ -144,7 +159,8 @@ public class NewEmailFragment extends Fragment { List contacts = new ArrayList(); try { for (Contact contact : I2PBote.getInstance().getAddressBook().getAll()) { - contacts.add(new Person(contact.getName(), contact.getBase64Dest())); + contacts.add(new Person(contact.getName(), contact.getBase64Dest(), + BoteHelper.decodePicture(contact.getPictureBase64()))); } } catch (PasswordException e) { // TODO handle @@ -156,20 +172,30 @@ public class NewEmailFragment extends Fragment { mask = mask.toLowerCase(Locale.US); return obj.getName().toLowerCase(Locale.US).startsWith(mask) || obj.getAddress().toLowerCase(Locale.US).startsWith(mask); } + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View v; + if (convertView == null) + v = ((LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)) + .inflate(R.layout.listitem_contact, parent, false); + else + v = convertView; + setViewContent(v, position); + return v; + } + private void setViewContent(View v, int position) { + Person person = getItem(position); + ((TextView)v.findViewById(R.id.contact_name)).setText(person.getName()); + if (person.getPicture() != null) + ((ImageView)v.findViewById(R.id.contact_picture)) + .setImageBitmap(person.getPicture()); + } }; mRecipients = (ContactsCompletionView) view.findViewById(R.id.recipients); mRecipients.setAdapter(mAdapter); if (recipientAddr != null) { - String name = BoteHelper.extractName(recipientAddr); - String address = BoteHelper.extractEmailDestination(recipientAddr); - if (address == null) { // Assume external address - address = recipientAddr; - if (name.isEmpty()) - name = address; - } else if (name.isEmpty()) // Dest with no name - name = address.substring(0, 5); - mRecipients.addObject(new Person(name, address)); + mRecipients.addObject(new Person(recipientName, recipientAddr, recipientPic)); } mSubject = (EditText) view.findViewById(R.id.subject); diff --git a/app/src/main/java/i2p/bote/android/util/BoteHelper.java b/app/src/main/java/i2p/bote/android/util/BoteHelper.java index 2d0730b..afe5d6b 100644 --- a/app/src/main/java/i2p/bote/android/util/BoteHelper.java +++ b/app/src/main/java/i2p/bote/android/util/BoteHelper.java @@ -35,7 +35,6 @@ public class BoteHelper extends GeneralHelper { * user, so their name is already "translated". * @param ctx Android Context to get strings from. * @param folder The folder. - * @param showNew Should the name contain the number of new messages? * @return The name of the folder. * @throws PasswordException */ @@ -94,30 +93,45 @@ public class BoteHelper extends GeneralHelper { String base64dest = extractEmailDestination(address); if (base64dest != null) { - // Address was found; try address book first - Contact c = getContact(base64dest); - if (c != null) { - // Address is in address book - String pic = c.getPictureBase64(); - if (pic != null) { - return decodePicture(pic); - } - } else { - // Address is an identity - EmailIdentity i = getIdentity(base64dest); - if (i != null) { - String pic = i.getPictureBase64(); - if (pic != null) { - return decodePicture(pic); - } - } - } + return getPictureForDestination(base64dest); } // Address not found anywhere, or found and has no picture return null; } + /** + * Get a Bitmap containing the picture for the contact or identity + * corresponding to the given Destination. + * @param base64dest + * @return a Bitmap, or null if no picture was found. + * @throws PasswordException + * @throws IOException + * @throws GeneralSecurityException + */ + public static Bitmap getPictureForDestination(String base64dest) throws PasswordException, IOException, GeneralSecurityException { + // Address was found; try address book first + Contact c = getContact(base64dest); + if (c != null) { + // Address is in address book + String pic = c.getPictureBase64(); + if (pic != null) { + return decodePicture(pic); + } + } else { + // Address is an identity + EmailIdentity i = getIdentity(base64dest); + if (i != null) { + String pic = i.getPictureBase64(); + if (pic != null) { + return decodePicture(pic); + } + } + } + // Address is not known + return null; + } + public static Bitmap decodePicture(String picB64) { if (picB64 == null) return null; diff --git a/app/src/main/java/i2p/bote/android/util/ContactsCompletionView.java b/app/src/main/java/i2p/bote/android/util/ContactsCompletionView.java index 55910b3..bf75651 100644 --- a/app/src/main/java/i2p/bote/android/util/ContactsCompletionView.java +++ b/app/src/main/java/i2p/bote/android/util/ContactsCompletionView.java @@ -45,32 +45,34 @@ public class ContactsCompletionView extends TokenCompleteTextView { // Check if it is a known Destination Contact c = BoteHelper.getContact(completionText); if (c != null) - return new Person(c.getName(), c.getBase64Dest()); + return new Person(c.getName(), c.getBase64Dest(), + BoteHelper.decodePicture(c.getPictureBase64())); // Check if it is a name SortedSet contacts = I2PBote.getInstance().getAddressBook().getAll(); for (Contact contact : contacts) { if (contact.getName().startsWith(completionText)) - return new Person(contact.getName(), contact.getBase64Dest()); + return new Person(contact.getName(), contact.getBase64Dest(), + BoteHelper.decodePicture(contact.getPictureBase64())); } // Try as a new Destination try { new EmailDestination(completionText); - return new Person(completionText.substring(0, 5), completionText); + return new Person(completionText.substring(0, 5), completionText, null); } catch (GeneralSecurityException e) { // Not a valid Destination // Assume the user meant an external address completionText = completionText.replace(" ", "") + "@example.com"; - return new Person(completionText, completionText, true); + return new Person(completionText, completionText, null, true); } } catch (PasswordException e) { // TODO handle completionText = completionText.replace(" ", "") + "@example.com"; - return new Person(completionText, completionText, true); + return new Person(completionText, completionText, null, true); } } else { - return new Person(completionText, completionText, true); + return new Person(completionText, completionText, null, true); } } } diff --git a/app/src/main/java/i2p/bote/android/util/Person.java b/app/src/main/java/i2p/bote/android/util/Person.java index 96ed0ea..ad4a729 100644 --- a/app/src/main/java/i2p/bote/android/util/Person.java +++ b/app/src/main/java/i2p/bote/android/util/Person.java @@ -1,18 +1,22 @@ package i2p.bote.android.util; +import android.graphics.Bitmap; + import java.io.Serializable; public class Person implements Serializable { private static final long serialVersionUID = -2874686247798691378L; private String name; private String address; + private Bitmap picture; private boolean isExternal; - public Person(String n, String a) { this(n, a, false); } - public Person(String n, String a, boolean e) { name = n; address = a; isExternal = e; } + public Person(String n, String a, Bitmap p) { this(n, a, p, false); } + public Person(String n, String a, Bitmap p, boolean e) { name = n; address = a; picture = p; isExternal = e; } public String getName() { return name; } public String getAddress() { return address; } + public Bitmap getPicture() { return picture; } public boolean isExternal() { return isExternal; } @Override