How to get the file path from a URI that points to a PDF document?
Right now my code opens up the default downloads view and it only shows me the PDFs I downloaded. I choose a PDF file and I get this:
content://com.android.providers.downloads.documents/document/1171
I want to get this:
/storage/emulated/0/Download/ch22Databases.pdf
My question is how do I do this in Android?
My code:
public void PDF() {
PDF = (Button) findViewById(R.id.FindPDFBtn);//Finds the button in design and put it into a button variable.
PDF.setOnClickListener(//Listens for a button click.
new View.OnClickListener() {//Creates a new click listener.
@Override
public void onClick(View v) {//does what ever code is in here when the button is clicked
Intent intent = new Intent();
intent.setType("application/pdf");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select a PDF "), SELECT_PDF);
}
}
);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//PDF
if (resultCode == RESULT_OK) {
if (requestCode == SELECT_PDF) {
Uri selectedUri_PDF = data.getData();
SelectedPDF = getPDFPath(selectedUri_PDF);
}
}
}
public String getPDFPath(Uri uri) {
String[] projection = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
Add this snippet below in your getPDFPath
method:
public String getPDFPath(Uri uri){
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
String[] projection = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(contentUri, projection, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
In your case, this code is specifically for documents from DownloadProvider
, for further implementation check Paul Burke's answer. I personally use his aFileChooser library to avoid this kind of problems.
My question is how do i do this in Android?
You don't. ACTION_GET_CONTENT
has little to do with files.
If you absolutely need a file, use a file picker library, not ACTION_GET_CONTENT
.
If you want to use ACTION_GET_CONTENT
, stop trying to get a filesystem path the content. Use ContentResolver
and openInputStream()
to read in the contents of the content, if the Uri
has a file
, content
, or android.resource
scheme. Use an HTTP client API if the Uri
has an http
or https
scheme.
Below are two solutions
1) You can use below code. It can handle any type of file and from any folder.
private String getPath(final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
if(isKitKat) {
// MediaStore (and general)
return getForApi19(uri);
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
@TargetApi(19)
private String getForApi19(Uri uri) {
Log.e(tag, "+++ API 19 URI :: " + uri);
if (DocumentsContract.isDocumentUri(this, uri)) {
Log.e(tag, "+++ Document URI");
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
Log.e(tag, "+++ External Document URI");
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
Log.e(tag, "+++ Primary External Document URI");
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
Log.e(tag, "+++ Downloads External Document URI");
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
Log.e(tag, "+++ Media Document URI");
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
Log.e(tag, "+++ Image Media Document URI");
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
Log.e(tag, "+++ Video Media Document URI");
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
Log.e(tag, "+++ Audio Media Document URI");
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {
split[1]
};
return getDataColumn(contentUri, selection, selectionArgs);
}
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
Log.e(tag, "+++ No DOCUMENT URI :: CONTENT ");
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
Log.e(tag, "+++ No DOCUMENT URI :: FILE ");
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
*/
public String getDataColumn(Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
This is the best solution I had to browse files.
Hope it'll help.
Another Way
2) Another solution I found is
Add dependancy in build.gradle
of Module: app
compile 'in.gauriinfotech:commons:1.0.8'
Then in your code use
String fullPath = Commons.getPath(uri, context);
Make sure you have added below permission in Manifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />