Correct way to choose a file for open - android

I am using this intent to open a chooser to pick up a file:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setDataAndType(Uri.parse(getExternalFilesDir(null).toString()), "*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
Intent chooser = Intent.createChooser(intent, "Choose File");
startActivityForResult(chooser, FILE_SELECT);
I don't fully understand it, but it works. I copied it from some example in the web
The result is an uri, but I find the processing of the uri, also copied from the example, too cumbersome
if(uri != null) {
if("content".equalsIgnoreCase(uri.getScheme())) {
String[] projection = {"_data"};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, projection, null, null, null);
int column_index = cursor.getColumnIndexOrThrow("_data");
if(cursor.moveToFirst()) {
return cursor.getString(column_index);
}
}
catch (Exception e) {}
} else if("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
}
Is this the simplest way to let the user pick a file and get its path?
Why do I need to check if the uri is "content"? I just want to open a text or zipped file the user chose in the intent.
Can I ignore the "content" check, just assume "file" and simply use uri.getPath() ?
Edit I: Is there a way to force choosing with uri scheme "file"?

Yes. This is correct. You are basically starting an intent to get access to the native app. which in this case is the file system. The next would be a part of the onActivityResultData() which would then use the uri on the intent and get the contents of the file that has been selected.

I'm wondering if Android isn't trying to push us away from file paths and towards Uris. Try something like:
InputStream inputStream=contentResolver.openInputStream(uri);
InputStreamReader file = new InputStreamReader(inputStream);
int size = file.read(text,0,mMaxLen);

Related

How to pick those photo stored in cloud from Google Photos?

I know how to pick photo from gallery and it always work.
The code to pick photo from gallery:
Intent intent = new Intent(Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, REQ_GALLERY);
And code to handle result in onActivityResult():
Uri uri = data.getData();
Cursor cursor = getContext().getContentResolver().query(uri, new String[] {
MediaStore.Images.Media.DATA,
}, null, null, null);
if (cursor == null) return false;
if (cursor.moveToFirst()) {
String path = cursor.getString(0);
if (path != null) {
startPhotoEdit(new File(path), output, requestCode);
cursor.close();
return true;
}
}
cursor.close();
This time I pick them from Google Photos and the photo is still stored in cloud and not downloaded to local yet. After I pick one and it starts downloading like that:
photo_downloading
When the download task finished and return to my application, the _data column in cursor which always contains photo path is null.
Can someone help me, please.
I finally find that when photo from cloud is picked, its Uri is different. Like this:
content://com.google.android.apps.photos.contentprovider/1/1/mediakey%3A%2Flocal%253A131b7978-cc48-4206-8561-d18f99421551/ORIGINAL/NONE/2134743478
It contains mediakey instead of content in normal Uri.
So, this might be helpful:
getContentResolver().openInputStream(uri);

Android: Get video from Gallery uri

I'm trying to get a video from the Android Gallery and get the full path, so I can upload the video in an app...
I'm displaying the gallery like this:
Intent intent = new Intent();
intent.setType("video/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
activity.startActivityForResult(Intent.createChooser(intent, context.getResources().getString(R.string.popup_menu_share_video_title)), Const.PICK_VIDEO_REQUEST);
I then extract the uri:
Uri selectedUri = data.getData();
String selectedPath = getRealPathFromURI(this, selectedUri);
And attempt to get the path:
public String getRealPathFromURI(Context context, Uri contentUri) {
Cursor cursor = null;
try {
String[] proj = { MediaStore.Video.Media.DATA };
cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
But I can't seem the get the path...
I can see all the videos if I do this:
public static void dumpVideos(Context context ) {
Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
String[] projection = { MediaStore.Video.Media.DATA };
Cursor c = context.getContentResolver().query(uri, projection, null, null, null);
int vidsCount = 0;
if (c != null) {
vidsCount = c.getCount();
while (c.moveToNext()) {
Log.d("VIDEO", c.getString(0));
}
c.close();
}
Log.d("VIDEO", "Total count of videos: " + vidsCount);
}
So I'm thinking there must be some real simple step, that I'm missing...
I'm trying to get a video from the Android Gallery
First, there is no "Android Gallery". There are many apps that might be considered "gallery" apps. There are thousands of device models, and they will ship with a wide variety of "gallery" apps, in addition to those that the user installs.
Second, ACTION_GET_CONTENT is not somehow magically limited to "gallery" apps. Any app can elect to support ACTION_GET_CONTENT for video/*.
and get the full path
That is impossible. You have no way of knowing what app the user chooses to handle your ACTION_GET_CONTENT request. You have no way of knowing what sort of Uri you will get back. You have no way of knowing where the content identified by that Uri will be stored, as it does not have to be a file on the filesystem that you can access.
so I can upload the video in an app
Use openInputStream() on ContentResolver to get an InputStream on the content identified by the Uri. Then, either:
Use that stream directly with your preferred HTTP client API to upload the content, or
Use that stream to make a local file copy of the content, then use that file with your preferred HTTP client
And attempt to get the path:
At best, that code will work in very limited circumstances:
The app that the user chooses to handle your ACTION_GET_CONTENT request happens to return a content Uri from the MediaStore
That content happens to be on external storage, not removable storage
Since that will not cover all of your scenarios, get rid of that code.
I can see all the videos if I do this
No, you can get paths for some videos:
Not every video accessible via ACTION_GET_CONTENT will be known to the MediaStore
Not every video in the MediaStore will be on external storage, where you might be able to access it via filesystem APIs
you can solve this issue by using ACTION_PICK instead of ACTION_GET_CONTENT. I believe unless you need the functionality of adding pictures/videos from Dropbox etc. it's not worth it.If all you need is to get stuff from the gallery use this code:
if (Build.VERSION.SDK_INT < 19) {
final Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/* video/*");
startActivityForResult(photoPickerIntent, PICK_PHOTO_ACTIVITY_REQUEST_CODE);
} else {
final Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("*/*");
photoPickerIntent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"image/*", "video/*"});
startActivityForResult(photoPickerIntent, PICK_PHOTO_ACTIVITY_REQUEST_CODE);
}
I just tried the code you posted with ACTION_PICK and it works perfectly!

Load file from external storage using chooser

So, I'm trying to load a simple .txt file like this:
private void showFileChooser() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("text/plain");
intent.addCategory(Intent.CATEGORY_OPENABLE);
try {
startActivityForResult(
Intent.createChooser(intent, "Select a File to Upload"),
FILE_SELECT_CODE);
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(this, "Please install a File Manager.",
Toast.LENGTH_SHORT).show();
}
}
And of course, catching the result like this:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case FILE_SELECT_CODE:
if (resultCode == RESULT_OK) {
// Get the Uri of the selected file
Uri uri = data.getData();
It works great on genymotion and on my device if I use a file explorer that I have installed (File explorer, see image abovew), now, If use the chooser directly like this:
It says it cannot find the specified file. (FileNotFoundException)
Now, I've realized that the URIs I get from these two file choosers are different
content://com.android.externalstorage.documents/document/primary%3ADownload%2Ffile.txt <- THIS DOESNT WORK (android built in explorer)
content://media/external/file/44751 <- THIS WORKS (custom explorer)
Does anyone have any idea why I'm getting different URIs for the SAME file.
EDIT:
I tried to use a content resolver to get the file path from the URI like this:
public class Utils {
public static String getRealPathFromURI(Context context, Uri contentUri) {
Cursor cursor = null;
try {
String[] proj = {MediaStore.Files.FileColumns.DATA};
cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(proj[0]);
cursor.moveToFirst();
return cursor.getString(column_index);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}
Still no luck :(
#CommonsWare's asnwer must be the right way of solving this, but I'm not sure how to implement his solution.
I've ended up doing what #Paul Burke recomends on this link:
Android Gallery on KitKat returns different Uri for Intent.ACTION_GET_CONTENT
EDIT
For my particular case, this is how my code ended up. I leave this here for future reference. (I'm using #CommonsWare's explanation) see his answer above.
try {
InputStream inputStream = getContentResolver().openInputStream(uri);
BufferedReader br = null;
if (inputStream != null) {
br = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = br.readLine()) != null) {
text.append(line);
text.append("\n");
}
} else {
subscriber.onError(new InputException("There's something seriously wrong with this file"));
}
} catch (IOException e) {
e.printStackTrace();
subscriber.onError(e);
}
I tried to use a content resolver to get the file path from the URI like this
There is no file path. A Uri does not necessarily point to a file that you can access on your local filesystem, just as the URL to this Web page does not necessarily point to a file that you can access on your local filesystem. The Uri might:
represent a file held in internal storage of the other app
represent a BLOB column value in a database
represent a file held in "the cloud", to be downloaded when requested
etc.
Use a ContentResolver and methods like openInputStream() to read in the content represented by the Uri, just like you would use HttpUrlConnection to read in the content represented by the URL for this Web page. openInputStream() takes the Uri as a parameter and returns an InputStream. You would use that InputStream the same way you would any other InputStream (e.g., FileInputStream, InputStream from HttpUrlConnection). The exact mechanics of that will depend a lot on the underlying data (e.g., read in a string, pass to BitmapFactory to decode a Bitmap).

GetRealPathFromUri always get null result

I'm trying to get real path from Uri(Of selected image from gallery) but This function returns always null value :
//Convert the image URI to the direct file system path of the image file
public String getRealPathFromURI(Uri contentUri) {
Cursor cursor = null;
try {
String[] proj = { MediaStore.Images.Media.DATA };
cursor = getActivity().getContentResolver().query(contentUri, proj, null, null, null);
cursor.moveToFirst();
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
return cursor.getString(column_index);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
That's because the image you're selecting is not physically available on the device.
Images from gallery or other choose can come from different sources that does not provide a physical file, like a cloud storage for example.
Check this code here on how to open the Uri as a InputStream that can be used to create a copy of the file (or just read directly).
Android: Getting a file URI from a content URI?
edit:
I'm doing some extra research on it, apparently it also varies on how you request this Uri.
if you request it like this (which is the current preferred method as per Android guides):
i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(i, REQUEST_STORAGE);
and it opens the KitKat style unified selector, if you choose an image from there, it will always return null. If you open the left-drawer menu, select the gallery (or a file browser), and pick an image from there, then, if it is a local file image, you will have the correct path.
on the other hand, if you request it like this (which is the old method):
i = new Intent(Intent.ACTION_PICK);
i.setType("image/*");
startActivityForResult(i, REQUEST_STORAGE);
it will directly open the Gallery, and again, if it is a local file image, you will have the correct path.
So yeah, I still don't believe there's a way to force local only, but at least you have more information to make an informed decision on your coding.

Resolving the URI from different sources in Android while opening an image

My Android app opens images and makes manipulations on them. My Activity requests an image to be opened as follows:
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,
"Complete action using"),
PICK_FROM_FILE);
On doing this, it launches a dialog with three to four options, like the ES File Explorer: Files, Gallery, etc.
I wish to read this as a Bitmap using BitmapFactory.decodeFile(path).
When I choose the file from ES File Explorer, the path is computed like this:
String path = imageData.getData().getPath();
However, this does not work for choosing an image from the Gallery or Files, where the value of path is /external/images/media/38 for example.
So I put a check, to convert such a path that begins with external using:
if (path.startsWith("/external")) {
path = getFilePathFromUri(imageData.getData());
}
private String getFilePathFromUri(Uri uri) {
Cursor cursor = managedQuery(uri,
new String[] { MediaStore.Images.Media.DATA },
null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
This converts the above path to /mnt/sdcard/dcim/Camera/2011-11-13_15-33-24_304.jpg and now this works for BitmapFactory.decodeFile(path).
I understand that hardcoding such a check (/external) is not a good idea, is there a better, perhaps generic way to resolve the format of the URI returned in the Intent?
I understand that hardcoding such a check (/external) is not a good idea, is there a better, perhaps generic way to resolve the format of the URI returned in the intent?
imageData.getData() returns a Uri. If its path starts with file:// (this is what most file managers will return), then the uri represents a file. Another option is uri represents a content item (this is what Gallery will return), in this case its path starts with content:// and you should call ContentProvider for an actual image file path (as you do in your sample). So I think you could go with something like this:
Uri uri = imageData.getData();
String scheme = uri.getScheme();
if ("file".equals(scheme)) {
// process as a uri that points to a file
} else if ("content".equals(scheme)) {
// process as a uri that points to a content item
}
You can ask MediaStore to create your Bitmap from the Uri:
Bitmap bmp = MediaStore.Images.Media.getBitmap( getContext().getContentResolver(), uri );

Categories

Resources