I am currently making an app which works with images. I need to implement functionality where the user picks a file stored on the SD card. Once they pick the picture (using the Android gallery), the the file-location of the image will be sent to another Activity, where other work will be done upon it.
I have seen similar posts here on SO, but none to answer my question specifically. Basically this is the code I am doing when the user clicks the "Load a Picture" button:
// Create a new Intent to open the picture selector:
Intent loadPicture = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
// To start it, run the startActivityForResult() method:
startActivityForResult(loadPicture, SELECT_IMAGE);
From that code, I then have a onActivityResult() method to listen to the call-back:
// If the user tried to select an image:
if(requestCode == SELECT_IMAGE)
{
// Check if the user actually selected an image:
if(resultCode == Activity.RESULT_OK)
{
// This gets the URI of the image the user selected:
Uri selectedImage = data.getData();
// Create a new Intent to send to the next Activity:
Intent i = new Intent(currentActivty.this, nextActivity.class);
// ----------------- Problem Area -----------------
// I would like to send the filename to the Intent object, and send it over.
// However, the selectedImage.toString() method will return a
// "content://" string instead of a file location. How do I get a file
// location from that URI object?
i.putExtra("PICTURE_LOCATION", selectedImage.toString());
// Start the activity outlined with the Intent above:
startActivity(i);
As the code above states, the uri.toString() will return a content:// string instead of the file location of the selected picture. How do I obtain the file location?
Note: Another possible solution is to send over the content:// string and convert that into a Bitmap (which is what happens in the next Activity). However, I don't know how to do that.
I have found the answer to my own question. After doing some more searching, I finally stumbled upon a post here on SO which asks the same question here: android get real path by Uri.getPath().
Unfortunately, the answer has a broken link. After some Google searching, I found the correct link to the site here: http://www.androidsnippets.org/snippets/130/ (I have verified that this code does indeed work.)
However, I decided to take a different route. Since my next Activity is using an ImageView to display the picture, I am instead going to use the Uri content string for all methods that link to the next Activity.
In the next Activity, I am using the ImageView.setImageUri() method.
Here is the code I am doing in the next Activity to display the picture from the content:// string:
// Get the content string from the previous Activity:
picLocation = getIntent().getStringExtra("PICTURE_LOCATION");
// Instantiate the ImageView object:
ImageView imageViewer = (ImageView)findViewById(R.id.ImageViewer);
// Convert the Uri string into a usable Uri:
Uri temp = Uri.parse(picLocation);
imageViewer.setImageURI(temp);
I hope that this question and answer will be helpful to future Android developers.
Here's another answer that I hope someone finds useful:
You can do this for any content in the MediaStore. In my app, I have to get the path from URIs and get the URI from paths. The former:
/**
* Gets the corresponding path to a file from the given content:// URI
* #param selectedVideoUri The content:// URI to find the file path from
* #param contentResolver The content resolver to use to perform the query.
* #return the file path as a string
*/
private String getFilePathFromContentUri(Uri selectedVideoUri,
ContentResolver contentResolver) {
String filePath;
String[] filePathColumn = {MediaColumns.DATA};
Cursor cursor = contentResolver.query(selectedVideoUri, filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
filePath = cursor.getString(columnIndex);
cursor.close();
return filePath;
}
The latter (which I do for videos, but can also be used for Audio or Files or other types of stored content by substituting MediaStore.Audio (etc) for MediaStore.Video:
/**
* Gets the MediaStore video ID of a given file on external storage
* #param filePath The path (on external storage) of the file to resolve the ID of
* #param contentResolver The content resolver to use to perform the query.
* #return the video ID as a long
*/
private long getVideoIdFromFilePath(String filePath,
ContentResolver contentResolver) {
long videoId;
Log.d(TAG,"Loading file " + filePath);
// This returns us content://media/external/videos/media (or something like that)
// I pass in "external" because that's the MediaStore's name for the external
// storage on my device (the other possibility is "internal")
Uri videosUri = MediaStore.Video.Media.getContentUri("external");
Log.d(TAG,"videosUri = " + videosUri.toString());
String[] projection = {MediaStore.Video.VideoColumns._ID};
// TODO This will break if we have no matching item in the MediaStore.
Cursor cursor = contentResolver.query(videosUri, projection, MediaStore.Video.VideoColumns.DATA + " LIKE ?", new String[] { filePath }, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(projection[0]);
videoId = cursor.getLong(columnIndex);
Log.d(TAG,"Video ID is " + videoId);
cursor.close();
return videoId;
}
Basically, the DATA column of MediaStore (or whichever sub-section of it you're querying) stores the file path, so you use what you know to look up the DATA, or you query on the DATA field to select the content you care about.
Related
I'm trying to:
Display a list of contacts
Let the user search through them by typing a query
Limit search results only to a specific Google/Gmail account.
This is how I build the URI for the cursor:
// User is searching for 'jo'
String query = "jo";
Uri uri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(query));
// Restrict the query to contacts from 'example#gmail.com'
Uri.Builder builder = uri.buildUpon();
builder.appendQueryParameter(
ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(ContactsContract.Directory.DEFAULT));
builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, "example#gmail.com");
builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, "com.google");
uri = builder.build();
This is the final URI:
content://com.android.contacts/contacts/filter/jo?directory=0&account_name=example%40gmail.com&account_type=com.google
Currently, this shows search results from all accounts on the phone.
NOTE: If I use Contacts.CONTENT_URI instead of Contacts.CONTENT_FILTER_URI, then specifying the directory/account works as expected, but I can no longer use 'type-to-filter' style search.
The documentation does state:
The most important use case for Directories is search. A Directory
provider is expected to support at least Contacts.CONTENT_FILTER_URI.
Could anyone help point out what I might be doing wrong?
I added your code in Google's example for contact retrieving, and with a couple of changes it worked perfectly with my Google for Work account.
The changes I made were:
remove the line with DIRECTORY_PARAM_KEY, as I didn't find it to make any difference
removed ContactsQuery.SELECTION from the return statement, because that constant prevents "invisible" contacts from being displayed.
The changes were made to ContactsListFragment.java
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// If this is the loader for finding contacts in the Contacts Provider
// (the only one supported)
if (id == ContactsQuery.QUERY_ID) {
Uri contentUri;
// There are two types of searches, one which displays all contacts and
// one which filters contacts by a search query. If mSearchTerm is set
// then a search query has been entered and the latter should be used.
if (mSearchTerm == null) {
// Since there's no search string, use the content URI that searches the entire
// Contacts table
contentUri = ContactsQuery.CONTENT_URI;
} else {
// Since there's a search string, use the special content Uri that searches the
// Contacts table. The URI consists of a base Uri and the search string.
contentUri = Uri.withAppendedPath(ContactsQuery.FILTER_URI, Uri.encode(mSearchTerm));
}
// HERE COMES YOUR CODE (except the DIRECTORY_PARAM_KEY line)
Uri.Builder builder = contentUri.buildUpon();
builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, "example#mycompany.com");
builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, "com.google");
contentUri = builder.build();
// Returns a new CursorLoader for querying the Contacts table. No arguments are used
// for the selection clause. The search string is either encoded onto the content URI,
// or no contacts search string is used. The other search criteria are constants. See
// the ContactsQuery interface.
return new CursorLoader(getActivity(),
contentUri,
ContactsQuery.PROJECTION,
null, // I REMOVED SELECTION HERE
null,
ContactsQuery.SORT_ORDER);
}
Log.e(TAG, "onCreateLoader - incorrect ID provided (" + id + ")");
return null;
}
I want to open Contacts pick activity from my application with search field should be filled programmatically.
Can anyone suggest what URI shoud i use or anything to put in intent's extra?
private static final int PICK_CONTACT_SUBACTIVITY = 2;
private void startContactActivity() {
Uri uri = Uri.parse("content://contacts/people");
// Here in this normally we pass number e.g. Uri.encode("987") but i want to pass name as filter is it possible?
// I have also tried
//uri = Uri.withAppendedPath(android.provider.ContactsContract.Contacts.CONTENT_FILTER_URI, Uri.encode("pra"));
//uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode("pra"));
uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI, Uri.encode("pra"));
Intent intent = new Intent(Intent.ACTION_PICK, uri);
startActivityForResult(intent, PICK_CONTACT_SUBACTIVITY);
}
Can anyone suggest how can i achieve this?
You can follow :
how-to-pick-contact-number-from-phone-book-of-android-in-to-my-application
or
developer.android.com/training/contacts-provider/modify-data
It seems that it is not possible to specify a filter programmatically.
The Android SDK documentation states (in the chapter "Retrieval and modification with intents") that for ACTION_PICK you can only use one of
Contacts.CONTENT_URI, Phone.CONTENT_URI, StructuredPostal.CONTENT_URI, Email.CONTENT_URI. This is indirectly a filter (all contacts, all with phone numbers, all with postal address, or all with email), but it is limited to this.
In my Android application, I want the user to be able to select Albums, Artists and Playlists and then send an intent for their default media player to play them.
I fetch the ID in the standard way:
final Uri exAudioUri = Uri.parse("content://com.google.android.music.MusicContent/playlists");
final String[] proj = { MediaStore.Audio.Playlists._ID, MediaStore.Audio.Playlists.NAME };
final Cursor musiccursor = ctx.getContentResolver().query(exAudioUri, proj, null, null, null);
String name = null;
String id = null;
if (musiccursor != null && musiccursor.getCount() > 0) {
while (musiccursor.moveToNext()) {
name = musiccursor.getString(musiccursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.NAME));
id = musiccursor.getString(musiccursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists._ID));
For Playlists I'm using the following intent:
Intent playMediaIntent = new Intent(Intent.ACTION_VIEW);
playMediaIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
playMediaIntent.setType(MediaStore.Audio.Playlists.CONTENT_TYPE);
playMediaIntent.putExtra("playlist", playlistID);
The Google Play Music app intercepts this and plays the playlist successfully.
Trying to do the same for albums gives me an activity not found exception:
Intent playMediaIntent = new Intent(Intent.ACTION_VIEW);
playMediaIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//playMediaIntent.setType("vnd.android.cursor.dir/track");
playMediaIntent.setType(MediaStore.Audio.Albums.CONTENT_TYPE);
//playMediaIntent.setData(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
playMediaIntent.putExtra("album", albumID);
I know these are undocumented methods and subject to change, but I don't want to have to implement an entire media player of my own, when a user will already have chosen their favourite....
Can anyone suggest any 'standard' code that media players will intercept?
Alternatively, is there a way to get all of the songs from the album (or artist) and send an intent with data containing an array of track URIs to play or queue up??
Been to the depths of Google/Stackoverflow on this one and drawn a blank...
Please help! I thank you in advance.
Android:
I am using following code to insert image into album
According to description this method insert image also creates its thumbnail i don't want thumbnail to be created how can i stop thumbnail creation ?
Problem is I write image in album but when i select image from album it returns the thumbnail image path which is not required.
insert image parameters
Parameters
scr The content resolver to use
source The stream to use for the image
title The name of the image
description The description of the image
MediaStore.Images.Media.insertImage(contentResolver ,file.getAbsolutePath(),
file.getName(), );
I struggled with this as well. To get the actual image data location using the images resource URI:
String dataColumnName = MediaStore.Images.Media.DATA;
Uri mediaUri = //Image's URI;
if (mediaUri != null)
{
String[] proj = { dataColumnName };
Cursor actualimagecursor = managedQuery(mediaUri, proj, null, null, null);
int actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(dataColumnName);
boolean hasValues = actualimagecursor.moveToFirst();
if (hasValues)
{
//this is the file location of the image
String path = actualimagecursor.getString(actual_image_column_index);
}
}
I'm writing an app that removes files that may or may not be listed in any one of the types of media libraries such as music or pictures. While I can use the MediaScannerConnection.scanFile method to add files to the media library there doesn't seem to be any call to notify the service that the file has been removed. Sending it the path of the file that no longer exists doesn't result in the desired behavior either. How should I go about removing items from the library that no longer exist on the Android storage?
I was able to put a method together using bits and pieces from these two questions
What is the String 'volumeName' argument of MediaStore.Audio.Playlists.Members.getContentUri referring to?
How can I refresh MediaStore on Android?
Basically I just run a query on each one of the MediaStore types (Audio, Video and Images) selecting by path and deleting any records I find.
public static void RemoveAllForPaths(String[] paths, Context context)
{
private static final String[] FIELDS = { MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.TITLE };
if(paths == null || paths.length == 0) return;
String select = "";
for(String path : paths)
{
if(!select.equals("")) select += " OR ";
select += MediaStore.MediaColumns.DATA + "=?";
}
Uri uri;
Cursor ca;
uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
ca = context.getContentResolver().query(uri, FIELDS, select, paths, null);
for(ca.moveToFirst(); !ca.isAfterLast(); ca.moveToNext()){
int id = ca.getInt(ca.getColumnIndex(MediaStore.MediaColumns._ID));
uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
context.getContentResolver().delete(uri, null, null);
}
ca.close();
// More of the same just setting the URI to Video and Images
}
I'm not entirely sure how safe this is to do but it's the only solution I've found so far and some initial testing seems to be working. I invite others to submit other answers if anyone has any further information on this approach or a better method for performing this functionality.
Answer of Spencer Ruport is right, but you don't need to query and open a cursor in order to delete.
So for one file that is music file the code is simple like that:
public void DeleteMP3FromMediaStore( Context context, String path )
{
Uri rootUri = MediaStore.Audio.Media.getContentUriForPath( path );
context.getContentResolver().delete( rootUri,
MediaStore.MediaColumns.DATA + "=?", new String[]{ path } );
}
P.S. I wanted to comment answer of Spencer Ruport but don't have enough reputation yet.
Easy as pie: whenever you add a file, let MediaStore ContentProvider knows about it using
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(fileToAddInMediaStore)));
For deletion: just use
getContentResolver().delete(Uri.fromFile(fileToDeleteFromMediaStore), null, null)
The following works well for me. You can delete or add files using this.
MediaScannerConnection.scanFile(
context,
new String[]{fileToDelete, fileToAdd},
null, null);
The available method is to remove the item from library.
This post is detailed expressed how to add into or remove from the Media Library.
http://androidyue.github.io/blog/2014/01/19/scan-media-files-in-android/ Hopes this could help you.