I have an application which open pdf file with tap on app (default PDF in raw folder), openwith (myapp) and also from mail attachment. I converted the selected PDF file into inputstream and then to bytearray. Now I have a button and when user click on it need to send email through gmail app using intent. I need to add inputstream or bytearray as pdffile to the mail as attachment.I used the following code I can see the attachment with no bytes and received email donot have attachment. Struggling from more than 10 hours.. please help...
Intent sendIntent = new Intent(Intent.ACTION_VIEW);
sendIntent.setType("application/pdf");
sendIntent.setData(Uri.parse("sample#gmailcom"));
sendIntent.setClassName("com.google.android.gm", "com.google.android.gm.ComposeActivityGmail");
sendIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { "sample#gmail.com" });
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "testPDF");
sendIntent.putExtra(Intent.EXTRA_TEXT, "this is a PDF ");
File fileIn = new File(name, "myfile.pdf");
Toast.makeText(this, fileIn.getName(), Toast.LENGTH_LONG).show();
Uri fileuri = Uri.fromFile(fileIn);
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(fileuri.toString()));
startActivity(sendIntent);
I tried many ways and finally found a solutions myself. Use file provider to attach the file to mail without storing it physically on the device. The code is follows and hope it helps someone else.
Add a class AttachFileProvider with below code
public class AttachFileProvider extends ContentProvider {
private static final String CLASS_NAME = "AttachFileProvider";
// The authority is the symbolic name for the provider class
public static final String AUTHORITY = "com.example.pDoc.pdocsigner.provider";
// UriMatcher used to match against incoming requests<br />
private UriMatcher uriMatcher;
#Override
public boolean onCreate() {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// Add a URI to the matcher which will match against the form
uriMatcher.addURI(AUTHORITY, "*", 1);
return true;
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
// Check incoming Uri against the matcher
switch (uriMatcher.match(uri)) {
// If it returns 1 - then it matches the Uri defined in onCreate
case 1:
// The desired file name is specified by the last segment of the path
// Take this and build the path to the file
String fileLocation = getContext().getCacheDir() + File.separator + uri.getLastPathSegment();
// Create & return a ParcelFileDescriptor pointing to the file
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
// Otherwise unrecognised Uri
default:
//Log.v(LOG_TAG, "Unsupported uri: '" + uri + "'.");
throw new FileNotFoundException("Unsupported uri: " + uri.toString());
}
}
// //////////////////////////////////////////////////////////////
// Not supported / used / required for this example
// //////////////////////////////////////////////////////////////
#Override
public int update(Uri uri, ContentValues contentvalues, String s, String[] as) { return 0; }
#Override
public int delete(Uri uri, String s, String[] as) { return 0; }
#Override
public Uri insert(Uri uri, ContentValues contentvalues) { return null; }
#Override
public String getType(Uri uri) { return null; }
#Override
public Cursor query(Uri uri, String[] projection, String s, String[] as1, String s1) { return null; }
}
to attach the mail use the following code.
if (bytes.length > 0) {
try {
createCachedFile(this, attachmentFileName, bytes);
this.startActivity(getSendEmailIntent(this, "tomailid", "Test", "See attached", attachmentFileName));
} catch (IOException e) {
e.printStackTrace();
} catch (ActivityNotFoundException e) {
Toast.makeText(this, "Gmail is not available on this device.", Toast.LENGTH_SHORT).show();
}
}
public void createCachedFile(Context context, String fileName, byte[] content) throws IOException {
File cacheFile = new File(context.getCacheDir() + File.separator + fileName);
cacheFile.createNewFile();
BufferedOutputStream bostream = new BufferedOutputStream(new FileOutputStream(cacheFile));
bostream.write(content);
bostream.flush();
bostream.close();
}
public static Intent getSendEmailIntent(Context context, String email, String subject, String body, String fileName) {
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
//Explicitly only use Gmail to send
// emailIntent.setClassName("com.google.android.gm","com.google.android.gm.ComposeActivityGmail");
//emailIntent.setType("message/rfc822");
emailIntent.setType("vnd.android.cursor.item/email");
//Add the recipients
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] { email });
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, body);
//Add the attachment to custom ContentProvider
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + AttachFileProvider.AUTHORITY + "/" + fileName));
return emailIntent;
}
You should make sure that you attachment file can be read by gmail app, so put it in the external sdcard storage area. Maybe you path has problem, change it and test.
Related
I want to attach a file I generate on runtime.
Runtime flow:
Create intent
Create a file under cacheDir
Add file as content:// extra to the intent
Start chooser and select Gmail
expect openFile() to be called and allow access
Gmail opens with no attachment
However, openFile() isn't called.
Here is the relevant code:
Create an intent and add extras:
public static void contact(Activity activity, String message) {
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType("text/plain");
String version = Version.appVersion(activity);
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{activity.getString(R.string.contact_email)});
emailIntent.putExtra(Intent.EXTRA_SUBJECT, activity.getString(R.string.app_name)+" ("+version+")");
emailIntent.putExtra(Intent.EXTRA_TEXT, message);
addInfoAttachment(emailIntent, activity);
String title = activity.getString(R.string.string_select_email_app);
activity.startActivity(Intent.createChooser(emailIntent, title));
}
Create the cached file:
public static void createCachedFile(Context context, String fileName,
String content) throws IOException
{
File cacheFile = new File(context.getCacheDir() + File.separator
+ fileName);
cacheFile.delete();
cacheFile.createNewFile();
FileOutputStream fos = new FileOutputStream(cacheFile);
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF8");
PrintWriter pw = new PrintWriter(osw);
pw.println(content);
pw.flush();
pw.close();
}
Add the file to the intent:
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + AUTHORITY + File.separator + REPORT_INFO_FILE_NAME));
Overriding ContentProvider: (not called)
public class LogFileProvider extends ContentProvider {
private static final String TAG = "LogFileProvider";
public static final String AUTHORITY = "com.domain.appName.LogFileProvider";
private UriMatcher uriMatcher;
#Override
public boolean onCreate() {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "*", 1);
return true;
}
#Override
public ParcelFileDescriptor openFile(#NonNull Uri uri, #NonNull String mode) throws FileNotFoundException {
Log.v(TAG, "openFile Called with uri: '" + uri + "'." + uri.getLastPathSegment());
// Check incoming Uri against the matcher
switch (uriMatcher.match(uri)) {
// If it returns 1 - then it matches the Uri defined in onCreate
case 1:
String fileLocation = getContext().getCacheDir() + File.separator
+ uri.getLastPathSegment();
return ParcelFileDescriptor.open(new File(
fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
default:
Log.v(TAG, "Unsupported uri: '" + uri + "'.");
throw new FileNotFoundException("Unsupported uri: "
+ uri.toString());
}
}
// //////////////////////////////////////////////////////////////
// Not supported / used / required for this example
// //////////////////////////////////////////////////////////////
#Override
public int update(#NonNull Uri uri, ContentValues contentvalues, String s,
String[] as) {
return 0;
}
#Override
public int delete(#NonNull Uri uri, String s, String[] as) {
return 0;
}
#Override
public Uri insert(#NonNull Uri uri, ContentValues contentvalues) {
return null;
}
#Override
public String getType(#NonNull Uri uri) {
return null;
}
#Override
public Cursor query(#NonNull Uri uri, String[] projection, String s, String[] as1,
String s1) {
return null;
}
}
In manifest:
<provider
android:name=".LogFileProvider"
android:authorities="com.domain.appName.LogFileProvider"
android:enabled="true"
android:exported="false"
android:grantUriPermissions="true"/>
You need to add FLAG_GRANT_READ_URI_PERMISSION to the Intent. As it stands, the recipient has no rights to access that content.
You might consider using FileProvider rather than bothering with your own. Some clients will expect the provider to do more than what you have (e.g., respond to query() for the OpenableColumns, respond to getType() with the actual MIME type).
Here's what I would like to do in my app:
allow user to select files and store paths to them inside a database
display names of those files
allow user to click on filename and open the chosen file
So I'd like to get real path to file and store it. The user will only be allowed to get files from internal/external storage so, as I assume, the real path will always exist. I've been looking for solutions and pretty much everyone asks only about images/audio/video and uses MediaStore in their solutions (and I, as of now, have no idea what this is, and I can get Uri without using it). I'd like a solution that would work with any type of file, if that's possible.
Here's how I'm getting Uri:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
intent.setType("*/*");
startActivityForResult(Intent.createChooser(intent, "Choose File"), 1);
and I'm storing the received information in onActivityResult method.
So i get Uri and store it as a string which looks like, for example, content://com.android.providers.downloads.documents/document/4588. How do I convert it to a real filepath?
Sharing my Fileutils file from one of my projects
public class FileUtils {
public static String getFilePath(Context context, Uri uri) throws URISyntaxException {
String selection = null;
String[] selectionArgs = null;
// Uri is different in versions after KITKAT (Android 4.4), we need to
if (Build.VERSION.SDK_INT >= 19 && DocumentsContract.isDocumentUri(context
.getApplicationContext(), uri)) {
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
return Environment.getExternalStorageDirectory() + "/" + split[1];
} else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
uri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
} else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("image".equals(type)) {
uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
selection = "_id=?";
selectionArgs = new String[]{
split[1]
};
} else if (isGoogleDriveFile(uri)) {
String mimeType = context.getContentResolver().getType(uri);
Cursor returnCursor =
context.getContentResolver().query(uri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
String fileName = returnCursor.getString(nameIndex);
Log.d("name", returnCursor.getString(nameIndex));
Log.d("size", Long.toString(returnCursor.getLong(sizeIndex)));
try {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
File root = new File(Environment.getExternalStorageDirectory(), "WittyParrot");
root.mkdirs();
File file = new File(root, fileName);
copyStreamToFile(file, inputStream);
return Uri.fromFile(file).getPath();
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
returnCursor.close();
}
}
}
if ("content".equalsIgnoreCase(uri.getScheme())) {
String[] projection = {
MediaStore.Images.Media.DATA
};
Cursor cursor = null;
try {
cursor = context.getContentResolver()
.query(uri, projection, selection, selectionArgs, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
if (cursor.moveToFirst()) {
return cursor.getString(column_index);
}
} catch (Exception e) {
}
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
public static boolean isGoogleDriveFile(Uri uri) {
return "com.google.android.apps.docs.storage".equals(uri.getAuthority());
}
public static void copyStreamToFile(File file, InputStream input) {
try {
OutputStream output = new FileOutputStream(file);
try {
byte[] buffer = new byte[4 * 1024]; // or other buffer size
int read;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
output.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
output.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The user will only be allowed to get files from internal/external storage
Not according to your code. The user can use anything that supports ACTION_GET_CONTENT. There is no requirement for ACTION_GET_CONTENT implementations to be limited to external storage, and you cannot access internal storage from other apps using the filesystem.
If you are only willing to work with external storage, use a file chooser library, not ACTION_GET_CONTENT or ACTION_OPEN_DOCUMENT.
I'd like a solution that would work with any type of file, if that's possible.
Use a file chooser library.
How do I convert it to a real filepath?
You don't.
I convert a bitmap image (bm) into jpeg and stored an image in internal phone memory using
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 100, outputBuffer);
byte[] byteImage1=outputBuffer.toByteArray();
//save file to internal storage
FileOutputStream outputStream;
try {
outputStream = context.openFileOutput("myphoto.jpg", Context.MODE_PRIVATE);
outputStream.write(byteImage1);
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
now i want to share the stored image using shareIntent
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/jpeg");
share.putExtra(Intent.EXTRA_STREAM,
Uri.parse(context.getFilesDir().getPath() + "myphoto.jpg"));
context.startActivity(Intent.createChooser(share, "Partager par"));
But the intent is not running when i pressed click button. Please lend a help on this issue.
You will need to write Content Providers to share image in internal storage
See Sharing images that are stored on internal memory
public class MediaContentProvider extends ContentProvider {
private static final String TAG = "MediaContentProvider";
// name for the provider class
public static final String AUTHORITY = "com.way.srl.HandyWay.contentproviders.media";
private MediaData _mediaData;
// UriMatcher used to match against incoming requests
private UriMatcher _uriMatcher;
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}
#Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
#Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
return null;
}
#Override
public boolean onCreate() {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// Add a URI to the matcher which will match against the form
// 'content://com.stephendnicholas.gmailattach.provider/*'
// and return 1 in the case that the incoming Uri matches this pattern
_uriMatcher.addURI(AUTHORITY, "*", 1);
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
return null;
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
Log.v(TAG, "Called with uri: '" + uri + "'." + uri.getLastPathSegment());
// Check incoming Uri against the matcher
switch (_uriMatcher.match(uri)) {
// If it returns 1 - then it matches the Uri defined in onCreate
case 1:
// The desired file name is specified by the last segment of the
// path
// E.g.
// 'content://com.stephendnicholas.gmailattach.provider/Test.txt'
// Take this and build the path to the file
// String fileLocation = getContext().getCacheDir() + File.separator + uri.getLastPathSegment();
Integer mediaID = Integer.valueOf(uri.getLastPathSegment());
if (_mediaData == null) {
_mediaData = new MediaData();
}
Media m = _mediaData.get(mediaID);
// Create & return a ParcelFileDescriptor pointing to the file
// Note: I don't care what mode they ask for - they're only getting
// read only
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(m.filePath), ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
// Otherwise unrecognised Uri
default:
Log.v(TAG, "Unsupported uri: '" + uri + "'.");
throw new FileNotFoundException("Unsupported uri: " + uri.toString());
}
}
I am trying to create a button in my android app that allows the user to share an image using their choice of social media network. The image file is stored in the assets folder of the app.
My plan is to implement a custom ContentProvider to give external access to the image, then send a TYPE_SEND intent specifying the uri of the image within my content provider.
I have done this and it works for Google+ and GMail, but for other services it fails. The hardest part has been finding information on what I'm supposed to return from the query() method of my ContentProvider. Some apps specify a projection (e.g. Google+ asks for _id and _data), while some apps pass null as the projection. Even where the projection is specified, I've no idea what actual data (types) are expected in the columns. I can find no documentation on this.
I have also implemented the openAssetFile method of the ContentProvider and this gets called (twice by Google+!) but then inevitably the query method get called as well. Only the result of the query method seems to count.
Any ideas where I'm going wrong? What should I be returning from my query method?
Code below:
// my intent
Intent i = new Intent(android.content.Intent.ACTION_SEND);
i.setType("image/jpeg");
Uri uri = Uri.parse("content://com.me.provider/ic_launcher.jpg");
i.putExtra(Intent.EXTRA_STREAM, uri);
i.putExtra(android.content.Intent.EXTRA_TEXT, text);
startActivity(Intent.createChooser(i, "Share via"));
// my custom content provider
public class ImageProvider extends ContentProvider
{
private AssetManager _assetManager;
public static final Uri CONTENT_URI = Uri.parse("content://com.me.provider");
// not called
#Override
public int delete(Uri arg0, String arg1, String[] arg2)
{
return 0;
}
// not called
#Override
public String getType(Uri uri)
{
return "image/jpeg";
}
// not called
#Override
public Uri insert(Uri uri, ContentValues values)
{
return null;
}
#Override
public boolean onCreate()
{
_assetManager = getContext().getAssets();
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
{
MatrixCursor c = new MatrixCursor(new String[] { "_id", "_data" });
try
{
// just a guess!! works for g+ :/
c.addRow(new Object[] { "ic_launcher.jpg", _assetManager.openFd("ic_launcher.jpg") });
} catch (IOException e)
{
e.printStackTrace();
return null;
}
return c;
}
// not called
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
{
return 0;
}
// not called
#Override
public String[] getStreamTypes(Uri uri, String mimeTypeFilter)
{
return new String[] { "image/jpeg" };
}
// called by most apps
#Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException
{
try
{
AssetFileDescriptor afd = _assetManager.openFd("ic_launcher.jpg");
return afd;
} catch (IOException e)
{
throw new FileNotFoundException("No asset found: " + uri);
}
}
// not called
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException
{
return super.openFile(uri, mode);
}
}
Thanks, your question solved mine ;)
I was having the exactly inverse problem of yours: every service would work except g+.
I was returning null in the query method, that made g+ crash.
The only thing to actually expose my images was to implement openFile().
I have my images stored on the filesystem, not in the assets, but I suppose you
could get a ParcelFileDescriptor from your AssetFileDescriptor and return it.
My openFile() method looks like this:
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
String path = uri.getLastPathSegment();
if (path == null) {
throw new IllegalArgumentException("Not a Path");
}
File f = new File(getContext().getFilesDir() + File.separator + "solved" + path + ".jpg");
int iMode;
if ("r".equals(mode)) {
iMode = ParcelFileDescriptor.MODE_READ_ONLY;
} else if ("rw".equals(mode)) {
iMode = ParcelFileDescriptor.MODE_READ_WRITE;
} else if ("rwt".equals(mode)) {
iMode = ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_TRUNCATE;
} else {
throw new IllegalArgumentException("Invalid mode");
}
return ParcelFileDescriptor.open(f, iMode);
}
This works for every service I have installed with the ACTION_SEND intents except g+.
Using your query method makes it work for google plus, too.
Similar threads here do not have answers that helped...
I want to create email message with file attach, file is on internal storage.
Here is code:
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("text/plain");
i.putExtra(Intent.EXTRA_EMAIL , new String[]{email});
i.putExtra(Intent.EXTRA_SUBJECT, "Smart Weight Tracker");
i.putExtra(Intent.EXTRA_TEXT , "CSV file is in attachment");
Uri uri;
if(useExternal){
uri = Uri.fromFile(new File(this.getExternalFilesDir(null),fname));
}
else{
File f = new File(this.getFilesDir(),fname);
f.setReadable(true, false);
f.setWritable(true, false);
uri = Uri.fromFile(f);
}
i.putExtra(Intent.EXTRA_STREAM, uri);
try {
startActivity(Intent.createChooser(i, "Send mail..."));
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(MainActivity.this, "There are no email clients installed.", Toast.LENGTH_SHORT).show();
}
It works perfectly with external storage, but i have no attach when use internal storage.
I checked - file opens, (it's length in my app by showing Toast - is OK, > 0).
I write it this way:
OutputStreamWriter out =
new OutputStreamWriter(con.openFileOutput(fname, Context.MODE_WORLD_READABLE));
In logcat i see
I/Gmail (28480): >>>>> Attachment uri: file:///data/data/Android.MyApp/files /31.10.2011.csv
I/Gmail (28480): >>>>> type: text/plain
I/Gmail (28480): >>>>> name: 31.10.2011.csv
I/Gmail (28480): >>>>> size: 0
Size == 0!
Any ideas?
Hi,
Try to use content providers.
emailIntent.putExtra(Intent.EXTRA_STREAM,Uri.parse("content://" + CachedFileProvider.AUTHORITY + "/"+ fileName));
........
Android: Attaching files from internal cache to Gmail
package com.stephendnicholas.gmailattach;
import java.io.File;
import java.io.FileNotFoundException;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.util.Log;
public class CachedFileProvider extends ContentProvider {
private static final String CLASS_NAME = "CachedFileProvider";
// The authority is the symbolic name for the provider class
public static final String AUTHORITY = "com.stephendnicholas.gmailattach.provider";
// UriMatcher used to match against incoming requests
private UriMatcher uriMatcher;
#Override
public boolean onCreate() {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// Add a URI to the matcher which will match against the form
// 'content://com.stephendnicholas.gmailattach.provider/*'
// and return 1 in the case that the incoming Uri matches this pattern
uriMatcher.addURI(AUTHORITY, "*", 1);
return true;
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
String LOG_TAG = CLASS_NAME + " - openFile";
Log.v(LOG_TAG,
"Called with uri: '" + uri + "'." + uri.getLastPathSegment());
// Check incoming Uri against the matcher
switch (uriMatcher.match(uri)) {
// If it returns 1 - then it matches the Uri defined in onCreate
case 1:
// The desired file name is specified by the last segment of the
// path
// E.g.
// 'content://com.stephendnicholas.gmailattach.provider/Test.txt'
// Take this and build the path to the file
String fileLocation = getContext().getCacheDir() + File.separator
+ uri.getLastPathSegment();
// Create & return a ParcelFileDescriptor pointing to the file
// Note: I don't care what mode they ask for - they're only getting
// read only
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(
fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
// Otherwise unrecognised Uri
default:
Log.v(LOG_TAG, "Unsupported uri: '" + uri + "'.");
throw new FileNotFoundException("Unsupported uri: "
+ uri.toString());
}
}
// //////////////////////////////////////////////////////////////
// Not supported / used / required for this example
// //////////////////////////////////////////////////////////////
#Override
public int update(Uri uri, ContentValues contentvalues, String s,
String[] as) {
return 0;
}
#Override
public int delete(Uri uri, String s, String[] as) {
return 0;
}
#Override
public Uri insert(Uri uri, ContentValues contentvalues) {
return null;
}
#Override
public String getType(Uri uri) {
return null;
}
#Override
public Cursor query(Uri uri, String[] projection, String s, String[] as1,
String s1) {
return null;
}
}
<provider android:name="CachedFileProvider" android:authorities="com.stephendnicholas.gmailattach.provider">
public static void createCachedFile(Context context, String fileName,
String content) throws IOException {
File cacheFile = new File(context.getCacheDir() + File.separator
+ fileName);
cacheFile.createNewFile();
FileOutputStream fos = new FileOutputStream(cacheFile);
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF8");
PrintWriter pw = new PrintWriter(osw);
pw.println(content);
pw.flush();
pw.close();
}
public static Intent getSendEmailIntent(Context context, String email,
String subject, String body, String fileName) {
final Intent emailIntent = new Intent(
android.content.Intent.ACTION_SEND);
//Explicitly only use Gmail to send
emailIntent.setClassName("com.google.android.gm","com.google.android.gm.ComposeActivityGmail");
emailIntent.setType("plain/text");
//Add the recipients
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
new String[] { email });
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, body);
//Add the attachment by specifying a reference to our custom ContentProvider
//and the specific file of interest
emailIntent.putExtra(
Intent.EXTRA_STREAM,
Uri.parse("content://" + CachedFileProvider.AUTHORITY + "/"
+ fileName));
return emailIntent;
}
http://stephendnicholas.com/archives/974#comment-342
Uri fileUri = Uri.fromFile(new File(context.getCacheDir()+ "/"+ fileName));
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("text/plain");
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
"Test Subject");
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT,
"go on read the emails");
emailIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
startActivity(emailIntent);
Please try this code. Hope this would help.
I was following the stephendnicholas code for hours thinking I HAD to write my files to internal storage because my device had no sd card.
Creating my "internal" file like this worked just fine for me:
File file = new File(getExternalFilesDir(null), filename); // :)
It avoided Gmail limitations during attachment.
Before this I was creating my file object like this:
File file = new File(this.getFilesDir(), filename); //will not attach
But if your file is literally from internal storage, then stephendnicholas content providers etc. work if you have mad android skills.