Reputation: 738
I am trying to create a option for the user to send the file from my application via email only. The file is internal to the application and is accessible via the FileProvider.
This is the contentURI looks like content://packagename.files/files/somefile.ext
Here as you can see that I am giving the user to share the file to PicsArt, Google Drive, OneDrive and EMail.
I am able to share the content to the first three clients successfully as they very specific applications. But when it comes to Email, I need to user to pick the client from the applications he has installed in his mobile.
Here are 2 set of codes I have created:
Code Option 1:
Intent EMail = ShareCompat.IntentBuilder.from(this)
.setType("message/rfc822")
.setSubject("Emailing: File Attached")
.setText("Hello")
.setStream(contentUri)
.setChooserTitle("Send via EMail").getIntent();
startActivity(Intent.createChooser(EMail, "Send via EMail"));
Above code shows me a chooser where there are many applications which can handle the files as shown in the image below.
This one works fine if I select any email client application or any other application.
But the problem with this is that there is an option for the user to select any application, which is not the desired behavior of the application. So, I modified the code as below:
final Intent _Intent = new Intent(Intent.ACTION_SENDTO);
_Intent.setType("text/html");
_Intent.setData(Uri.parse("mailto:"));
_Intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
_Intent.putExtra(Intent.EXTRA_STREAM, contentUri);
_Intent.putExtra(android.content.Intent.EXTRA_SUBJECT,
"Emailing: File Attached");
_Intent.putExtra(android.content.Intent.EXTRA_TEXT,
"Hello");
startActivity(Intent.createChooser(_Intent, "Send via EMail"));
Here is the outcome of the code:
But, now the problem here is that I am not able to send the file from the content provider (FileProvider). The email client shows the message as below after selecting:
It is simply not attaching the file to the email in any client in the above list.
I will be greatfull, if anyone can help me out here. I think that, I have tried all the possible scenarios here, by changing the mime-type, setting content in different manner setting data setting stream etc, but not able to get the desired outcome.
Please let me know in case you need any other details on this.
Thanks again in advance.
Upvotes: 5
Views: 2323
Reputation: 936
try this snippet.
Intent testIntent = new Intent(Intent.ACTION_VIEW);
Uri data = Uri.parse("mailto:?subject=" + "Feedback" + "&body=" + "Write Feedback here....." + "&to=" + "[email protected]");
testIntent.setData(data);
startActivity(testIntent);
Upvotes: 0
Reputation: 738
I have decided to copy the file from internal app storage to external app storage (not external public storage) and sharing the file from there. I am bit surprised though as the FileProvider is capable of sharing the files from the internal file storage with anything and every thing in the system but fails to do so when I want to filter the Intents which are email clients only.
It was kind of difficult for me to implement a custom provider at beginner's level.
Upvotes: 0
Reputation: 3112
You need to write the ContentProvider which will provide the InputStream to the client to which you have passed the ContentUri or may be you can directly provide the file path if its present in the SdCard or the Internal storage because you will need to handle the uri's and pass the InputStream . Note: ExtraStream is best for files that are not in device that is which is to be accessed from internet.
public class SampleContentProvider extends ContentProvider implements ContentProvider.PipeDataWriter<InputStream> {
static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//Uri matcher for different
}
/**
* Database specific constant declarations
*/
private SQLiteDatabase db;
@Override
public boolean onCreate() {
return true;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
throw new SQLException("Insert operation not supported for " + uri);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
//condition just for files. You can try something else
if (uri.toString().contains("files")) {
//you get the file name
String lastSegment = uri.getLastPathSegment();
if (projection == null) {
projection = new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
}
File file = //Code to read the file as u have the directory, just get the file from the file name obtained from the uri
if (null == file) {
throw new IllegalArgumentException("Unknown File for Uri " + uri);
}
String[] cols = new String[projection.length];
Object[] values = new Object[projection.length];
int i = 0;
for (String col : projection) {
if (OpenableColumns.DISPLAY_NAME.equals(col)) {
cols[i] = OpenableColumns.DISPLAY_NAME;
values[i++] = //file name;
} else if (OpenableColumns.SIZE.equals(col)) {
cols[i] = OpenableColumns.SIZE;
values[i++] = //file size;
}
}
cols = copyOf(cols, i);
values = copyOf(values, i);
final MatrixCursor cursor = new MatrixCursor(cols, 1);
cursor.addRow(values);
return cursor;
}
return super.query(uri, projection, selection, selectionArgs, sortOrder);
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return super.delete(uri, selection, selectionArgs);
}
private static String[] copyOf(String[] original, int newLength) {
final String[] result = new String[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
private static Object[] copyOf(Object[] original, int newLength) {
final Object[] result = new Object[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
@Nullable
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
File file = //read the file
if (file != null) {
try {
StrictMode.ThreadPolicy tp = StrictMode.ThreadPolicy.LAX;
StrictMode.setThreadPolicy(tp);
InputStream in = //Code to get the inputstream;
// Start a new thread that pipes the stream data back to the caller.
return openPipeHelper(uri, null, null, in, this);
} catch (IOException e) {
FileNotFoundException fnf = new FileNotFoundException("Unable to open " + uri);
throw fnf;
}
}
throw new IllegalArgumentException("Unknown URI " + uri);
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return super.update(uri, values, selection, selectionArgs);
}
@Override
public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
Bundle opts, InputStream args) {
// Transfer data from the asset to the pipe the client is reading.
byte[] buffer = new byte[8192];
int n;
FileOutputStream fout = new FileOutputStream(output.getFileDescriptor());
try {
while ((n = args.read(buffer)) >= 0) {
fout.write(buffer, 0, n);
}
} catch (IOException e) {
} finally {
try {
args.close();
} catch (IOException e) {
}
try {
fout.close();
} catch (IOException e) {
}
}
}
}
Upvotes: 1