I am making an application which requires me to list all the images available on the SD-Card of the phone.
I tried querying the ContentResolver way i.e.
Cursor image = getContentResolver().query(Images.Media.EXTERNAL_CONTENT_URI, new String[]{Images.Media._ID,Images.Media.DATA,Images.Media.DISPLAY_NAME}, null, null, null);
but without any result.
Is there any way i can get the list or if that's not possible then is there any possible intent (e.g. PICK) by which I can allow the user to select a file and then access the path of the file the user selected??
//where contextObject is your activity
ContentResolver cr = contextObject.getContentResolver();
String[] columns = new String[] {
ImageColumns._ID,
ImageColumns.TITLE,
ImageColumns.DATA,
ImageColumns.MIME_TYPE,
ImageColumns.SIZE };
cur = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
columns, null, null, null);
My code is pretty much like yours (except broken down) and it works. You don't need to go about asking the Gallery Intent for stuff, it should work. My two guesses are:
1) Make sure your USB storage is not mounted, if it is you'll see no external images.
2) Maybe a permissions issue? Try adding GLOBAL_SEARCH permission to see if that helps at all.
You could use the gallery activity to select images, something like this:
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select Picture"),SELECT_IMAGE);
in the callback for the activity the file uri will be in the intent parameter
Related
I made an app. It has 2 options for uploading photo:
1) by taking a photo using camera
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, 1);
2) by picking from gallery
Intent intent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, 2);
My problem is getting file path after these intents in onActivityResult.
Is there any methods to get those paths for new File(path) , that also takes care of sdk level changes? For example till KitKat is 1 type of file system , after KitKat is other type.
My problem is getting file path after these intents in onActivityResult.
There will be no file path for the ACTION_IMAGE_CAPTURE approach, because you did not provide EXTRA_OUTPUT. If you do provide EXTRA_OUTPUT, then you already know what the file path is.
There is no file path for ACTION_PICK, insofar as there is no requirement that what the user picks be in a file that you have access to. For example, it could be an image on removable storage. Use a ContentResolver and methods like openInputStream() to get the content represented by the Uri that you are given.
If you can able to get path from camera then for Intent.ACTION_PICK you can directly get uri of image using data.getData() and then you can get file path using this method(as suggested in the link provided by Gennadii Saprykin
public String getRealPathFromURI(Uri uri) {
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
cursor.moveToFirst();
int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
return cursor.getString(idx); }
I am trying to allow the user to share an image to other apps on the device. The image is inside the files/ subdirectory of my app's internal storage area. It works just fine with Gmail, but Facebook and Twitter both crash when responding to my intent.
EDIT: Google+ also works fine.
Here are the relevant sections of code.
In Application.xml
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="org.iforce2d.myapp.MyActivity"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
xml/filepaths.xml
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="shared" path="shared"/>
</paths>
Here is the sharing code in my activity:
File imagePath = new File(getContext().getFilesDir(), "shared");
File newFile = new File(imagePath, "snapshot.jpg");
Uri contentUri = FileProvider.getUriForFile(getContext(),
"org.iforce2d.myapp.MyActivity", newFile);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("image/jpeg");
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
List<ResolveInfo> resInfos =
getPackageManager().queryIntentActivities(shareIntent,
PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo info : resInfos) {
getContext().grantUriPermission(info.activityInfo.packageName,
contentUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
startActivity(Intent.createChooser(shareIntent, "Share image..."));
The value of contentUri when logged is:
content://org.iforce2d.myapp.MyActivity/shared/snapshot.jpg
I have only checked this with Gmail, Facebook and Twitter as the receiving apps, but the results have been very consistent over a wide range of OS versions (from 2.2.1 to 4.4.3) and 7 devices include a Kindle.
Gmail works great. Image thumbnail appears in mail composition, and successfully attaches to mail when sent.
Twitter and Facebook both crash, as below.
Here are the stack traces from logcat showing the problem these two apps are having, it appears to be the same problem for both of them (this is taken from 4.4.3, but the errors were practically the same all the way back to 2.2.1 albeit with slightly different wording of the error message):
Caused by:
java.lang.IllegalStateException: Couldn't read row 0, col 0 from CursorWindow.
Make sure the Cursor is initialized correctly before accessing data from it.
at android.database.CursorWindow.nativeGetString(Native Method)
at android.database.CursorWindow.getString(CursorWindow.java:434)
at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:51)
at android.database.CursorWrapper.getString(CursorWrapper.java:114)
at com.twitter.library.media.util.f.a(Twttr:95)
...
Caused by:
java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow.
Make sure the Cursor is initialized correctly before accessing data from it.
at android.database.CursorWindow.nativeGetString(Native Method)
at android.database.CursorWindow.getString(CursorWindow.java:434)
at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:51)
at android.database.CursorWrapper.getString(CursorWrapper.java:114)
at com.facebook.photos.base.media.MediaItemFactory.b(MediaItemFactory.java:233)
...
Given that sharing images on Facebook and Twitter is something that millions of people do all day long, I'm pretty shocked that it's so hard to implement :/
Can anybody spot something I'm doing wrong here?
Twitter (wrongly) assumes that there will be a MediaStore.MediaColumns.DATA column. Starting in KitKat the MediaStore returns null, so luckily, Twitter gracefully handles nulls, and does the right thing.
public class FileProvider extends android.support.v4.content.FileProvider {
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Cursor source = super.query(uri, projection, selection, selectionArgs, sortOrder);
String[] columnNames = source.getColumnNames();
String[] newColumnNames = columnNamesWithData(columnNames);
MatrixCursor cursor = new MatrixCursor(newColumnNames, source.getCount());
source.moveToPosition(-1);
while (source.moveToNext()) {
MatrixCursor.RowBuilder row = cursor.newRow();
for (int i = 0; i < columnNames.length; i++) {
row.add(source.getString(i));
}
}
return cursor;
}
private String[] columnNamesWithData(String[] columnNames) {
for (String columnName : columnNames)
if (MediaStore.MediaColumns.DATA.equals(columnName))
return columnNames;
String[] newColumnNames = Arrays.copyOf(columnNames, columnNames.length + 1);
newColumnNames[columnNames.length] = MediaStore.MediaColumns.DATA;
return newColumnNames;
}
}
EDIT -- The workaround described in this answer works, but the solution provided by Stefan is more elegant. It should be the accepted answer.
Also, as of January 2015, the Facebook app no longer has this bug (and now works with the standard FileProvider) but Twitter still does. Hopefully it will go completely away in the future, and neither of these will be necessary.
While the FileProvider object looks wonderful in theory, it doesn't seem to work when sharing to many third-party applications (while Google ones, like G+, Gmail, &c work perfectly). Either it doesn't expose sufficient information, or those applications just assume you're sharing files, and thus crash.
It's really frustrating! :/
The only reliable way I've found to share image files is to copy them to the external storage directory (you can create a .nomedia subdirectory to avoid them appearing in the Gallery app).
For example:
Bitmap bitmapToShare = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
File pictureStorage = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File noMedia = new File(pictureStorage, ".nomedia");
if (!noMedia.exists())
noMedia.mkdirs();
File file = new File(noMedia, "shared_image.png");
saveBitmapAsFile(bitmapToShare, file);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
shareIntent.setType("image/png");
startActivity(shareIntent);
Addendum: of course, you should also check getExternalStorageState() before trying to write/copy the file there.
I need help in accessing the attachment received in an email.
I have setup intent filter for my activity and on clicking the attachment in the gmail app, my activity is launched with intent containing following Uri.
content://gmail-ls/xxxx#gmail.com/messages/2407/attachments/0.1/BEST/false
I have tried following query and filename returned is null.
String[] projection = {MediaStore.MediaColumns.DATA};
Cursor c = context.getContentResolver().query(uri, projection, null, null, null);
if (c.moveToFirst()) {
fileName = c.getString(0);
}
Anyone, please let me know how can I access the file received in the attachment ?
Thanks.
Anyone, please let me know how can I access the file received in the attachment ?
There isn't necessarily a file at all, let alone one that you have direct access to. Gmail, in particular, had better be storing its attachments in a place where other apps cannot access them directly.
To use the attachment, use the Uri with a ContentResolver to get an InputStream on it.
I have an app that lets user choose music file from sdcard. To launch chooser intent I am using
Intent intent = new Intent();
intent.setType("audio/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(
Intent.createChooser(intent, "Complete action using"), 0);
But I am getting different absolute paths depending on what method user choose. If user uses ES File Explorer then I get /sdcard/Music/song.mp3 but if user uses some music app then I am getting /storage/sdcard0/Music/song.mp3. Its very confusing and my app requires me to know one final base path.
Environment.getExternalStorageDirectory() returns /storage/sdcard0/. Any help would be appreciated.
Note: in both cases
Uri uri = Uri.parse(new File(soundPath).getAbsolutePath());
mPlayer = MediaPlayer.create(this, uri);
works fine.
/sdcard is usually symlinked to the real(*) path in the filesystem (the /storage one) to stay compatible with early Android devices where /sdcard was the default.
Using Environment.getExternalStorageDirectory() is the method you should use. There is no guarantee that /sdcard or /storage/sdcard0 will work if you hardcode that path. Device manufacturers can use pretty much any filesystem layout they want but they will make sure that Environment knows the correct path.
(*) Starting Honeycomb & the "unified storage model" the real path is actually something like /data/media which is loop-mounted via fuse to /storage/sdcard0 (or whatever Environment tells you) to enforce the correct permission required for the WRITE_EXTERNAL_STORAGE permission.
Impromptu Q&A Session With Android Engineer Dan Morrill Brings To Light Reasons Behind Galaxy Nexus' Lack Of USB Mass Storage - second question has some details.
getAbsolutePath() is by the way not working in the same way as it does for desktop Java apps. On Android, the current working directory / root is always /. So getAbsolutePath() would always return the same as getPath() does and will at most prefix the path with a /.
Uris from File can be easily constructed via
Uri uri = Uri.fromFile(new File(soundPath));
that way you get a correct Uri using the file:// scheme which is not the case (and could lead to errors) if you use Uri.parse("/some/path")
Getting full path and file name:
Private String getPath(Uri u) {
String[] projection = { MediaStore.Audio.Media.DATA };
Cursor cursor = managedQuery(u, projection, null, null, null);
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
use this on onActivityResult
String mpath = getPath(data.getData());
I am simply trying to get the path of an image that the user selects and then convert it into a bitmap. The problem is, only some of the images in the gallery work when selected (by "work" I mean they are found to be a file that exists), while the others claim the file does not exist (even though the image is showing up in the gallery?). Even more strange is that this doesn't seem to be consistent, an image that was at one point considered to "exist" now claims to be nonexistent. My code is below:
-----The Intent-----
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent, GALLERY_ACTIVITY);
-----onActivityForResult-----
Uri uri = intent.getData();
String [] proj={MediaStore.Images.Media.DATA};
Cursor cursor = managedQuery(uri,proj,null,null,null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
BitmapFactory.Options opts = new BitmapFactory.Options();<br/>
opts.inSampleSize = 2;<br/>
Bitmap b = BitmapFactory.decodeFile(cursor.getString(column_index),opts);
Any ideas on this will be greatly appreciated, thank you!
Matt.
Some images in gallery were loaded from external sources (such as Picasa), thus were not stored locally, causing local filepath reading failure. You can distinguish them by reading your uri value. I could not find a fix for this, perhaps this bug http://code.google.com/p/android/issues/detail?id=21234 can lure out a solution soon.