How to create a Uri from an Android resource with file extension - java

I am trying to send a png file in my res/drawable folder as an attachment in my email. I can get the file to attach and be included in the email, but it is missing the .png file extension. I want it to include the extension in the file name. Can someone help? Here is my code:
Intent i = new Intent(Intent.ACTION_SEND_MULTIPLE);
i.setType("message/rfc822");
i.putExtra(Intent.EXTRA_SUBJECT, "Subject");
i.putExtra(Intent.EXTRA_TEXT , "Text");
ArrayList<Uri> uris = new ArrayList<Uri>();
Uri path = Uri.parse("android.resource://" + getPackageName() + "/" + R.drawable.png_image);
uris.add(newPath);
i.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
try {
startActivity(Intent.createChooser(i, "Send mail..."));
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(ShareActivity.this, "There are no email clients installed.", Toast.LENGTH_SHORT).show();
}

Change the type to this:
i.setType("image/png");
Although I believe putParcelableArrayListExtra() is not necessary. putExtra() should do the job.

Use this method to get the extension of the file
public static String getMimeType(Context context, Uri uri) {
String extension;
//Check uri format to avoid null
if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
//If scheme is a content
final MimeTypeMap mime = MimeTypeMap.getSingleton();
extension = mime.getExtensionFromMimeType(context.getContentResolver().getType(uri));
} else {
//If scheme is a File
//This will replace white spaces with %20 and also other special characters. This will avoid returning null values on file name with spaces and special characters.
extension = MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(new File(uri.getPath())).toString());
}
return extension;
}

Related

Understanding FileProvider in Android Studio

I am very noob in Android studio Java apps. so please bear with me on this.
So I would like to save some text data to a .txt file and then share this Text file on a click of a button as attachment for an Email.
I think there are two ways of doing this:
1- Send the .txt file as attachment per email in the background of the activity/App or whatever it is called.
for that way I have already found the SendGmail and I don't know the available stuff in Internet and so on and made Gmail account less secure and stuff but as a noob it just did not work for me so if you can provide me with step-by-step salutation it will be great (it would really really help me if you don't miss out any step even if small and understandable,... thanks in advance)
2- Promote the user to send the Email by using the Intent stuff in android studio java language.
for that according also to the search I have done we need to use the FileProvider service (if I am allowed to call it like that) from the Android Studion Java language.
for that I did the following:
I Added the Stuff need to be added in the AndroidManifest.xml as following:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.XXXX.YYYY"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
and then I created the file_paths.xml file here please support me as I don't exactly know what and how to do it correctly:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="/storage/emulated/0" path="."/>
</paths>
And now I think I am ready to Save The Text data to a .txt file using this function:
filepath = "AppData";
// in side the on create method i added this permission stuff
ActivityCompat.requestPermissions(MainActivity.this, new String[]{READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE},PackageManager.PERMISSION_GRANTED);
// of course in the Manifest i also added the user request for Eternal Storage saving stuff
private String SaveToExternal(String fileContent) {
if (!fileContent.equals("")) {
File myExternalFile = new File(getExternalFilesDir(filepath), "DataToSend.txt");
String Out = getExternalFilesDir(filepath) + File.separator + "DataToSeve.txt";
FileOutputStream fos = null;
try {
fos = new FileOutputStream(myExternalFile);
fos.write(fileContent.getBytes());
} catch (FileNotFoundException e) {
e.printStackTrace();
return "Unable to save";
} catch (IOException e) {
e.printStackTrace();
return "Unable to save";
}
Toast.makeText(MainActivity.this,"Data to send is prepared and ready",Toast.LENGTH_SHORT).show();
//return Out;
return myExternalFile.getPath();
} else {
Toast.makeText(MainActivity.this,"Data to send is not available",Toast.LENGTH_SHORT).show();
return "Unable to save";
}
}
here if you can provide me with more information on how really to select where to save what it will be great (sorry for asking too much)
now the .txt file is there I just need to attach it and send it per Email of the App user: I used the following Code for that:
private void sendEmail(String Body, String FilePath) {
// Toast.makeText(MainActivity.this, "Your Email Body is: " + Body,Toast.LENGTH_LONG).show();
try {
// First copy the File from internal to External storage:
// File dst = new File(StringPath);
// exportFile(new File(FilePath), dst);
// Then Share it now
// This one was working:
Uri Pathtosend = FileProvider.getUriForFile(MainActivity.this,"com.example.XXXX.YYYY",new File(FilePath));
Toast.makeText(MainActivity.this,"Your Uri is: " + Pathtosend,Toast.LENGTH_LONG).show();
TTba.setText(Pathtosend.toString());
Intent intent = new Intent(Intent.ACTION_SENDTO)
//.setType("message/rfc822")
//.setType("text/plain")
.setType("*/*")
.setData(new Uri.Builder().scheme("mailto").build())
.putExtra(Intent.EXTRA_EMAIL, new String[]{"XXXX YYYY <XYZ#gmail.com>"})
.putExtra(Intent.EXTRA_SUBJECT, "Email subject")
.putExtra(Intent.EXTRA_TEXT, "Dear Sir/Madem" + System.getProperty("line.separator") + System.getProperty("line.separator") + "I would like to ......")
.putExtra(Intent.EXTRA_STREAM, Pathtosend)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
Intent chooser = Intent.createChooser(intent, "Send email with");
List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
grantUriPermission(packageName, Pathtosend, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
try {
// Toast.makeText(MainActivity.this,"I am at the try of send Email",Toast.LENGTH_LONG).show();
startActivity(chooser);
} catch(android.content.ActivityNotFoundException ex) {
Toast.makeText(MainActivity.this, "I am at the Catch of send Email", Toast.LENGTH_LONG).show();
ComponentName emailApp = intent.resolveActivity(getPackageManager());
ComponentName unsupportedAction = ComponentName.unflattenFromString("com.android.fallback/.Fallback");
Toast.makeText(MainActivity.this, "Results of ComponentName: " + emailApp, Toast.LENGTH_LONG).show();
if (emailApp != null && !emailApp.equals(unsupportedAction))
try {
// Needed to customise the chooser dialog title since it might default to "Share with"
// Note that the chooser will still be skipped if only one app is matched
Intent chooser1 = Intent.createChooser(intent, "Send email with");
startActivity(chooser1);
return;
} catch (ActivityNotFoundException ignored) {
}
Toast
.makeText(this, "Couldn't find an email app and account", Toast.LENGTH_LONG)
.show();
}
}catch(android.content.ActivityNotFoundException ex){
Toast.makeText(MainActivity.this,"Unable to create Pathtosend",Toast.LENGTH_LONG).show();
}
}
So after I have those two I can call it when the button is clicked and so on like this:
rv = "Testing stuff that need to be saved to Text file and then attached";
DataPathFile = SaveToExternal(rv);
sendEmail(rv,DataPathFile);
so guy now comes the problem with this method I am using. it is working on the Emulator and SAMUSNG with Android version 8.0.0 but not working on my SAMSUNG with Android version 11.
in my build.gradle i have:
defaultConfig {
applicationId "com.example.XXXX"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
I think the problem is with where the file is saved and how to know the exact path of the file and how to dynamically update the file path in xml or java or wherever it should be updated.
Thanks a lot for your help and support :)

How do I get the Uri of a file that I just downloaded using the DownloadManager class?

I need content uri of the downloaded image. I got the id of the download but I am not able to get any type of uri using getUriForDownloadedFile() function.
String imgName = "WallPics" + System.currentTimeMillis() + ".jpg";
DownloadManager.Request download = downloadImage(imgName);
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
long downloadId = manager.enqueue(download);
Uri cUri=manager.getUriForDownloadedFile(downloadId);
Log.e(TAG, "onClick: "+downloadId );
Log.i(TAG, "onClick: "+cUri);
The downloadImage function is defined as:
private DownloadManager.Request downloadImage(String imgName)
{
DownloadManager.Request download=new DownloadManager.Request(imageUri);
download.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE|
DownloadManager.Request.NETWORK_WIFI);
download.setAllowedOverRoaming(true);
download.setDestinationInExternalPublicDir("",imgName);
return download;
}
I need content uri of the downloaded image
Since you are using setDestinationInExternalPublicDir("",imgName);, I would expect your code to crash, as "" is not a valid value for setDestinationInExternalPublicDir().
So, let's instead pretend that you are using:
setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,imgName);
In that case, your file is in:
new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), imgName);
If you truly need a content Uri for that, use FileProvider to serve that file yourself.

Saving Camera Photo To Gallery From Fragment using File Provider

I am following along with Googles Official docs on how to save a picture take with the camera to the gallery.
They want you to create a file using getExternalFilesDir.
String mCurrentPhotoPath;
private File createImageFile() throws IOException {
// Create an image file name
String imageFileName = "JPEG_" + UUID.randomUUID();
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
mCurrentPhotoPath is equal to /storage/emulated/0/Android/data/com.mycompany.myapp/files/Pictures/JPEG_22fda6f2-dad9-4dd9-b327-c1130c8df0eb187766077.jpg
But in the very next section, the most important section, Add the Photo to a Gallery,
They say:
If you saved your photo to the directory provided by
getExternalFilesDir(), the media scanner cannot access the files
because they are private to your app.
Which is the exact method getExternalFilesDir() they used. :-(
So I looked at the documentation on that too. And I don't understand well enough yet to figure out which directory method I need to use. I tried getFilesDir() but it doesn't like the Environment.DIRECTORY_PICTURES.
But they do not offer a way to save to the gallery using their method. Their code snippet doesn't work
private void cameraIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
ex.printStackTrace();
}
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(getActivity().getApplicationContext(), "com.mycompany.myapp.fileprovider", photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_CODE_CAPUTURE_IMAGE);
}
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_CAPUTURE_IMAGE && resultCode == Activity.RESULT_OK) {
galleryAddPic();
}
}
private void galleryAddPic() {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(mCurrentPhotoPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
So my app does NOT save a picture to the gallery. I don't see where it saves it at all.
Anyone know what I did wrong?
When you add files to Android’s filesystem these files are not picked up by the MedaScanner automatically, also Android runs a full media scan only on reboot. The problem is that a full scan is taking long time.
One solution is using the static scanFile() method. If you simply need to know when the files have been added, you could use MediaScannerConnection’s static method scanFile() together with a MediaScannerConnection.OnScanCompletedListener. The static method scanFile() is badly named, as it actually takes an array of paths and thus can be used to add multiple files at once and not just one, but it nevertheless does what we want.
Here’s how to use this method:
MediaScannerConnection.scanFile(
getApplicationContext(),
new String[]{file.getAbsolutePath()},
null,
new OnScanCompletedListener() {
#Override
public void onScanCompleted(String path, Uri uri) {
Log.v("grokkingandroid",
"file " + path + " was scanned seccessfully: " + uri);
}
});
the below information is the parameters for the static scanFile() method.
context : The application context
paths : A String array containing the paths of the files you want to add
mimeTypes : A String array containing the mime types of the files
callback : A MediaScannerConnection.OnScanCompletedListener to be notified when the scan is completed
The OnScanCompletedListener itself must implement the onScanCompleted() method. This method gets the filename and the URI for the MediaStore.Files provider passed in as parameters.
I hope this will help.

Android Studio, Genymotion - sending email with txt file attachment: display in gmail, but doesn't send

Java, Android Studio, Genymotion.
Dear colleagues,
i'm sending email (Intent) with txt attach from android application. Txt file was created by application earlier.
In genymotion in gmail client this attachment (file around 1 Kb) is displaying, but real mail is coming without attachment.
Code snippets:
// file creating
...
final String FILENAME = "file";
...
try {
// отрываем поток для записи
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(openFileOutput(FILENAME, MODE_PRIVATE)));
// writing any data
bw.write ("\n");
...
Log.d(LOG_TAG, "file is created");
bw.close();
}
// sending email with intent
public void sendEmailwithMailClient (){
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
// sending email
emailIntent.setType("plain/text");
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{"example#rambler.ru"});
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getString(R.string.app_name));
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, "Hello!");
File file = new File(getFilesDir(), FILENAME);
// if (!file.exists() || !file.canRead()) {
// return;}
Uri uri = Uri.fromFile(file);
emailIntent.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(emailIntent, "Pick an Email provider"));
}
Do i correctly define Uri for attachment via getFilesDir() and FILENAME?
Why email is loosing attachment during sending? It's issue of Genymotion or in reality i'm not attaching anything to mail and attach displaying in Genymotion is just a fake?
Thank you in advance!
You cannot attach a file from the your apps private storage.
You need to save it to external storage and then attach.
File file = new File(getFilesDir(), FILENAME);
is creating your file in /data/data/package_name/files directory.
Which is not accessible form the other apps.
If you still want to share the file from the apps private storage you need to create your ContentProvider.

How to send file from Android app

Before anything else, I have actually read through several threads regarding sending attachments on Android. That said, I haven't found a solution to my problem.
My app is relatively simple, user types numbers, they get saved to "values.csv" using openFileOutput(filename, Context.MODE_APPEND);.
Now, here's the code I'm using to attach the file to an email and send (I got it from one of the other file threads.)
private void sendEmail(String email) {
File file = getFileStreamPath(filename);
Uri path = Uri.fromFile(file);
Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setType("application/octet-stream");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Test");
String to[] = { email };
intent.putExtra(Intent.EXTRA_EMAIL, to);
intent.putExtra(Intent.EXTRA_TEXT, "Testing...");
intent.putExtra(Intent.EXTRA_STREAM, path);
startActivityForResult(Intent.createChooser(intent, "Send mail..."),
1222);
}
This opens my email client, which does everything right except attach the file, showing me a toast notification saying "file doesn't exist."
Am I missing something? I've already added the permissions for reading and writing to external storage, by the way.
Any and all help would be much appreciated.
EDIT: I can use the File Explorer module in DDMS and navigate to /data/data/com.example.myapp/files/, where my values.csv is located, and copy it to my computer, so the file DOES exist. Must be a problem with my code.
So another way to attach the email except using MODE_WORLD_READABLE is using ContentProvider.
There is a nice tutorial to do this in here
but I will explain it to you shortly too and then you can read that tutorial after that.
So first you need to create a ContentProvider that provides access to the files from the application’s internal cache.
public class CachedFileProvider extends ContentProvider {
public static final String AUTHORITY = "com.yourpackage.gmailattach.provider";
private UriMatcher uriMatcher;
#Override
public boolean onCreate() {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "*", 1);
return true;
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
switch (uriMatcher.match(uri)) {
case 1:// If it returns 1 - then it matches the Uri defined in onCreate
String fileLocation = AppCore.context().getCacheDir() + File.separator + uri.getLastPathSegment();
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
default:// Otherwise unrecognised 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) { return null; }
}
and then write a temporary file
File tempDir = getContext().getCacheDir();
File tempFile = File.createTempFile("values", ".csv", tempDir);
fout = new FileOutputStream(tempFile);
fout.write(bytes);
fout.close();
And then pass it to the email Intent
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" +
CachedFileProvider.AUTHORITY + "/" + tempFile.getName()));
and don't forget to add this to the manifest
<provider android:name="CachedFileProvider" android:grantUriPermissions="true"
android:authorities="yourpackage.provider"></provider>
I see the answers and of course the question.
You asked how you can send a file from android, but in your code example you are talking about sending by email.
Those are two different things at all...
For sending by email you already have answers so I will not enter it at all.
For sending files from android is something else:
you can create a socket connection to your server and send it. you have of course to write both client and server side.
An example can be found here : Sending file over socket...
you can use an ftp server and upload it to there. you can use any server.
example : It's using apache ftp client library (open source)
you can use a http request and make it a post (this method is preferred only for small files)
example : Sending file with POST over HTTP
you can also use dropbox api or amazon s3 sdk's so you don't care about any connections issues and retries and so on and at the end you have a link to the file and pass over the link.
a. DropBox API : documentation
b. Amazon S3 SDK API : documentation
c. Google Drive API : documentation
The advantages in working with Google Drive.
Is working excelent on Android
Authentication is much easier.
The "used space" is on user account.
regards

Categories

Resources