Not a duplicate to Choosing photo using new Google Photos app is broken
My app requests for file which can be images or videos and we only support some of the file extensions. And since MediaStore.MediaColumns.Data is deprecated, I've to go with the fileDescriptor approach to solve this. But I'm not able to figure out a way to get the extension of selected file.
The URI I get after selecting a video from the Google Photos app looks like this:
content://com.google.android.apps.photos.contentprovider/-1/2/content%3A%2F%2Fmedia%2Fexternal%2Fvideo%2Fmedia%2F25/ORIGINAL/NONE/643532680
When I try to query ContentResolver using _data column it gives an IllegalArgumentException to me, so I used below code to sort of copy the file to a temporary location.
String[] str = uri.toString().split("/");
String fileName = str[str.length - 1];
String filePath = context.getFilesDir().getAbsoluteFile() + File.separator + fileName;
File newFile = new File(filePath);
FileDescriptor fileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r").getFileDescriptor();
FileInputStream fis = new FileInputStream(fileDescriptor);
copyFile(fis, newFile);
return filePath;
So, how should I get the correct mimeType of the selected file?
PS: I tried MimeTypeMap, ContentResolver.getType but none of them works.
EDIT:
Adding more details:
I'm using api level 29. Running the app on a Nexus 6(API 29) emulator. And simply calling ACTION_GET_CONTENT to allow user to select an image/video. Also included the URI I get after selecting the photo which doesn't contain mimetype(which as suggested by CommansWare is normal). When I call ContentResolver.getType on the above URI I get null.
EDIT 2:
Minimal code to reproduce:
Started an intent like this:
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.setType("video/*");
startActivityForResult(i,IMAGE_VIDEO_FETCH);
Then in the onActivityResult, checked the mimetype.
Uri media = intent.getData();
this.getContentResolver().getType(media); /// Here it returns null.
Related
I'm trying to get file from google drive using ACTION_GET_CONTENT.
When I clicked the file, then the results are:
uri.getPath() : "/document/acc=1;doc=encoded=K45Kn19bK1yXpNssYJAhfwJ/4hgc5VmQJk2FWtVNoeM+c1vHUO4H"
uri.toString():"content://com.google.android.apps.docs.storage/document/acc%3D1%3Bdoc%3Dencoded%3DK45Kn19bK1yXpNssYJAhfwJ%2F4hgc5VmQJk2FWtVNoeM%2Bc1vHUO4H"
File recoveryFIle = new File(uri.toString());
file size : 0
How can I get the real file? I select the file from Google Drive.
getfile
Intent recovery = new Intent(Intent.ACTION_GET_CONTENT);
recovery.setType("*/*");
recovery.putExtra(Intent.EXTRA_SUBJECT,"Savelocation_Backup");
startActivityForResult(Intent.createChooser(recovery, "Share File"),3002);
result
Uri uri = data.getData();
Log.d("TAG",uri.getPath()+"");
Log.d("TAG",uri.toString());
File recoveryFIle = new File(uri.toString());
Log.d("TAG",recoveryFIle.length()+"");
No. Impossible.
Stop trying.
Moreover you need not what you call a file.
In order to access a video and share it via our app, I am trying to access it using a FileProvider. The code works fine for all URIs except the one starting with "content://0#media/". In this specific case, the check "vidFile.exists()" returns false. Please let me know how to access files that have such content URI. Appreciate your help.
File vidFile = new File(uri.getPath());
if (vidFile.exists()) //This is returning false for this content URI
{
Uri vidUri = FileProvider.getUriForFile(
context,
context.getString(R.string.file_provider_authority),
vidFile);
}
I used getContentResolver().query and I could get the video attributes. No need to use FileProvider as CommonsWare commented.
As android is increasing its security and changing some stuff now a days to make Android OS more secure to Vulnerabilities, its a good thing to understand new structure for developers to get into it.
I have been away from development from last couple of years.I just noticed that some stuff have been changed e.g file scheme of URI.
Case 1 : :
I am launching camera intent with custom URI mentioned below.
var takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(packageManager) != null) {
// Create the File where the photo should go
var photoFile: File? = null
try {
photoFile = createImageFile()
} catch (ex: IOException) {
// Error occurred while creating the File
}
// Continue only if the File was successfully created
if (photoFile != null) {
val photoURI: Uri = FileProvider.getUriForFile(this,
BuildConfig.APPLICATION_ID + ".fileprovider",
photoFile)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO)
}
}
photoURI is custom URI.
When i took picture and check URI it has file:// in start from where i can get file path and i can do some stuff with it(i am uploading it to server)
Case 2
I am launching simple intent to pick image from gallery
val intent = Intent()
intent.type = "image/*"
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
intent.action = Intent.ACTION_GET_CONTENT
startActivityForResult(Intent.createChooser(intent, "Select Picture"), TYPE_IMAGE)
and now when i pick image and check URI its has content:// in start. And the trouble starts when i try to get file path from it. i have been searching into SOF but none of solutions worked for me.
My main purpose it get file path is that i want to pick file from device and upload it to server.But with content:// scheme i can not make a file from URI and thus can not send this file server.
i just read CommonsWareBlog about scheme system. it cleared few things but i still don't know how can i switch from content:// too file:// in my onActivityResult!
i would like to mention it again that current answers on SOF are not working for me that's why i have to ask this question in details.
I am using 7.0 device emulator
This is just an example of my problem in actual i am picking up PDF and DOCX files and want to upload them on server and after picking files i have URI with content:// scheme and i can not make a file and send to server.
i am not sure what i am missing about schemes.
I hope this question will help many other developers which are new to file system.
pleasure try to explain by code example according to my problem of picking file from intent (if you like)
Since Android 7.0, returning file:// scheme is discouraged (android changelog), since it can leak files from private folders. You can still encounter it, especially when using apps that target below Nougat.
Short answer is when you query for Intent.ACTION_GET_CONTENT You receive content:// uri that you can ContentResolver.openInputStream(Uri) on to obtain the data. This ensures item you picked is read-only, and masks it's real source - it might've been a file, provided remotely or generated by provider all together.
I'm trying to update my code which gets the database file. I'm trying to use a FileProvider instead.
I'm a bit stuck because the original file path is "/storage/emulated/0/Android/data/com.abc/files/abcMobile.db3”
I need to get the URI which is returned by FileProvider.GetUriForFile to include "/storage/emulated/0/“ at the start of it.
Here is my code:
Log.Info ("directory", GetDirectory (dir).ToString());
Log.Info ("filename", filename);
File newFile = new File (GetDirectory (dir).AbsolutePath, "/" + filename);
Android.Net.Uri androidContentUri = FileProvider.GetUriForFile (Application.Context, Application.Context.PackageName + ".fileprovider", newFile);
System.Uri systemContentUri = SystemUri (androidContentUri);
return systemContentUri;
In the code above I have created a file which has a path of "/storage/emulated/0/Android/data/com.abc/files/abcMobile.db3” but when I pass the file to FileProvider.GetUriForFile() It seems to create an Android.Net.URI of {content://com.abc.fileprovider/./Android/data/com.abc/files/abcMobile.db3} and I cast it to a System.Net.URI which becomes {content://com.abc.fileprovider/Android/data/com.abc/files/abcMobile.db3}.
I just need to figure out why it deletes "/storage/emulated/0/“ from the start of the Android.Net.Uri and stop that from happening and then it should work. Any ideas?
I just need to figure out why it deletes "/storage/emulated/0/“ from the start of the Android.Net.Uri and stop that from happening and then it should work. Any ideas?
Because you are generating content uri for your file,which grant temporary permission for access to this file. Return a full path of the file is not considered to be a secure way.
And if you are refer to "Generating the Content URI for a File" section of FileProvider. You will find the following sentence:
To share a file with another app using a content URI, your app has to generate the content URI.
...
As a result of the previous snippet, getUriForFile() returns the content URI content://com.mydomain.fileprovider/my_images/default_image.jpg.
So, it is a expected form of ContentUri that returned by FileProvider.
When opening a file from a file explorer I get a content scheme URI like following:
content://com.asus.filemanager.OpenFileProvider/file/sdcard/backups/apps/testfile.apk
I then make a temporary copy of the file using the content resolver using something like following:
File tempFile = getFileFromContentUri(getContext(), mUri, null);
After the app processed tempFile this file gets deleted. The problem is now that I want to forward mUri to another activity with following code, but I am getting a security exception while doing so, so it seems that the URI can only be used once, is this right?:
private forwardFile(Uri fileUri) {
final Intent installIntent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
Uri uri;
if (ContentResolver.SCHEME_CONTENT.equals(fileUri.getScheme())) {
uri = new Uri.Builder()
.path(fileUri.getPath())
.authority(fileUri.getAuthority())
.scheme(ContentResolver.SCHEME_CONTENT)
.build();
}
installIntent.setData(uri);
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(installIntent);
}
Is there the possibility to reuse the uri or a workaorund that you can see? I'm not seeing a way I can handle this, e.g. I must delete the temp file, but if I forward the copied file instead of the original URI I don't get a callback so I wouldn't know when to delete the copied file.
And here the exception I am getting:
java.lang.SecurityException: Uid 10165 does not have permission to uri 0 # content://com.asus.filemanager.OpenFileProvider/file/sdcard/backups/apps/testfile.apk
Only if the serving app defines a persistent permission and the client calls context.getContentResolver().takePersistableUriPermission() this may work, but I am also not sure.
You got the permission for:
content://com.asus.filemanager.OpenFileProvider/file/sdcard/backups/apps/testfile.apk
No wonder that you got a java.lang.SecurityException: using a completely different (and non existing) file:
content://com.asus.filemanager.OpenFileProvider/file/sdcard/Download/testfile.apk