diff --git a/res/drawable/token_background.xml b/res/drawable/token_background.xml new file mode 100644 index 0000000..846b16c --- /dev/null +++ b/res/drawable/token_background.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/res/layout/contact_token.xml b/res/layout/contact_token.xml new file mode 100644 index 0000000..fb18fc6 --- /dev/null +++ b/res/layout/contact_token.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/res/layout/fragment_new_email.xml b/res/layout/fragment_new_email.xml index f668af6..e3d35c6 100644 --- a/res/layout/fragment_new_email.xml +++ b/res/layout/fragment_new_email.xml @@ -14,15 +14,13 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> - + android:layout_height="wrap_content" > - + mAdapter; + ContactsCompletionView mRecipients; EditText mSubject; EditText mContent; @@ -84,12 +90,32 @@ public class NewEmailFragment extends Fragment { mSpinner.setAdapter(identities); mSpinner.setSelection(mDefaultPos); - mRecipients = (MultiAutoCompleteTextView) view.findViewById(R.id.recipients); - mRecipients.setAdapter(null); // TODO: Implement - mRecipients.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer()); + List contacts = new ArrayList(); + try { + for (Contact contact : I2PBote.getInstance().getAddressBook().getAll()) { + contacts.add(new Person(contact.getName(), contact.getBase64Dest())); + } + } catch (PasswordException e) { + // TODO handle + e.printStackTrace(); + } + mAdapter = new FilteredArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, contacts) { + @Override + protected boolean keepObject(Person obj, String mask) { + mask = mask.toLowerCase(Locale.US); + return obj.getName().toLowerCase(Locale.US).startsWith(mask) || obj.getAddress().toLowerCase(Locale.US).startsWith(mask); + } + }; + + mRecipients = (ContactsCompletionView) view.findViewById(R.id.recipients); + mRecipients.setAdapter(mAdapter); mSubject = (EditText) view.findViewById(R.id.subject); mContent = (EditText) view.findViewById(R.id.message); + + if (savedInstanceState == null) { + mRecipients.setPrefix(getResources().getString(R.string.to)); + } } @Override @@ -124,8 +150,11 @@ public class NewEmailFragment extends Fragment { // Bote versions to see a sender (and validate the signature). email.setSender(ia); - // TODO: Implement properly - email.addRecipient(Message.RecipientType.TO, ia); + for (Object obj : mRecipients.getObjects()) { + Person person = (Person) obj; + email.addRecipient(Message.RecipientType.TO, new InternetAddress( + person.getAddress(), person.getName())); + } email.setSubject(mSubject.getText().toString(), "UTF-8"); diff --git a/src/i2p/bote/android/util/ContactsCompletionView.java b/src/i2p/bote/android/util/ContactsCompletionView.java new file mode 100644 index 0000000..d667eeb --- /dev/null +++ b/src/i2p/bote/android/util/ContactsCompletionView.java @@ -0,0 +1,74 @@ +package i2p.bote.android.util; + +import java.security.GeneralSecurityException; +import java.util.SortedSet; + +import i2p.bote.I2PBote; +import i2p.bote.android.R; +import i2p.bote.email.EmailDestination; +import i2p.bote.fileencryption.PasswordException; +import i2p.bote.packet.dht.Contact; +import android.app.Activity; +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.tokenautocomplete.TokenCompleteTextView; + +public class ContactsCompletionView extends TokenCompleteTextView { + public ContactsCompletionView(Context context, AttributeSet attrs) { + super(context, attrs); + allowDuplicates(false); + } + + @Override + protected View getViewForObject(Object object) { + Person person = (Person) object; + + LayoutInflater l = (LayoutInflater)getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + LinearLayout view = (LinearLayout)l.inflate(R.layout.contact_token, (ViewGroup)ContactsCompletionView.this.getParent(), false); + ((TextView)view.findViewById(R.id.contact_name)).setText(person.getName()); + + return view; + } + + @Override + protected Object defaultObject(String completionText) { + // Stupid simple example of guessing if we have an email or not + int index = completionText.indexOf('@'); + if (index == -1) { + try { + // Check if it is a known Destination + Contact c = BoteHelper.getContact(completionText); + if (c != null) + return new Person(c.getName(), c.getBase64Dest()); + + // 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()); + } + + // Try as a new Destination + try { + new EmailDestination(completionText); + return new Person(completionText.substring(0, 5), completionText); + } catch (GeneralSecurityException e) { + // Not a valid Destination + // Assume the user meant an external address + return new Person(completionText, completionText.replace(" ", "") + "@example.com", true); + } + } catch (PasswordException e) { + // TODO handle + return new Person(completionText, completionText.replace(" ", "") + "@example.com", true); + } + } else { + return new Person(completionText.substring(0, index), completionText, true); + } + } +} diff --git a/src/i2p/bote/android/util/Person.java b/src/i2p/bote/android/util/Person.java new file mode 100644 index 0000000..96ed0ea --- /dev/null +++ b/src/i2p/bote/android/util/Person.java @@ -0,0 +1,27 @@ +package i2p.bote.android.util; + +import java.io.Serializable; + +public class Person implements Serializable { + private static final long serialVersionUID = -2874686247798691378L; + private String name; + private String address; + 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 String getName() { return name; } + public String getAddress() { return address; } + public boolean isExternal() { return isExternal; } + + @Override + public boolean equals(Object other) { + if (!(other instanceof Person)) + return false; + return address.equals(((Person)other).address); + } + + @Override + public String toString() { return name; } +}