Android - not able to attach a file in email - android

By default, files saved to the internal storage are private to your application and other applications cannot access them (nor can the user).
I am able to see the file "/data/data/package_name/files/ in file explore in DDMS, but when i attached the above file URI using imageUri in email , then i saw that attached file is of 0kb.
i have used the default email APIs of Android.
Can anyone suggest me ,how to attach a file in email that is private to the application?
although i am successful able to save the file in SD card and attaching the file from SD card , this is working fine.
But if SD card is not available and saving the file to the internal storage , then how can i attach them in email.
String FILENAME = "hello_file.txt";
String string = "hello world!";FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();
File imageFile = getFileStreamPath(FILENAME );
Uri imageUri = Uri.fromFile(imageFile);
final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("*/*");
emailIntent.putExtra(android.content.Intent.EXTRA_STREAM,imageUri);
this.startActivityForResult(Intent.createChooser(emailIntent, "Send mail..."),SUB_ACTIVITY);

When you try to attach file from internal storage, GMail writes an error to the log:
ERROR/Gmail(...): file:// attachment paths must point to file:///mnt/sdcard.
E-mail application would show you the attached file even if it didn't physically exist.
As for an external storage, documentation says that:
Every Android-compatible device supports a shared "external storage" that you can use to save files. This can be a removable storage media (such as an SD card) or an internal (non-removable) storage.
That means you don't have to worry about device not having an external storage at all. Still, external storage can be unavailable at times. Refer to http://developer.android.com/guide/topics/data/data-storage.html#filesExternal

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
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;
}
enter code here

In order to share a private file you need to use a ContentProvider to provide access to your file by other applications. Here's a great example: Android: Attaching files from internal cache to Gmail.
Also, although the tutorial mentions that you need to declare your provider in the Android manifest file, it does not specify that it should be contained in <application>, so make sure that when you declare it is within <application> </application>.

This Code may help you out to get idea about attachment:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
buttonSend = (Button) findViewById(R.id.buttonSend);
textTo = (EditText) findViewById(R.id.editTextTo);
textSubject = (EditText) findViewById(R.id.editTextSubject);
textMessage = (EditText) findViewById(R.id.editTextMessage);
buttonSend.setOnClickListener( new OnClickListener() {
#Override
public void onClick(View v) {
String to = textTo.getText().toString();
String subject = textSubject.getText().toString();
String message = textMessage.getText().toString();
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("plain/text");
File data = null;
try {
Date dateVal = new Date();
String filename = dateVal.toString();
data = File.createTempFile("Report", ".csv");
FileWriter out = (FileWriter) GenerateCsv.generateCsvFile(
data, "Name,Data1");
i.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(data));
i.putExtra(Intent.EXTRA_EMAIL, new String[] { to });
i.putExtra(Intent.EXTRA_SUBJECT, subject);
i.putExtra(Intent.EXTRA_TEXT, message);
startActivity(Intent.createChooser(i, "E-mail"));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
public class GenerateCsv
{
public static FileWriter generateCsvFile(File sFileName,String fileContent)
{
FileWriter writer = null;
try {
writer = new FileWriter(sFileName);
writer.append(fileContent);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return writer;
}
}
The above code requires you add the following permission to your manifest file:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>

Try using Context.MODE_WORLD_READABLE instead of Context.MODE_PRIVATE when saving the file. Then other apps will have access to the file.

I have also experienced this problem using internal files and although I have used openFileInput with MODE_WORLD_READABLE on /data/data//files/testFileName.txt and used the URI.parse with the extra "/" (see below), the received test emailed is still lacking the desired attachment. So sorry but there is no answer, except try to use External files on the SD Card - which is my next experiment!
Code :
File tmpFile = new File(context.getFilesDir(), mfileName);
Log.d(TAG, tmpFile.toString());
// This shows: /data/data/org.eddiem.adeveloper.flatfiletest/files/testFile.csv
//File tmpFile2 = new File(context.getFileStreamPath(mfileName), mfileName);
//Log.v(TAG, tmpFile2.toString());
// This also shows: /data/data/org.eddiem.adeveloper.flatfiletest/files/testFile.csv
//Uri uri = Uri.fromFile(new File(context.getFileStreamPath(mfileName), mfileName));
Uri uri = Uri.parse("file://" + tmpFile.toString());
//Uri uri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(),
// mfileName));
Log.d(TAG, "Uri-path is: " + uri.getPath()); // or .toString()
Intent i = new Intent(android.content.Intent.ACTION_SEND);
i.setType("text/plain");
i.putExtra(Intent.EXTRA_EMAIL, new String[]{"eddie.moxey#sky.com"});
i.putExtra(Intent.EXTRA_SUBJECT, "Test Email - with Attachment");
i.putExtra(Intent.EXTRA_TEXT, "This is a test Email with an Attachment.");
i.putExtra(Intent.EXTRA_STREAM, uri);
//startActivity(Intent.createChooser(i, "Select application"));
startActivity(Intent.createChooser(i, "Send mail"));

I was facing the same issue and following worked for me.
First send Broadcast to notify device that file is created / mounted.
For example:
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,Uri.parse("file://"+storagePath)));
Then use code to send mail with attachment.
Intent email = new Intent(Intent.ACTION_SEND);
email.putExtra(Intent.EXTRA_EMAIL, "Receiver Email Address" );
email.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
email.putExtra(Intent.EXTRA_SUBJECT, "Subject");
email.putExtra(Intent.EXTRA_TEXT,"Email Text");
//Mime type of the attachment (or) u can use sendIntent.setType("*/*")
//email.setType("text/plain");
email.setType("application/YourMimeType");
//Full Path to the attachment
email.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://"+storagePath));
try
{
startActivity(Intent.createChooser(email, "Send Message..."));
}
catch (android.content.ActivityNotFoundException ex)
{
}

Related

Android email intent not attaching files as attachment

For my company i am trying to send email from my android app using email intent.
I'm using emulator to test my app. But the problem is when i'm trying to Add and Attachment (eg. pdf, image) it won't attaching yet.
here is my code:
private String SendEmail(String oid, final String img, final String party_code, final String order_by, final Bitmap attachimg, final String note) {
try {
String filename="DOAttachment.jpeg";
String subject ="Order "+oid+" has been sent successfully";
String body="\nDear Sir, \n"+"Please find the attached file herewith.\nThe D.O for the customer(party) "+party_code+" has been successfully done with the order number: "+oid+"\n\n\n"+"With regards \n \n Employee code/ID: "+order_by+"\n\nN.B:"+note;
File root = Environment.getExternalStorageDirectory();
String pathToMyAttachedFile = "DirName/"+filename;
File file = new File(root, pathToMyAttachedFile);
file.setReadable(true,false);
Log.e("File path"," "+file);
//using outlook
Intent intent = new Intent(Intent.ACTION_VIEW).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP);
intent.setType("image/*");
Uri data = Uri.parse("mailto:?subject=" + subject + "&body=" + body+ "&stream="+Uri.parse("file:///"+Environment.getExternalStorageDirectory().getAbsolutePath())+"/DirName/"+filename);
intent.setData(data);
intent .putExtra(Intent.EXTRA_EMAIL, toemail);
if (!file.exists() || !file.canRead()||!file.canWrite()) {
Log.e(" FILE ERROR ","File Not found");
Toast.makeText(getApplicationContext(),"File Not found",Toast.LENGTH_LONG).show();
}
else {
file.setReadable(true);
Log.e(" FILE OK ","File was found");
Uri uri = Uri.fromFile(file);
intent.putExtra(Intent.EXTRA_STREAM, uri);
}
));
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
return "TRUE";
} catch (Exception e) {
Log.e("SendMail", e.getMessage(), e);
return "MAIL NOT SENT";
}
}
The result is email with empty attachment find screenshot : https://1drv.ms/i/s!AruisQQIx8MTgatuhJFmSaoArg_6Xw
Check whether you had provided READ_EXTERNAL_STORAGE permission for your application both in manifest and run-time.
Then Call the below method send mail assuming the full file path is saved in filePath.
File root = Environment.getExternalStorageDirectory();
String pathToMyAttachedFile = "DirName/"+filename;
File filePath = new File(root, pathToMyAttachedFile)
sendEmailAlert(filePath,subject,text);
CALLED METHOD
private void sendEmailAlert(File fileName,String subject,String text) {
final File file=fileName;
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("application/octet-stream"); /* or use intent.setType("message/rfc822); */
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_TEXT, text);
if (!file.exists() || !file.canRead()) {
Toast.makeText(getContext(), "Attachment Error", Toast.LENGTH_SHORT).show();
return;
}
Uri uri = Uri.fromFile(file);
intent.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(intent, "Send email..."));
}

Share an image with a content provider in Android app

I'm developing an Android app that is a gallery of images in which the images are downloaded from internet for display on the screen of smathphone. Images are displayed one at a time and the application has a button to share the image that is displayed.
Following the directions I've found in some StackOverflow post which indicated that the right way to share an image was using a ContentProvider I have implemented the following code that works to share the images of certain applications (eg Twitter, Gmail ...) but does not work for others (Facebook, Yahoo, MMS ...).
Then I show the code used hoping you can help me get the correct implementation to share images in all applications.
Initially I capture the button press to share:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.menu_share) {
// I get the image being displayed on the screen
View root = getView();
ImageView imageView = (ImageView) root.findViewById(R.id.image);
Drawable imageToShareDrawable = imageView.getDrawable();
if (imageToShareDrawable instanceof BitmapDrawable) {
// I convert the image to Bitmap
Bitmap imageToShare = ((BitmapDrawable) imageToShareDrawable).getBitmap();
// Name of de image extracted from a bean property
String fileName = quote.getImage();
// I keep the image in the folder "files" of internal storage application
TempInternalStorage.createCachedFile(fileName, imageToShare, getActivity().getApplicationContext());
// I start the Activity to select the application to share the image after the intent Built with the method "getDefaultShareIntent"
startActivity(getDefaultShareIntent(fileName));
} else {
Toast.makeText(getActivity().getApplicationContext(), "Please wait, the quote is being downloaded", Toast.LENGTH_SHORT).show();
}
}
return true;
}
The method for saving the image to the internal storage of the application is as follows:
public static void createCachedFile(String fileName, Bitmap image, Context context) {
try {
File file = new File(context.getFilesDir(), fileName);
if (!file.exists()) {
FileOutputStream fos = new FileOutputStream(file);
image.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
}
} catch (Exception e) {
Log.e("saveTempFile()", "**** Error");
}
}
The method that constructs the Intent to share it:
private Intent getDefaultShareIntent(String fileName) {
final Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("image/jpeg");
shareIntent.putExtra(Intent.EXTRA_TEXT, "Test text");
shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + CachedFileProvider.AUTHORITY + File.separator + "img" + File.separator + fileName));
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
return shareIntent;
}
Finally ContentProvider code is as follows:
public class CachedFileProvider extends ContentProvider {
private static final String CLASS_NAME = "CachedFileProvider";
public static final String AUTHORITY = "com.example.appname.cachefileprovider";
private UriMatcher uriMatcher;
#Override
public boolean onCreate() {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "img/*", 1);
return true;
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
String LOG_TAG = CLASS_NAME + " - openFile";
Log.i(LOG_TAG, "Called with uri: '" + uri + "'." + uri.getLastPathSegment());
switch (uriMatcher.match(uri)) {
case 1:
String fileLocation = getContext().getFilesDir() + File.separator + uri.getLastPathSegment();
ParcelFileDescriptor image = ParcelFileDescriptor.open(new File(fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
return image;
default:
Log.i(LOG_TAG, "Unsupported uri: '" + uri + "'.");
throw new FileNotFoundException("Unsupported uri: " + uri.toString());
}
}
#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) {
MatrixCursor c = null;
Log.i(">>>> projection", java.util.Arrays.toString(projection));
String fileLocation = getContext().getFilesDir() + File.separator + uri.getLastPathSegment();
File file = new File(fileLocation);
long time = System.currentTimeMillis();
c = new MatrixCursor(new String[] { "_id", "_data", "orientation", "mime_type", "datetaken", "_display_name" });
c.addRow(new Object[] { 0, file, 0, "image/jpeg", time, uri.getLastPathSegment() });
return c;
}
#Override
public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
return null;
}
}
I have found that when the image is sharing some applications only call the method "query" (these are where the code does not work and I can not share the image) while there are others that also call the method "query" also call the method "openFile" and these do work and I can share the image.
I hope you can help me, thank you very much in advance.
As #Sun Ning-s comment noted some "share target apps" can handle URI-s starting with "content://.." which you have implemented.
Other apps handle file uri-s starting with "file://...." so you have to implement a 2nd share menue "share as file"
private Intent getFileShareIntent(String fullPathTofile) {
final Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("image/jpeg");
shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + fullPathTofile));
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
return shareIntent;
}
You can use the android app intentintercept to find out what other "share source apps" provide.

How to get camera result as a uri in data folder?

I am creating an application in which I want to capture a image and then I want to send that image in the email as a attachment.
I am opening a camera using android.provider.MediaStore.ACTION_IMAGE_CAPTURE intent action and I am passing the Uri of the file as a parameter EXTRA_OUTPUT to get the image back to the file. This is working perfectly and I am able to get the captured image if I use the external storage uri as a EXTRA_OUTPUT but if I use the data folder uri it is not working and the camera is not closing and its all buttons are not working.
Here is my code for get the result in the external storage directory
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File out = Environment.getExternalStorageDirectory();
out = new File(out, imagename);
i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(out));
startActivityForResult(i, CAMERA_RESULT);
And this code is for get the image in the data folder
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File out = getFilesDir();
out = new File(out, MyPharmacyOptions.PRESCRIPTION_IMAGE_NAME);
i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(out));
startActivityForResult(i, CAMERA_RESULT);
I knew that the data folder is not accessible to third application so may be this causes an issue so I have create one content provider to share the file.
Here is my content provide class
public class MyContentProvider extends ContentProvider {
private static final String Tag = RingtonContentProvider.class.getName();
public static final Uri CONTENT_URI = Uri
.parse("content://x.y.z/");
private static final HashMap<String, String> MIME_TYPES = new HashMap<String, String>();
static {
MIME_TYPES.put(".mp3", "audio/mp3");
MIME_TYPES.put(".wav", "audio/mp3");
MIME_TYPES.put(".jpg", "image/jpeg");
}
#Override
public boolean onCreate() {
return true;
}
#Override
public String getType(Uri uri) {
String path = uri.toString();
for (String extension : MIME_TYPES.keySet()) {
if (path.endsWith(extension)) {
return (MIME_TYPES.get(extension));
}
}
return (null);
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
File f = new File(getContext().getFilesDir(), uri.getPath());
if (f.exists()) {
return (ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY));
}
throw new FileNotFoundException(uri.getPath());
}
#Override
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sort) {
throw new RuntimeException("Operation not supported");
}
#Override
public Uri insert(Uri uri, ContentValues initialValues) {
File file = new File(getContext().getFilesDir(), uri.getPath());
if(file.exists()) file.delete();
try {
file.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return Uri.fromFile(file);
}
#Override
public int update(Uri uri, ContentValues values, String where,
String[] whereArgs) {
throw new RuntimeException("Operation not supported");
}
#Override
public int delete(Uri uri, String where, String[] whereArgs) {
File f = new File(getContext().getFilesDir(), "image1.jpg");
if(f.exists()) f.delete();
f = new File(getContext().getFilesDir(), "image2.jpg");
if(f.exists()) f.delete();
getContext().getContentResolver().notifyChange(CONTENT_URI, null);
}
}
So to use this content provide I am using following code to pass the uri to the camera activity
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
Uri uri = MyContentProvider.CONTENT_URI;
uri = Uri.withAppendedPath(uri, imagename);
getContentResolver().insert(uri, null);
getContentResolver().notifyChange(RingtonContentProvider.CONTENT_URI, null);
Log.d(Tag, uri.toString());
i.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(i, CAMERA_RESULT);
Now if I pass the url other then external storage directory the camera is opening but it is not closing in emulator but in device the camera is going to closed but I am not getting the result.
I have declared this content provide in the manifest file
<provider
android:name=".contentproviders.MyContentProvider"
android:authorities="x.y.z" />
Also I have given the permission to write the external storage and also for use the camera.
I am able to capture the image using the external storage but I want to store the image in the data directory instead of external storage because if the external storage in not available I want to capture the image and want to send mail.
If I create content provide then I can also share my image to the email application.
If we not provide the extras with the camera intent it will return the image as a byte[] in the activity result as a data extra but this is for the purpose of the thumbnail so I can't get the high resolution image using this way.
There are two ways to solve this problem.
1. Save bitmap which you received from onActivityResult method
You can start camera through intent to capture photo using below code
Intent cameraIntent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
After capture photo, you will get bitmap in onActivityResult method
if (requestCode == CAMERA_REQUEST) {
Bitmap photo = (Bitmap) data.getExtras().get("data");
}
Now you can simply save this bitmap to internal storage
Note: Here bitmap object consists of thumb image, it will not have a full resolution image
2. Save bitmap directly to internal storage using content provider
Here we will create content provider class to allow permission of local storage directory to camera activity
Sample provider example as per below
public class MyFileContentProvider extends ContentProvider {
public static final Uri CONTENT_URI = Uri.parse
("content://com.example.camerademo/");
private static final HashMap<String, String> MIME_TYPES =
new HashMap<String, String>();
static {
MIME_TYPES.put(".jpg", "image/jpeg");
MIME_TYPES.put(".jpeg", "image/jpeg");
}
#Override
public boolean onCreate() {
try {
File mFile = new File(getContext().getFilesDir(), "newImage.jpg");
if(!mFile.exists()) {
mFile.createNewFile();
}
getContext().getContentResolver().notifyChange(CONTENT_URI, null);
return (true);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
#Override
public String getType(Uri uri) {
String path = uri.toString();
for (String extension : MIME_TYPES.keySet()) {
if (path.endsWith(extension)) {
return (MIME_TYPES.get(extension));
}
}
return (null);
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
File f = new File(getContext().getFilesDir(), "newImage.jpg");
if (f.exists()) {
return (ParcelFileDescriptor.open(f,
ParcelFileDescriptor.MODE_READ_WRITE));
}
throw new FileNotFoundException(uri.getPath());
}
}
After that you can simply use the URI to pass to camera activity using the below code
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.EXTRA_OUTPUT, MyFileContentProvider.CONTENT_URI);
startActivityForResult(i, CAMERA_RESULT);
If you don't want to create your own provider then you can use FileProvider from support-library-v4. For detailed help you can look into this post
Best solution I found is: FileProvider (needs support-library-v4)
It uses the internal storage!
https://developer.android.com/reference/android/support/v4/content/FileProvider.html
Define your FileProvider in Manifest in Application element:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="your.package.name.fileprovider"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/image_path" />
</provider>
Add permissions in manifest root element:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
Define your image paths in for example res/xml/image_path.xml:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="captured_image" path="your/path/"/>
</paths>
Java:
private static final int IMAGE_REQUEST_CODE = 1;
// your authority, must be the same as in your manifest file
private static final String CAPTURE_IMAGE_FILE_PROVIDER = "your.package.name.fileprovider";
4.1 capture intent:
File path = new File(activity.getFilesDir(), "your/path");
if (!path.exists()) path.mkdirs();
File image = new File(path, "image.jpg");
Uri imageUri = FileProvider.getUriForFile(activity, CAPTURE_IMAGE_FILE_PROVIDER, image);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, IMAGE_REQUEST_CODE);
4.2 onActivityResult():
#Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == IMAGE_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
File path = new File(getFilesDir(), "your/path");
if (!path.exists()) path.mkdirs();
File imageFile = new File(path, "image.jpg");
// use imageFile to open your image
}
}
super.onActivityResult(requestCode, resultCode, intent);
}
Intent to call to capture photo,
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
Then Take Bitmap in ActivityResult
if (requestCode == CAMERA_REQUEST) {
Bitmap photo = (Bitmap) data.getExtras().get("data");
}
Then Write that Into Internal Memory, see this
// The openfileOutput() method creates a file on the phone/internal storage in the context of your application
final FileOutputStream fos = openFileOutput("my_new_image.jpg", Context.MODE_PRIVATE);
// Use the compress method on the BitMap object to write image to the OutputStream
bm.compress(CompressFormat.JPEG, 90, fos);
Then next time to read that file,
Bitmap bitmap = BitmapFactory.decodeFile(file);
At first save came photo in your external storage and try it -
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.imageView = (ImageView)this.findViewById(R.id.imageView1);
Button photoButton = (Button) this.findViewById(R.id.button1);
photoButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
}
});
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAMERA_REQUEST) {
Bitmap bmp = intent.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray(); // convert camera photo to byte array
// save it in your external storage.
FileOutputStream fo = new FileOutputStream(new File(Environment.getExternalStorageDirectory() + "/_camera.png"));
fo.write(byteArray);
fo.flush();
fo.close();
}
}
Next target -
File cameraFile = new File(Environment.getExternalStorageDirectory() + "/_camera.png");
startActivityForResult(Intent.createChooser(new Intent(Intent.ACTION_SEND)
.setType("image/jpg")
.putExtra(Intent.EXTRA_SUBJECT, "Subject")
.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(cameraFile))
.putExtra(Intent.EXTRA_TEXT, textBody), "Send your message using"), Constant.EMAIL);
You can also do this without needing a content provider since you will need the sd card to open the camera image capture intent anyway. You can of course hack around the presence of the sd card but not with the camera intent capture....So you are checking for external storage but need it at this point.... FYI you should also check out a crop library like crop-image instead of using native, as it is not well supported across devices.
File mediaStorageDir;
String photoFileName = "photo.jpg";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// page = getArguments().getInt("someInt", 0);
// title = getArguments().getString("someTitle");
// Get safe storage directory for photos
mediaStorageDir = new File(
Environment .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
APP_TAG);
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists() && !mediaStorageDir.mkdirs()) {
Log.d(APP_TAG, "Directory exists: " + mediaStorageDir.isDirectory());
Log.d(APP_TAG, "Directory exists: " + mediaStorageDir.getPath());
Log.d(APP_TAG,
"Directory exists: "
+ Environment.getExternalStorageState());
Log.d(APP_TAG, "failed to create directory");
}
}
in your 'take the pic' code:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, getPhotoFileUri(photoFileName));
...
public Uri getPhotoFileUri(String fileName) {
return Uri.fromFile(new File(mediaStorageDir.getPath() + File.separator
+ fileName));
}
After researching this bug for some time, I have noticed that the activity that called the camera intent is only restarted when the phone is running out of memory.
so because the activity is restarted, the object holding the path or Uri to the captured image is refreshed (nulled)
so I would suggest you catch/ detect the null object in the onActivityResult, and prompt the user to free up space on their device or restart their phone for a quick temporary fix.

Android attaching multiple files from sdcard to email

How to attach multiple files in email in android?
Is there any permission required for multiple files attachment to an intent?
I am trying with putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList uriList) method but still in doubt whether Uri class is <? extends Parcelable> or not. I am not able to attach any file to email.
This is my code ::
Intent sendIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
sendIntent.setType("plain/text");
sendIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"soubhabpathak2010#gmail.com"});
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "Accident Capture");
sendIntent.putExtra(Intent.EXTRA_TEXT, emailBody);
ArrayList<Uri> uriList = getUriListForImages();
sendIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriList);
Log.d(TAG, "Size of the ArrayList :: " +uriList.size());
FormHolderActivity.this.startActivity(Intent.createChooser(sendIntent, "Email:"));
and getUriListForImages() this method is defined as bellow -----
private ArrayList<Uri> getUriListForImages() {
ArrayList<Uri> uriList = new ArrayList<Uri>();
String imageDirectoryPath = Environment.getExternalStorageDirectory().getAbsolutePath()+ "/accident/";
File imageDirectory = new File(imageDirectoryPath);
String[] fileList = imageDirectory.list();
if(fileList.length != 0) {
for(int i=0; i<fileList.length; i++)
{
String file = "file://" + imageDirectoryPath + fileList[i];
Log.d(TAG, "File name for Uri :: " + file);
Uri uriFile = Uri.parse(file);
uriList.add(uriFile);
Log.d(TAG, "Image File for Uri :: " +(file));
}
}
return uriList;
}
To, subject and body of the email is coming and I have images in the accident folder in sdcard (I am using 2.1 API level 7) but nothing is attaching even there is also no exception in logcat.Arraylist is also ok(means length OK and name of the files are ok too). Can anyone help me to solve this problem?
After 1 day work finally I am able to attach multiple image files from \sdcard\accident\ folder to email client. For attaching multiple files I had to add the images to the ContentResolver which is responsible for gallery images provider.
Here is the Complete Code ---
Intent sendIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
sendIntent.setType("plain/text");
sendIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"soubhabpathak2010#gmail.com"});
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "Accident Capture");
sendIntent.putExtra(Intent.EXTRA_TEXT, emailBody);
ArrayList<Uri> uriList = getUriListForImages();
sendIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriList);
Log.d(TAG, "Size of the ArrayList :: " +uriList.size());
FormHolderActivity.this.startActivity(Intent.createChooser(sendIntent, "Email:"));
So there is no change in the First Section of Code -- But Change is in getUriListForImages() method which is as follows---
private ArrayList<Uri> getUriListForImages() throws Exception {
ArrayList<Uri> myList = new ArrayList<Uri>();
String imageDirectoryPath = Environment.getExternalStorageDirectory().getAbsolutePath()+ "/accident/";
File imageDirectory = new File(imageDirectoryPath);
String[] fileList = imageDirectory.list();
if(fileList.length != 0) {
for(int i=0; i<fileList.length; i++)
{
try
{
ContentValues values = new ContentValues(7);
values.put(Images.Media.TITLE, fileList[i]);
values.put(Images.Media.DISPLAY_NAME, fileList[i]);
values.put(Images.Media.DATE_TAKEN, new Date().getTime());
values.put(Images.Media.MIME_TYPE, "image/jpeg");
values.put(Images.ImageColumns.BUCKET_ID, imageDirectoryPath.hashCode());
values.put(Images.ImageColumns.BUCKET_DISPLAY_NAME, fileList[i]);
values.put("_data", imageDirectoryPath + fileList[i]);
ContentResolver contentResolver = getApplicationContext().getContentResolver();
Uri uri = contentResolver.insert(Images.Media.EXTERNAL_CONTENT_URI, values);
myList.add(uri);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return myList;
}
This is working fine and I am able to attach multiple image files to emulator default email client and send them successfully .
EXTRA_STREAM says this:
A content: URI holding a stream of data associated with the Intent, used with
ACTION_SEND to supply the data being sent.
Constant Value: "android.intent.extra.STREAM"
You can not pass a set of file URIs: it will simply ignore the results (as you are observing).
EDIT: scratch that. I was wrong. This is the chunk of code in the standard Android Email client that handles multiple files.
if (Intent.ACTION_SEND_MULTIPLE.equals(mAction)
&& intent.hasExtra(Intent.EXTRA_STREAM)) {
ArrayList<Parcelable> list = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
if (list != null) {
for (Parcelable parcelable : list) {
Uri uri = (Uri) parcelable;
if (uri != null) {
Attachment attachment = loadAttachmentInfo(uri);
if (MimeUtility.mimeTypeMatches(attachment.mMimeType,
Email.ACCEPTABLE_ATTACHMENT_SEND_INTENT_TYPES)) {
addAttachment(attachment);
}
}
}
}
}
Try doing this:
private ArrayList<Parcelable> getUriListForImages() {
ArrayList<Parcelable> uriList = new ArrayList<Parcelable>();
String imageDirectoryPath = Environment.getExternalStorageDirectory().getAbsolutePath()+ "/accident/";
File imageDirectory = new File(imageDirectoryPath);
String[] fileList = imageDirectory.list();
if(fileList.length != 0) {
for(int i=0; i<fileList.length; i++)
{
String file = "file://" + imageDirectoryPath + fileList[i];
Log.d(TAG, "File name for Uri :: " + file);
Uri uriFile = Uri.parse(file);
uriList.add(uriFile);
Log.d(TAG, "Image File for Uri :: " +(file));
}
}
return uriList;
}

android exporting to csv and sending as email attachment

I have seen multiple threads in this site discussing about sending email with attachments in android. I tried every methods discussed here, here and here.
I am creating a csv file via code and saving this file to android internal storage. Then I want to send this file as attachment in an email. Well, the email is being sent, I am getting it without attachment. This is what I have done.
String columnString = "\"Person\",\"Gender\",\"Street1\",\"PostOfice\",\"Age\"";
String dataString = "\"" + currentUser.userName +"\",\"" + currentUser.gender + "\",\"" + currentUser.street1 + "\",\"" + currentUser.poNumber.toString() + "\",\"" + currentUser.age.toString() + "\"";
String combinedString = columnString + "\n" + dataString;
File file = new File(this.getCacheDir()+ File.separator + "Data.csv");
try {
FileOutputStream out = new FileOutputStream(file);
out.write(combinedString.getBytes());
out.close();
} catch (IOException e) {
Log.e("BROKEN", "Could not write file " + e.getMessage());
}
Uri u1 = Uri.fromFile(file);
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "Person Details");
sendIntent.putExtra(Intent.EXTRA_STREAM, u1);
sendIntent.setType("text/richtext");
startActivity(sendIntent);
I tried changing mime settings to "text/html" and "text/richtext" etc. But no luck yet. Can anyone tell me what I am doing wrong?
Thanks for everyone who tried to help..After taking a full day I have send an email from my app with attachment..This is the working code..
String columnString = "\"PersonName\",\"Gender\",\"Street1\",\"postOffice\",\"Age\"";
String dataString = "\"" + currentUser.userName +"\",\"" + currentUser.gender + "\",\"" + currentUser.street1 + "\",\"" + currentUser.postOFfice.toString()+ "\",\"" + currentUser.age.toString() + "\"";
String combinedString = columnString + "\n" + dataString;
File file = null;
File root = Environment.getExternalStorageDirectory();
if (root.canWrite()){
File dir = new File (root.getAbsolutePath() + "/PersonData");
dir.mkdirs();
file = new File(dir, "Data.csv");
FileOutputStream out = null;
try {
out = new FileOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
out.write(combinedString.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Uri u1 = null;
u1 = Uri.fromFile(file);
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "Person Details");
sendIntent.putExtra(Intent.EXTRA_STREAM, u1);
sendIntent.setType("text/html");
startActivity(sendIntent);
Also If you have mounted your phone SDCard in the machine , this code wont work. Only one can access SDCard at one time. So in that case unmount your SDCard from computer and try..Thanks to the guy who answered here..Also make sure you have bought permission to write to external Storage in your manifest file...
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
Hope it helps someone...Thanks for everyone who tried to help..
This Code will help you out
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
buttonSend = (Button) findViewById(R.id.buttonSend);
textTo = (EditText) findViewById(R.id.editTextTo);
textSubject = (EditText) findViewById(R.id.editTextSubject);
textMessage = (EditText) findViewById(R.id.editTextMessage);
buttonSend.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
String to = textTo.getText().toString();
String subject = textSubject.getText().toString();
String message = textMessage.getText().toString();
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("plain/text");
File data = null;
try {
Date dateVal = new Date();
String filename = dateVal.toString();
data = File.createTempFile("Report", ".csv");
FileWriter out = (FileWriter) GenerateCsv.generateCsvFile(
data, "Name,Data1");
i.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(data));
i.putExtra(Intent.EXTRA_EMAIL, new String[] { to });
i.putExtra(Intent.EXTRA_SUBJECT, subject);
i.putExtra(Intent.EXTRA_TEXT, message);
startActivity(Intent.createChooser(i, "E-mail"));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
public class GenerateCsv {
public static FileWriter generateCsvFile(File sFileName,String fileContent) {
FileWriter writer = null;
try {
writer = new FileWriter(sFileName);
writer.append(fileContent);
writer.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally
{
try {
writer.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return writer;
}
}
Add this line in AndroidManifest.xml file:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission
Try
sendIntent.setType("message/rfc822");
For internal storage files, you need to make the file readable:
shareFile.setReadable(true, false);
Here is the code for attachment of csv file in mail (Its working code):
MyCsvFile.csv" should be present in your Internal/External memory of phone.
For More Look into this :https://stackoverflow.com/a/48643905/8448886
Below is the code for attachment of csv file into mail :
String csv = (Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyCsvFile.csv"); // Here csv file name is MyCsvFile.csv
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType("text/plain");
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"email#example.com"});
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "subject here");
emailIntent.putExtra(Intent.EXTRA_TEXT, "body text");
File file = new File(csv);
Uri uri = Uri.fromFile(file);
emailIntent.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(emailIntent, "Pick an Email provider"));
}
});

Categories

Resources