Implemented AttachmentProvider, removed ContentAttachment.getUri()
This commit is contained in:
@@ -1,87 +1,92 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest
|
||||||
package="i2p.bote.android" >
|
package="i2p.bote.android"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
<uses-permission android:name="android.permission.NFC"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/Theme.Bote" >
|
android:theme="@style/Theme.Bote">
|
||||||
<service android:name=".service.BoteService" />
|
<service android:name=".service.BoteService"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".EmailListActivity"
|
android:name=".EmailListActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleTop" >
|
android:launchMode="singleTop">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".intro.IntroActivity"
|
android:name=".intro.IntroActivity"
|
||||||
android:parentActivityName=".EmailListActivity" >
|
android:parentActivityName=".EmailListActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="i2p.bote.android.EmailListActivity" />
|
android:value="i2p.bote.android.EmailListActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".intro.SetupActivity"
|
android:name=".intro.SetupActivity"
|
||||||
android:label="@string/title_activity_setup"
|
android:label="@string/title_activity_setup"
|
||||||
android:parentActivityName=".EmailListActivity" >
|
android:parentActivityName=".EmailListActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="i2p.bote.android.EmailListActivity" />
|
android:value="i2p.bote.android.EmailListActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ViewEmailActivity"
|
android:name=".ViewEmailActivity"
|
||||||
android:parentActivityName=".EmailListActivity" >
|
android:parentActivityName=".EmailListActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="i2p.bote.android.EmailListActivity" />
|
android:value="i2p.bote.android.EmailListActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".NewEmailActivity"
|
android:name=".NewEmailActivity"
|
||||||
android:label="@string/compose"
|
android:label="@string/compose"
|
||||||
android:parentActivityName=".EmailListActivity" >
|
android:parentActivityName=".EmailListActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="i2p.bote.android.EmailListActivity" />
|
android:value="i2p.bote.android.EmailListActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".addressbook.AddressBookActivity"
|
android:name=".addressbook.AddressBookActivity"
|
||||||
android:label="@string/address_book"
|
android:label="@string/address_book"
|
||||||
android:parentActivityName=".EmailListActivity" >
|
android:parentActivityName=".EmailListActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="i2p.bote.android.EmailListActivity" />
|
android:value="i2p.bote.android.EmailListActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".addressbook.ViewContactActivity"
|
android:name=".addressbook.ViewContactActivity"
|
||||||
android:parentActivityName=".addressbook.AddressBookActivity" >
|
android:parentActivityName=".addressbook.AddressBookActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="i2p.bote.android.addressbook.AddressBookActivity" />
|
android:value="i2p.bote.android.addressbook.AddressBookActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".addressbook.EditContactActivity"
|
android:name=".addressbook.EditContactActivity"
|
||||||
android:label="@string/action_new_contact"
|
android:label="@string/action_new_contact"
|
||||||
android:parentActivityName=".addressbook.ViewContactActivity" >
|
android:parentActivityName=".addressbook.ViewContactActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="i2p.bote.android.addressbook.ViewContactActivity" />
|
android:value="i2p.bote.android.addressbook.ViewContactActivity"/>
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
|
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<data android:scheme="vnd.android.nfc"
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
|
||||||
|
<data
|
||||||
android:host="ext"
|
android:host="ext"
|
||||||
android:pathPrefix="/i2p.bote:contact"/>
|
android:pathPrefix="/i2p.bote:contact"
|
||||||
|
android:scheme="vnd.android.nfc"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.nfc.action.TAG_DISCOVERED"/>
|
<action android:name="android.nfc.action.TAG_DISCOVERED"/>
|
||||||
@@ -90,49 +95,57 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".NetworkInfoActivity"
|
android:name=".NetworkInfoActivity"
|
||||||
android:label="@string/network_status"
|
android:label="@string/network_status"
|
||||||
android:parentActivityName=".EmailListActivity" >
|
android:parentActivityName=".EmailListActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="i2p.bote.android.EmailListActivity" />
|
android:value="i2p.bote.android.EmailListActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".config.SettingsActivity"
|
android:name=".config.SettingsActivity"
|
||||||
android:label="@string/action_settings"
|
android:label="@string/action_settings"
|
||||||
android:parentActivityName=".EmailListActivity" >
|
android:parentActivityName=".EmailListActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="i2p.bote.android.EmailListActivity" />
|
android:value="i2p.bote.android.EmailListActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".config.SetPasswordActivity"
|
android:name=".config.SetPasswordActivity"
|
||||||
android:label="@string/pref_title_change_password"
|
android:label="@string/pref_title_change_password"
|
||||||
android:parentActivityName=".config.SettingsActivity" >
|
android:parentActivityName=".config.SettingsActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="i2p.bote.android.config.SettingsActivity" />
|
android:value="i2p.bote.android.config.SettingsActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".config.ViewIdentityActivity"
|
android:name=".config.ViewIdentityActivity"
|
||||||
android:parentActivityName=".config.SettingsActivity" >
|
android:parentActivityName=".config.SettingsActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="i2p.bote.android.config.SettingsActivity" />
|
android:value="i2p.bote.android.config.SettingsActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".config.EditIdentityActivity"
|
android:name=".config.EditIdentityActivity"
|
||||||
android:label="@string/title_new_identity"
|
android:label="@string/title_new_identity"
|
||||||
android:parentActivityName=".config.ViewIdentityActivity" >
|
android:parentActivityName=".config.ViewIdentityActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="i2p.bote.android.config.ViewIdentityActivity" />
|
android:value="i2p.bote.android.config.ViewIdentityActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".config.IdentityShipActivity"
|
android:name=".config.IdentityShipActivity"
|
||||||
android:parentActivityName=".config.SettingsActivity" >
|
android:parentActivityName=".config.SettingsActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="i2p.bote.android.config.SettingsActivity" />
|
android:value="i2p.bote.android.config.SettingsActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name=".provider.AttachmentProvider"
|
||||||
|
android:authorities="${applicationId}.attachmentprovider"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
</provider>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -117,8 +117,7 @@ public class EmailListAdapter extends ArrayAdapter<Email> {
|
|||||||
List<Part> parts = email.getParts();
|
List<Part> parts = email.getParts();
|
||||||
for (Part part : parts) {
|
for (Part part : parts) {
|
||||||
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) {
|
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) {
|
||||||
((ImageView) v.findViewById(
|
v.findViewById(R.id.email_attachment).setVisibility(View.VISIBLE);
|
||||||
R.id.email_attachment)).setVisibility(View.VISIBLE);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,6 +339,10 @@ public class EmailListFragment extends AuthenticatedListFragment implements
|
|||||||
for (int i = (toDelete.size() - 1); i >= 0; i--) {
|
for (int i = (toDelete.size() - 1); i >= 0; i--) {
|
||||||
if (toDelete.valueAt(i)) {
|
if (toDelete.valueAt(i)) {
|
||||||
Email email = (Email) listView.getItemAtPosition(toDelete.keyAt(i));
|
Email email = (Email) listView.getItemAtPosition(toDelete.keyAt(i));
|
||||||
|
BoteHelper.revokeAttachmentUriPermissions(
|
||||||
|
getActivity(),
|
||||||
|
mFolder.getName(),
|
||||||
|
email);
|
||||||
// The Loader will update mAdapter
|
// The Loader will update mAdapter
|
||||||
I2PBote.getInstance().deleteEmail(mFolder, email.getMessageID());
|
I2PBote.getInstance().deleteEmail(mFolder, email.getMessageID());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -444,11 +444,11 @@ public class NewEmailFragment extends Fragment {
|
|||||||
private void addAttachment(Uri uri) {
|
private void addAttachment(Uri uri) {
|
||||||
// Try to create a ContentAttachment using the provided Uri.
|
// Try to create a ContentAttachment using the provided Uri.
|
||||||
try {
|
try {
|
||||||
final ContentAttachment attachment = new ContentAttachment(getActivity().getContentResolver(), uri);
|
final ContentAttachment attachment = new ContentAttachment(getActivity(), uri);
|
||||||
final View v = getActivity().getLayoutInflater().inflate(R.layout.listitem_attachment, mAttachments, false);
|
final View v = getActivity().getLayoutInflater().inflate(R.layout.listitem_attachment, mAttachments, false);
|
||||||
v.setTag(attachment);
|
v.setTag(attachment);
|
||||||
((TextView) v.findViewById(R.id.filename)).setText(attachment.getFileName());
|
((TextView) v.findViewById(R.id.filename)).setText(attachment.getFileName());
|
||||||
((TextView) v.findViewById(R.id.size)).setText(attachment.getHumanReadableSize(getActivity()));
|
((TextView) v.findViewById(R.id.size)).setText(attachment.getHumanReadableSize());
|
||||||
v.findViewById(R.id.remove_attachment).setOnClickListener(new View.OnClickListener() {
|
v.findViewById(R.id.remove_attachment).setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import javax.mail.Address;
|
|||||||
import javax.mail.MessagingException;
|
import javax.mail.MessagingException;
|
||||||
import javax.mail.Part;
|
import javax.mail.Part;
|
||||||
|
|
||||||
|
import i2p.bote.android.provider.AttachmentProvider;
|
||||||
import i2p.bote.android.util.BoteHelper;
|
import i2p.bote.android.util.BoteHelper;
|
||||||
import i2p.bote.android.util.ContentAttachment;
|
import i2p.bote.android.util.ContentAttachment;
|
||||||
import i2p.bote.email.Email;
|
import i2p.bote.email.Email;
|
||||||
@@ -168,15 +169,17 @@ public class ViewEmailFragment extends Fragment {
|
|||||||
for (int partIndex=0; partIndex < parts.size(); partIndex++) {
|
for (int partIndex=0; partIndex < parts.size(); partIndex++) {
|
||||||
Part part = parts.get(partIndex);
|
Part part = parts.get(partIndex);
|
||||||
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) {
|
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) {
|
||||||
ContentAttachment attachment = new ContentAttachment(part);
|
ContentAttachment attachment = new ContentAttachment(getActivity(), part);
|
||||||
|
|
||||||
View a = getActivity().getLayoutInflater().inflate(R.layout.listitem_attachment, attachments, false);
|
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.filename)).setText(attachment.getFileName());
|
||||||
((TextView)a.findViewById(R.id.size)).setText(attachment.getHumanReadableSize(getActivity()));
|
((TextView)a.findViewById(R.id.size)).setText(attachment.getHumanReadableSize());
|
||||||
a.findViewById(R.id.remove_attachment).setVisibility(View.GONE);
|
a.findViewById(R.id.remove_attachment).setVisibility(View.GONE);
|
||||||
|
|
||||||
final Intent i = new Intent(Intent.ACTION_VIEW);
|
final Intent i = new Intent(Intent.ACTION_VIEW);
|
||||||
i.setData(attachment.getUri());
|
i.setData(AttachmentProvider.getUriForAttachment(mFolderName, mMessageId, partIndex));
|
||||||
|
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION |
|
||||||
|
Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||||
a.setOnClickListener(new View.OnClickListener() {
|
a.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
|
|||||||
@@ -0,0 +1,208 @@
|
|||||||
|
package i2p.bote.android.provider;
|
||||||
|
|
||||||
|
import android.content.ContentProvider;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.UriMatcher;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.MatrixCursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.provider.OpenableColumns;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.mail.MessagingException;
|
||||||
|
import javax.mail.Part;
|
||||||
|
|
||||||
|
import i2p.bote.Util;
|
||||||
|
import i2p.bote.android.BuildConfig;
|
||||||
|
import i2p.bote.android.util.BoteHelper;
|
||||||
|
import i2p.bote.email.Email;
|
||||||
|
import i2p.bote.fileencryption.PasswordException;
|
||||||
|
|
||||||
|
public class AttachmentProvider extends ContentProvider {
|
||||||
|
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".attachmentprovider";
|
||||||
|
|
||||||
|
private static final int RAW_ATTACHMENT = 1;
|
||||||
|
|
||||||
|
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
|
|
||||||
|
static {
|
||||||
|
sUriMatcher.addURI(AUTHORITY, "*/*/#/RAW", RAW_ATTACHMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static String[] OPENABLE_PROJECTION = {
|
||||||
|
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
|
||||||
|
|
||||||
|
public static Uri getUriForAttachment(String folderName, String messageId, int partNum) {
|
||||||
|
return new Uri.Builder()
|
||||||
|
.scheme("content")
|
||||||
|
.authority(AUTHORITY)
|
||||||
|
.appendPath(folderName)
|
||||||
|
.appendPath(messageId)
|
||||||
|
.appendPath(Integer.toString(partNum))
|
||||||
|
.appendPath("RAW")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor query(Uri uri, String[] projection, String selection,
|
||||||
|
String[] selectionArgs, String sortOrder) {
|
||||||
|
if (sUriMatcher.match(uri) == UriMatcher.NO_MATCH)
|
||||||
|
throw new IllegalArgumentException("Invalid URI: " + uri);
|
||||||
|
if (projection == null) {
|
||||||
|
projection = OPENABLE_PROJECTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
final MatrixCursor cursor = new MatrixCursor(projection, 1);
|
||||||
|
MatrixCursor.RowBuilder b = cursor.newRow();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Part attachment = getAttachment(uri);
|
||||||
|
if (attachment != null) {
|
||||||
|
for (String col : projection) {
|
||||||
|
switch (col) {
|
||||||
|
case OpenableColumns.DISPLAY_NAME:
|
||||||
|
b.add(attachment.getFileName());
|
||||||
|
break;
|
||||||
|
case OpenableColumns.SIZE:
|
||||||
|
b.add(Util.getPartSize(attachment));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
b.add(null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (PasswordException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType(Uri uri) {
|
||||||
|
System.out.println("getType(): URI: " + uri);
|
||||||
|
System.out.println("Match: " + sUriMatcher.match(uri));
|
||||||
|
if (sUriMatcher.match(uri) != UriMatcher.NO_MATCH) {
|
||||||
|
try {
|
||||||
|
Part attachment = getAttachment(uri);
|
||||||
|
if (attachment != null) {
|
||||||
|
System.out.println("Content type: " + attachment.getContentType());
|
||||||
|
return attachment.getContentType();
|
||||||
|
}
|
||||||
|
} catch (PasswordException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
|
||||||
|
if (sUriMatcher.match(uri) == UriMatcher.NO_MATCH)
|
||||||
|
throw new FileNotFoundException("Invalid URI: " + uri);
|
||||||
|
if (!"r".equals(mode))
|
||||||
|
throw new FileNotFoundException("Attachments can only be read");
|
||||||
|
|
||||||
|
ParcelFileDescriptor[] pipe;
|
||||||
|
try {
|
||||||
|
pipe = ParcelFileDescriptor.createPipe();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(getClass().getSimpleName(), "Exception opening pipe", e);
|
||||||
|
throw new FileNotFoundException("Could not open pipe for: "
|
||||||
|
+ uri.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Part attachment = getAttachment(uri);
|
||||||
|
if (attachment == null)
|
||||||
|
throw new FileNotFoundException("Unknown email or attachment for URI " + uri);
|
||||||
|
|
||||||
|
new TransferThread(attachment.getInputStream(),
|
||||||
|
new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1])).start();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(getClass().getSimpleName(), "Exception accessing attachment", e);
|
||||||
|
throw new FileNotFoundException("Exception accessing attachment: " + e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return pipe[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TransferThread extends Thread {
|
||||||
|
InputStream in;
|
||||||
|
OutputStream out;
|
||||||
|
|
||||||
|
TransferThread(InputStream in, OutputStream out) {
|
||||||
|
this.in = in;
|
||||||
|
this.out = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
byte[] buf = new byte[8192];
|
||||||
|
int len;
|
||||||
|
|
||||||
|
try {
|
||||||
|
while ((len = in.read(buf)) > 0) {
|
||||||
|
out.write(buf, 0, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
in.close();
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(getClass().getSimpleName(), "Exception transferring file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri insert(Uri uri, ContentValues values) {
|
||||||
|
// Copied from ContentProvider
|
||||||
|
return uri.buildUpon().appendPath("0").build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Part getAttachment(Uri uri) throws PasswordException, IOException, MessagingException {
|
||||||
|
List<String> segments = uri.getPathSegments();
|
||||||
|
String folderName = segments.get(0);
|
||||||
|
String messageId = segments.get(1);
|
||||||
|
int partNum = Integer.valueOf(segments.get(2));
|
||||||
|
|
||||||
|
Email email = BoteHelper.getEmail(folderName, messageId);
|
||||||
|
if (email != null) {
|
||||||
|
if (partNum >= 0 && partNum < email.getParts().size())
|
||||||
|
return email.getParts().get(partNum);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,12 +4,14 @@ import android.app.AlertDialog;
|
|||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Bitmap.CompressFormat;
|
import android.graphics.Bitmap.CompressFormat;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -29,8 +31,10 @@ import java.util.List;
|
|||||||
|
|
||||||
import javax.mail.Address;
|
import javax.mail.Address;
|
||||||
import javax.mail.MessagingException;
|
import javax.mail.MessagingException;
|
||||||
|
import javax.mail.Part;
|
||||||
|
|
||||||
import i2p.bote.android.R;
|
import i2p.bote.android.R;
|
||||||
|
import i2p.bote.android.provider.AttachmentProvider;
|
||||||
import i2p.bote.email.Email;
|
import i2p.bote.email.Email;
|
||||||
import i2p.bote.email.EmailDestination;
|
import i2p.bote.email.EmailDestination;
|
||||||
import i2p.bote.email.EmailIdentity;
|
import i2p.bote.email.EmailIdentity;
|
||||||
@@ -415,4 +419,36 @@ public class BoteHelper extends GeneralHelper {
|
|||||||
}
|
}
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to revoke any URI permissions that were granted on an Email's attachments.
|
||||||
|
* This is best-effort; exceptions are silently ignored.
|
||||||
|
*
|
||||||
|
* @param context the Context in which permissions were granted
|
||||||
|
* @param folderName where the Email is
|
||||||
|
* @param email the Email to revoke permissions for
|
||||||
|
*/
|
||||||
|
public static void revokeAttachmentUriPermissions(Context context, String folderName, Email email) {
|
||||||
|
List<Part> parts;
|
||||||
|
try {
|
||||||
|
parts = email.getParts();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Nothing we can do, abort
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Part part : parts) {
|
||||||
|
try {
|
||||||
|
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) {
|
||||||
|
Uri uri = AttachmentProvider.getUriForAttachment(folderName,
|
||||||
|
email.getMessageID(), parts.indexOf(part));
|
||||||
|
context.revokeUriPermission(uri,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION |
|
||||||
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
}
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
// Ignore and carry on
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,8 @@ import android.content.ContentResolver;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.ParcelFileDescriptor;
|
|
||||||
import android.provider.OpenableColumns;
|
import android.provider.OpenableColumns;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -23,22 +19,19 @@ import javax.mail.MessagingException;
|
|||||||
import javax.mail.Part;
|
import javax.mail.Part;
|
||||||
|
|
||||||
import i2p.bote.Util;
|
import i2p.bote.Util;
|
||||||
import i2p.bote.android.Constants;
|
|
||||||
import i2p.bote.android.R;
|
import i2p.bote.android.R;
|
||||||
import i2p.bote.email.Attachment;
|
import i2p.bote.email.Attachment;
|
||||||
|
|
||||||
public class ContentAttachment implements Attachment {
|
public class ContentAttachment implements Attachment {
|
||||||
private ParcelFileDescriptor mAttachmentPFD;
|
private Context mCtx;
|
||||||
private String mFileName;
|
private String mFileName;
|
||||||
private long mSize;
|
private long mSize;
|
||||||
private DataHandler mDataHandler;
|
private DataHandler mDataHandler;
|
||||||
private Uri mUri;
|
|
||||||
|
|
||||||
public ContentAttachment(ContentResolver cr, Uri uri) throws FileNotFoundException {
|
public ContentAttachment(Context context, final Uri uri) throws FileNotFoundException {
|
||||||
// Get the content resolver instance for this context, and use it
|
mCtx = context;
|
||||||
// to get a ParcelFileDescriptor for the file.
|
// Get the content resolver instance for this context
|
||||||
mAttachmentPFD = cr.openFileDescriptor(uri, "r");
|
ContentResolver cr = context.getContentResolver();
|
||||||
// If we get to here, the file exists
|
|
||||||
|
|
||||||
Cursor returnCursor = cr.query(
|
Cursor returnCursor = cr.query(
|
||||||
uri,
|
uri,
|
||||||
@@ -46,18 +39,19 @@ public class ContentAttachment implements Attachment {
|
|||||||
null, null, null);
|
null, null, null);
|
||||||
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||||
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
|
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
|
||||||
returnCursor.moveToFirst();
|
|
||||||
|
if (!returnCursor.moveToFirst())
|
||||||
|
throw new FileNotFoundException();
|
||||||
|
|
||||||
mFileName = returnCursor.getString(nameIndex);
|
mFileName = returnCursor.getString(nameIndex);
|
||||||
mSize = returnCursor.getLong(sizeIndex);
|
mSize = returnCursor.getLong(sizeIndex);
|
||||||
returnCursor.close();
|
returnCursor.close();
|
||||||
|
|
||||||
// Get a regular file descriptor for the file
|
|
||||||
final FileDescriptor fd = mAttachmentPFD.getFileDescriptor();
|
|
||||||
final String mimeType = cr.getType(uri);
|
final String mimeType = cr.getType(uri);
|
||||||
mDataHandler = new DataHandler(new DataSource() {
|
mDataHandler = new DataHandler(new DataSource() {
|
||||||
@Override
|
@Override
|
||||||
public InputStream getInputStream() throws IOException {
|
public InputStream getInputStream() throws IOException {
|
||||||
return new FileInputStream(fd);
|
return mCtx.getContentResolver().openInputStream(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -75,15 +69,14 @@ public class ContentAttachment implements Attachment {
|
|||||||
return mFileName;
|
return mFileName;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// mUri is not set here because uri is only usable by us.
|
|
||||||
// Viewing attachments is only allowed once the email has been created.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContentAttachment(final Part part) throws IOException, MessagingException {
|
public ContentAttachment(Context context, Part part)
|
||||||
|
throws IOException, MessagingException {
|
||||||
|
mCtx = context;
|
||||||
mFileName = part.getFileName();
|
mFileName = part.getFileName();
|
||||||
mSize = Util.getPartSize(part);
|
mSize = Util.getPartSize(part);
|
||||||
mDataHandler = part.getDataHandler();
|
mDataHandler = part.getDataHandler();
|
||||||
// TODO: Set mUri
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -95,7 +88,7 @@ public class ContentAttachment implements Attachment {
|
|||||||
return mSize;
|
return mSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getHumanReadableSize(Context context) {
|
public String getHumanReadableSize() {
|
||||||
int unit = (63-Long.numberOfLeadingZeros(mSize)) / 10; // 0 if totalBytes<1K, 1 if 1K<=totalBytes<1M, etc.
|
int unit = (63-Long.numberOfLeadingZeros(mSize)) / 10; // 0 if totalBytes<1K, 1 if 1K<=totalBytes<1M, etc.
|
||||||
double value = (double)mSize / (1<<(10*unit));
|
double value = (double)mSize / (1<<(10*unit));
|
||||||
int formatStr;
|
int formatStr;
|
||||||
@@ -109,7 +102,7 @@ public class ContentAttachment implements Attachment {
|
|||||||
formatter.setMaximumFractionDigits(1);
|
formatter.setMaximumFractionDigits(1);
|
||||||
else
|
else
|
||||||
formatter.setMaximumFractionDigits(0);
|
formatter.setMaximumFractionDigits(0);
|
||||||
return context.getString(formatStr, formatter.format(value));
|
return mCtx.getString(formatStr, formatter.format(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -117,21 +110,8 @@ public class ContentAttachment implements Attachment {
|
|||||||
return mDataHandler;
|
return mDataHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uri getUri() {
|
|
||||||
return mUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean clean() {
|
public boolean clean() {
|
||||||
if (mAttachmentPFD == null)
|
return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
mAttachmentPFD.close();
|
|
||||||
return true;
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(Constants.ANDROID_LOG_TAG, "Can't close ParcelFileDescriptor: <" + mFileName + ">", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user