I want to show an images gallery. The problem is during the loading of them.
At the beginning, I was loading the images with :
images = new File("/sdcard/DCIM/Camera/");
if(images.listFiles().length==0)
//no images, do some other stuff...
else
//put images in the gallery and do some stuff
This code works with the emulator (Android 2.2 - API Level 8) and with a Samsung Galaxy S2 (Android 2.2.1). But I ask some friends to test this code with their own phones (HTC, LG...).
With an HTC Desire, there is a problem during this loading. The code is going into "//no images" but they have images in the album (for example from Camera...) My friend said to me that :
album is stored in the internal storage and not in the SD Card with HTC.
So i've tried to check others directory, like this :
//Get the internal content
images = new File(MediaStore.Images.Media.INTERNAL_CONTENT_URI.getPath());
if (!images.isDirectory()) {
//Get the external content
images = new File(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.getPath());
}
if (!images.isDirectory()) {
//Get the SD Card content
images = new File("/sdcard/DCIM/Camera/");
}
But still not working :( That is not the good directory :/
So, my question is : how can i get the good path for images with all android phone ? is their an Object which is able to return the path of the album ?
Thanks :)
(sorry for mistakes)
I did some checking and it looks like you are trying to access the MediaStore incorrectly. It should be accessed via a cursor and not directly as a file.
This is because as mentioned at http://developer.android.com/guide/topics/providers/content-providers.html
Content providers expose their data as a simple table on a database model, where each row is a record and each column is data of a particular type and meaning.
So basically you can't read the URI as a file. It is a table of data.
Here's an example from that same page on how to access it.
import android.provider.Contacts.People;
import android.content.ContentUris;
import android.net.Uri;
import android.database.Cursor;
// Use the ContentUris method to produce the base URI for the contact with _ID == 23.
Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI, 23);
// Alternatively, use the Uri method to produce the base URI.
// It takes a string rather than an integer.
Uri myPerson = Uri.withAppendedPath(People.CONTENT_URI, "23");
// Then query for this specific record:
Cursor cur = managedQuery(myPerson, null, null, null, null);
Also, here's another Stackoverflow question that has some examples that shows how to use the cursor.
How to query Android MediaStore Content Provider, avoiding orphaned images?
Finally, based on the links above I would just use the mediastore object as a cursor on all devices and that will solve the problem you are having with different directories.
Hope this helps,
George
Related
I am trying to get the metadata for the video files stored on my app's user's phone. I can get the file name, id, date taken and so on. However, latitude and longitude data always returns as 0.0. I have been referring to this:
developer.android.com/reference/android/provider/MediaStore.Video.VideoColumns.html
Yes, I am already enabling use location in my settings. I have a very similar function to this for images which works fine.
public void getLocalVideoFiles(Context context) {
ContentResolver videoResolver = context.getContentResolver();
Uri videoUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
String test = getRealPathFromURI(context, videoUri);
Cursor videoCursor = videoResolver.query(videoUri, null, null, null, null);
if(videoCursor!=null && videoCursor.moveToFirst()){
//get columns
int latColumn = videoCursor.getColumnIndex
(MediaStore.Video.Media.LATITUDE);
int lonColumn = videoCursor.getColumnIndex
(MediaStore.Video.Media.LONGITUDE);
do {
String thisLat = Double.toString(videoCursor.getDouble(latColumn));
String thisLon = Double.toString(videoCursor.getDouble(lonColumn));
Log.d("video Latitude",thisLat);
Log.d("video Longitude",thisLon);
}
while (videoCursor.moveToNext());
}
return localClips;
}
The approach described here: Geotagging a captured video yields similar results (null value in the METADATA_KEY_LOCATION column).
So, my question is: does the built-in Android video tool record location data when creating videos? It seems like the answer is no, but I don't understand why there are columns for the location data if this is the case. If that is not the case, how can I access the video location data? I need the location of video files that have already been taken.
Thanks in advance!
Well i've just tested your assumption that google does not keep location data while recording video and it's incorrect.
For example: using my nexus 5 with version 5.1 i was able to get a geotag on a video i just took. you can try it by yourself and if your phone is rooted, just browse the MediaStore external DB (com.android.providers.media) using some SQLITE viewer
but let's say that google does not keep GeoTag. there are number of reasons why they would keep such a column:
To support other video libraries that do want to keep geo taggging
To allow users who are implementing Video recorder a way to save current location ( using FusedLocation or something similar). a way of doing it is just updating the relevant row in the DB for example:
getContentResolver().update(MediaStore.Video.Media.EXTERNAL_CONTENT_URI.buildUpon().appendPath("2152").build(), cv, null, null);
to support previous versions that did support tagging
i know android database keep two kinds of contact photo,
[[[ The photo may be stored in up to two ways - the default "photo" is a thumbnail-sized image stored directly in the data row, while the "display photo", if present, is a larger version stored as a file. ]]]
the small one is 96*96 and the large is 480 *480
now the questions:
i still can't find the large one while the small one is "data15"in the database,someone please show me how to get the large one?
i need to place the photo on the first screen of launcher , so i need a photo at least 800*480,there must be declare constant for the photo size....
PS: I tried:
intent.putExtra("outputX", 96);
intent.putExtra("outputY", 96);
but when i opened the database , it became 96*57 ....confused.... some help changed it?
好人一声平安 (that's chinese-good boy,good luck)
thanks ^_^
I'm not too sure what you mean by storing the photo twice?
Yes, a lot of the devices out there will generate thumbnails, but thats more due to the fact that displaying thumbail images is a regular use case.
Have you tried fetching the user ID and using that to query the contacts data table?
This may be a long winded approach but, (pseudocode)
Fetch contact id using the URI RawContacts.CONTENT_URI
Fetch Photo id using the URI Contacts.CONTENT_URI
Fetch Photo using the URI Data.CONTENT_URI, the projection you want here is Photo.PHOTO (this will give you a blob)
Google should provide easy to read examples of these actions :)
To get the larger image you can use the following code (provided you have the contactID):
public static InputStream getLargeContactPhoto(Long contactId, Context context) {
Uri contactUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId);
Uri displayPhotoUri = Uri.withAppendedPath(contactUri, ContactsContract.Contacts.Photo.DISPLAY_PHOTO);
try {
AssetFileDescriptor fd = context.getContentResolver().openAssetFileDescriptor(displayPhotoUri, "r");
return fd.createInputStream();
} catch (Exception e) {
return null;
}
}
I'm querying all images on the Android device as such:
string[] columns = { MediaStore.Images.Media.InterfaceConsts.Data,
MediaStore.Images.Media.InterfaceConsts.Id };
string orderBy = MediaStore.Images.Media.InterfaceConsts.Id;
var imagecursor = ManagedQuery(MediaStore.Images.Media.ExternalContentUri, columns, null, null, orderBy);
for (int i = 0; i < this.Count; i++) {
imagecursor.MoveToPosition(i);
Paths[i]= imagecursor.GetString(dataColumnIndex);
Console.WriteLine(Paths[i]);
Console.WriteLine(System.IO.File.Exists(Paths[i]));
}
The problem is that the output shows that some files don't exist. Here's a sample output:
/storage/sdcard0/Download/On-Yom-Kippur-Jews-choose-different-shoes-VSETQJ6-x-large.jpg
False
/storage/sdcard0/Download/397277_10151250943161341_876027377_n.jpg
False
/storage/sdcard0/Download/Roxy_Cottontail_&_Melo-X_Present..._Some_Bunny_Love's_You.jpg
False
/storage/sdcard0/Download/album-The-Rolling-Stones-Some-Girls.jpg
True
/storage/sdcard0/Download/some-people-ust-dont-appreciate-fashion[1].jpg
True
/storage/sdcard0/Download/express.gif
True
...
/storage/sdcard0/Download/some-joys-are-expressed-better-in-silence.JPG
False
How is this possible? I downloaded these images myself from the internet! They should exist in disk.
You appear to be using a LoaderMananger/ManagedQuery to query the Media Content Provider in Android. A Content Provider is just a way to access a particular SQLite database from different apps. If you use the Android provided Media Content Provider you'll have to update it 'manually' by using MediaScannerConnection to add in the new files that you've placed, as the 'service' may or may not update internally while your app is running.
Here are some related SO questions:
Scan Android SD card for new files
and Trigger mediascanner on specific path (folder), how to? but I don't recommend the answer of globally scanning your SD card.
If this is a Samsung device, all those paths may be incorrect !
// Put this in your code and then log the 'external' string to see
String external = Environment.getExternalStorageDirectory();
if you get say: /storage/sdcard0/
Then Samsung themselves say you have to do this lame append (I claim they broke the API :)
external = external + "/external_sd/";
I am not certain this applies to downloads or not, but I suspect it affects ALL similar sub-paths.
http://developer.samsung.com/forum/board/thread/view.do?boardName=GeneralB&messageId=162934&messageNumber=1381&startId=zzzzz~&searchType=TITLE&searchText=sdcard
[EDIT:] Caveat Emptor ! Even this does not work on some Samsung devices. ARRRRGGGGGG
I'm trying to expose a .png file located in my application's /data directory through a ContentProvider but instead of reaching the openFile method query is being called. Now I only ever have a single image which I need to expose for sharing to other applications, how can I setup my Intent to goto openFile instead of query?
Intent shareImageIntent = new Intent(Intent.ACTION_SEND);
shareImageIntent.setType("image/*");
shareImageIntent.putExtra(Intent.EXTRA_STREAM, imageUri);
startActivity(Intent.createChooser(shareImageIntent, "Share image"));
Where the Uri looks like
content://my.package.contentprovider/fileName
Or alternatively do I need to create a database for this and return a cursor?
UPDATE
So this appears to be working on everything except the SMS app (which is what I decided to test first) I would like to support sharing to it however.
Here's the relevant stack trace:
Caused by: java.lang.IllegalArgumentException: Query on
content://mypackage.myprovider/someImage.png returns null result. at
com.android.mms.ui.UriImage.initFromContentUri(UriImage.java:104) at
com.android.mms.ui.UriImage.(UriImage.java:63) at
com.android.mms.model.ImageModel.initModelFromUri(ImageModel.java:83)
at com.android.mms.model.ImageModel.(ImageModel.java:65) at
com.android.mms.data.WorkingMessage.changeMedia(WorkingMessage.java:481)
at
com.android.mms.data.WorkingMessage.setAttachment(WorkingMessage.java:375)
...
So the SMS app is performing a query instead of reading directly from openFile, which every other app on my phone seems to do (including other Google apps)
Does anyone know what I need to return here to fullfil the query appropriately? I'm going to go AOSP digging now.
After digging through the source code of the SMS (MMS really) app this is what I came up with.
Inside UriImage.initFromContentUri the application makes the query code and assumes there are 2 returned columns in the Cursor
} else {
filePath = c.getString(c.getColumnIndexOrThrow(Images.Media.DATA));
mContentType = c.getString(c.getColumnIndexOrThrow(Images.Media.MIME_TYPE));
}
So inorder for your ContentProvider to work with the MMS app, you need to return a Cursor in query that only has one row and the two columns (Images.Media.DATA & Images.Media.MIME_TYPE) with the appropriate data. The MMS will then make the call to openFile to actually retrieve the image.
An easier way to share a image resource is to save it to external storage (SD-card) and then do:
Uri imageUri = Uri.fromFile(pathToFile);
Update:
Try using
Uri imageUri = Uri.parse("android.resource://com.package.yourapp/" +imageResID);
Update2
Try saving file to Media Store and then sending it:
String url = Media.insertImage(context.getContentResolver(), imageFile.getAbsolutePath(), imageFile.getName(), imageFile.getName());
Uri imageUri = Uri.parse(url);
Final Update using ContentProvider and Cursor:
Your ContentProvider must implement query(..) method and it must return a Cursor. See the source code of UrlImage.initFromContentUri(..) (which is internally used by MMS app) to see how cursor is called. Take a look at the MatrixCursor if it fits the bill.
If your content provider is already working you can access to a ParcelFileDescriptor via the method openFileDescriptor in the content provider.
A quick, and dirty, example for this:
ParcelFileDescriptor descriptor = mContext.getContentResolver().openFileDescriptor(IMGURI, "r");
Bitmap bmp = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor());
Cheers!
I have an app that allows the user to take and save a new picture using the Intent ACTION_IMAGE_CAPTURE. After the user accepts the newly taken picture, I create a ContentValue object to set the picture information, and then I insert the picture into the MediaStore and send a broadcast so that the user can see the photo when opening a picture viewer app such as gallery:
ContentValues newImage = new ContentValues(3);
newImage.put(Media.DISPLAY_NAME, mPicName);
newImage.put(Media.MIME_TYPE, "image/png");
newImage.put(MediaStore.Images.Media.DATA, path);
mPictureUri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, newImage);
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, mPictureUri));
All of this code works fine. The photo is correctly saved, and I can view the photo using gallery. The problem is that when I view the photo in gallery and select "Details" it shows the photo name as "null." And yes, I know for a fact that the variable mPicName above is not null and always has the correct value.
The odd thing is that, when running my app on my Droid running Android 2.1, when I choose to copy files to/from the phone and my computer, I open up the Droid on my Windows computer, and when I go into my app's created folder for photos and view the new photo, the name is correct. Then, when I disable copying files from the phone/computer and go back into gallery to view the file on my Droid, the name is suddenly correct and is no longer null.
Can someone tell me why the name is null after my code runs, and why the name is correct after viewing the file on my computer rather than on the phone? Is there a way to get the name non-null right when the picture is saved? I'd bet that the name would be correct if I turned my phone off and turned it back on, but I'm not sure how exactly to get force things to work immediately.
Answer is by looking at the sources of MediaStore.
One places is here:
http://devdaily.com/java/jwarehouse/android/core/java/android/provider/MediaStore.java.shtml
Basically they also add a thumbnail representation of the image and instead of
Media.DISPLAY_NAME they use Media.TITLE.
I used that code adapted a little and worked fine.