Add/remove attachments in new email, list attachments in view email

This commit is contained in:
str4d
2014-12-28 20:16:06 +00:00
parent 2dc6663161
commit 97f290f272
11 changed files with 260 additions and 1 deletions

View File

@@ -1,14 +1,20 @@
package i2p.bote.android;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ClipData;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -18,6 +24,7 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
@@ -25,6 +32,7 @@ import com.tokenautocomplete.FilteredArrayAdapter;
import net.i2p.data.DataFormatException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
@@ -40,6 +48,7 @@ import javax.mail.internet.InternetAddress;
import i2p.bote.I2PBote;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.ContactsCompletionView;
import i2p.bote.android.util.ContentAttachment;
import i2p.bote.android.util.Person;
import i2p.bote.email.Attachment;
import i2p.bote.email.Email;
@@ -89,6 +98,8 @@ public class NewEmailFragment extends Fragment {
public static final String QUOTE_MSG_TYPE = "type";
private static final int REQUEST_FILE = 1;
private String mSenderKey;
Spinner mSpinner;
@@ -100,6 +111,7 @@ public class NewEmailFragment extends Fragment {
ContactsCompletionView mBcc;
EditText mSubject;
EditText mContent;
LinearLayout mAttachments;
boolean mMoreVisible;
boolean mDirty;
@@ -137,6 +149,7 @@ public class NewEmailFragment extends Fragment {
mBcc = (ContactsCompletionView) view.findViewById(R.id.bcc);
mSubject = (EditText) view.findViewById(R.id.subject);
mContent = (EditText) view.findViewById(R.id.message);
mAttachments = (LinearLayout) view.findViewById(R.id.attachments);
String quoteMsgFolder = getArguments().getString(QUOTE_MSG_FOLDER);
String quoteMsgId = getArguments().getString(QUOTE_MSG_ID);
@@ -357,6 +370,10 @@ public class NewEmailFragment extends Fragment {
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.action_attach_file:
requestFile();
return true;
case R.id.action_send_email:
if (sendEmail())
mCallbacks.onTaskFinished();
@@ -388,6 +405,64 @@ public class NewEmailFragment extends Fragment {
}
}
private void requestFile() {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.setType("*/*");
i.addCategory(Intent.CATEGORY_OPENABLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
startActivityForResult(
Intent.createChooser(i,
getResources().getString(R.string.select_attachment)),
REQUEST_FILE);
}
@SuppressLint("NewApi")
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) {
if (resultCode == Activity.RESULT_CANCELED) {
System.out.println("Cancelled");
}
return;
}
switch (requestCode) {
case REQUEST_FILE:
addAttachment(data.getData());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 &&
data.getClipData() != null) {
ClipData clipData = data.getClipData();
for (int i = 0; i < clipData.getItemCount(); i++) {
addAttachment(clipData.getItemAt(i).getUri());
}
}
break;
}
}
private void addAttachment(Uri uri) {
// Try to create a ContentAttachment using the provided Uri.
try {
final ContentAttachment attachment = new ContentAttachment(getActivity().getContentResolver(), uri);
final View v = getActivity().getLayoutInflater().inflate(R.layout.listitem_attachment, mAttachments, false);
v.setTag(attachment);
((TextView) v.findViewById(R.id.filename)).setText(attachment.getFileName());
((TextView) v.findViewById(R.id.size)).setText(attachment.getHumanReadableSize(getActivity()));
v.findViewById(R.id.remove_attachment).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
attachment.clean();
mAttachments.removeView(v);
}
});
mAttachments.addView(v);
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.e(Constants.ANDROID_LOG_TAG, "File not found: " + uri);
}
}
private boolean sendEmail() {
Email email = new Email(I2PBote.getInstance().getConfiguration().getIncludeSentTime());
try {
@@ -433,11 +508,25 @@ public class NewEmailFragment extends Fragment {
email.setSubject(mSubject.getText().toString(), "UTF-8");
// Extract the attachments
List<Attachment> attachments = new ArrayList<Attachment>();
for (int i = 0; i < mAttachments.getChildCount(); i++) {
View v = mAttachments.getChildAt(i);
attachments.add((Attachment) v.getTag());
}
// Set the text and add attachments
email.setContent(mContent.getText().toString(), (List<Attachment>) null);
email.setContent(mContent.getText().toString(), attachments);
// Send the email
I2PBote.getInstance().sendEmail(email);
// Clean up attachments
for (Attachment attachment : attachments) {
if (!attachment.clean())
Log.e(Constants.ANDROID_LOG_TAG, "Can't clean up attachment: <" + attachment + ">");
}
return true;
} catch (PasswordException e) {
// TODO Auto-generated catch block

View File

@@ -20,11 +20,15 @@ import android.widget.Toast;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.text.DateFormat;
import java.util.List;
import javax.mail.Address;
import javax.mail.MessagingException;
import javax.mail.Part;
import i2p.bote.Util;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.ContentAttachment;
import i2p.bote.email.Email;
import i2p.bote.fileencryption.PasswordException;
@@ -84,6 +88,7 @@ public class ViewEmailFragment extends Fragment {
TextView sent = (TextView) v.findViewById(R.id.email_sent);
TextView received = (TextView) v.findViewById(R.id.email_received);
TextView content = (TextView) v.findViewById(R.id.email_content);
LinearLayout attachments = (LinearLayout) v.findViewById(R.id.attachments);
try {
String fromAddress = email.getOneFromAddress();
@@ -160,6 +165,19 @@ public class ViewEmailFragment extends Fragment {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
content.setTextIsSelectable(true);
List<Part> parts = email.getParts();
for (int partIndex=0; partIndex < parts.size(); partIndex++) {
Part part = parts.get(partIndex);
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) {
ContentAttachment attachment = new ContentAttachment(part);
final View a = getActivity().getLayoutInflater().inflate(R.layout.listitem_attachment, attachments, false);
((TextView)a.findViewById(R.id.filename)).setText(attachment.getFileName());
((TextView)a.findViewById(R.id.size)).setText(attachment.getHumanReadableSize(getActivity()));
a.findViewById(R.id.remove_attachment).setVisibility(View.GONE);
attachments.addView(a);
}
}
// Prepare fields for replying
mIsAnonymous = email.isAnonymous();
} catch (MessagingException e) {

View File

@@ -0,0 +1,129 @@
package i2p.bote.android.util;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns;
import android.util.Log;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.NumberFormat;
import java.util.Locale;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.MessagingException;
import javax.mail.Part;
import i2p.bote.Util;
import i2p.bote.android.Constants;
import i2p.bote.android.R;
import i2p.bote.email.Attachment;
public class ContentAttachment implements Attachment {
private ParcelFileDescriptor mAttachmentPFD;
private String mFileName;
private long mSize;
private DataHandler mDataHandler;
public ContentAttachment(ContentResolver cr, Uri uri) throws FileNotFoundException {
// Get the content resolver instance for this context, and use it
// to get a ParcelFileDescriptor for the file.
mAttachmentPFD = cr.openFileDescriptor(uri, "r");
// If we get to here, the file exists
Cursor returnCursor = cr.query(
uri,
new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE},
null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
mFileName = returnCursor.getString(nameIndex);
mSize = returnCursor.getLong(sizeIndex);
returnCursor.close();
// Get a regular file descriptor for the file
final FileDescriptor fd = mAttachmentPFD.getFileDescriptor();
final String mimeType = cr.getType(uri);
mDataHandler = new DataHandler(new DataSource() {
@Override
public InputStream getInputStream() throws IOException {
return new FileInputStream(fd);
}
@Override
public OutputStream getOutputStream() throws IOException {
throw new IOException("Cannot write to attachments");
}
@Override
public String getContentType() {
return mimeType;
}
@Override
public String getName() {
return mFileName;
}
});
}
public ContentAttachment(final Part part) throws IOException, MessagingException {
mFileName = part.getFileName();
mSize = Util.getPartSize(part);
mDataHandler = part.getDataHandler();
}
@Override
public String getFileName() {
return mFileName;
}
public long getSize() {
return mSize;
}
public String getHumanReadableSize(Context context) {
int unit = (63-Long.numberOfLeadingZeros(mSize)) / 10; // 0 if totalBytes<1K, 1 if 1K<=totalBytes<1M, etc.
double value = (double)mSize / (1<<(10*unit));
int formatStr;
switch (unit) {
case 0: formatStr = R.string.n_bytes; break;
case 1: formatStr = R.string.n_kilobytes; break;
default: formatStr = R.string.n_megabytes;
}
NumberFormat formatter = NumberFormat.getInstance(Locale.getDefault());
if (value < 100)
formatter.setMaximumFractionDigits(1);
else
formatter.setMaximumFractionDigits(0);
return context.getString(formatStr, formatter.format(value));
}
@Override
public DataHandler getDataHandler() {
return mDataHandler;
}
@Override
public boolean clean() {
if (mAttachmentPFD == null)
return true;
try {
mAttachmentPFD.close();
return true;
} catch (IOException e) {
Log.e(Constants.ANDROID_LOG_TAG, "Can't close ParcelFileDescriptor: <" + mFileName + ">", e);
return false;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

View File

@@ -68,6 +68,12 @@
android:ems="10"
android:hint="@string/compose_email"
android:inputType="textMultiLine"/>
<LinearLayout
android:id="@+id/attachments"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
</LinearLayout>
</ScrollView>

View File

@@ -221,6 +221,12 @@
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:textAppearance="@style/TextAppearance.AppCompat.Primary"/>
<LinearLayout
android:id="@+id/attachments"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
</LinearLayout>
</ScrollView>

View File

@@ -2,6 +2,12 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:i2pandroid="http://schemas.android.com/apk/res-auto" >
<item
android:id="@+id/action_attach_file"
android:icon="@drawable/ic_attach_file_white_24dp"
android:title="@string/action_attach_file"
i2pandroid:showAsAction="ifRoom"/>
<item
android:id="@+id/action_send_email"
android:icon="@drawable/ic_send_white_24dp"

View File

@@ -38,6 +38,11 @@
<string name="log_out">Log out</string>
<string name="touch_lock_to_log_in">Touch the lock icon to log in.</string>
<string name="action_new_email">New email</string>
<string name="action_attach_file">Attach file</string>
<string name="select_attachment">Select a file to attach</string>
<string name="n_bytes" translatable="false">%s B</string>
<string name="n_kilobytes" translatable="false">%s KB</string>
<string name="n_megabytes" translatable="false">%s MB</string>
<string name="action_send_email">Send email</string>
<string name="action_start_bote">Connect to network</string>
<string name="action_stop_bote">Disconnect from network</string>