The way I currently get audio files on my devices is by iterating over a cursor like this:
val Cursor = applicationContext.contentResolver.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
null,
null,
null,
null);
Cursor?.let {
it.moveToFirst();
while(it.moveToNext()){
val title = it.getString(it.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
Log.d("SONGLIST: ", " Song title"+title);
}
}
It does get the audio files, but, includes audio files like system ringtone, alert audio files etc.
I tried doing a query like this:
val Cursor = applicationContext.contentResolver.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
null,
"${MediaStore.Audio.Media.IS_MUSIC} = 1",
null,
null);
Cursor?.let {
it.moveToFirst();
while(it.moveToNext()){
val title = it.getString(it.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
Log.d("SONGLIST: ", " Song title"+title);
}
}
but, using this query I get no results at all, the cursor.getCount becomes 0. So, how do I fetch all audio files in my device using android's MediaStore avoid system sound files?
Related
so I'm pretty new to Android development. I'm trying to have the user select a song from their SD card or internal storage using a file manager and upload the file to a server. Right now, I'm just trying to access the name of the file that the user selected so that I can use it to access the contents of the file later and upload those contents.
I've looked at other posts concerning this and most of them tell you to query the content resolver and then use the cursor to grab the display name, but that does not always return the display name (this is mentioned in Google's guide to the SAF). It has not been returning the full file name, just part of it (the title of the song).
Here's my code that starts the intent:
Intent chooseIntent = new Intent(Intent.ACTION_GET_CONTENT);
chooseIntent.addCategory(Intent.CATEGORY_OPENABLE);
chooseIntent.setType("audio/*");
startActivityForResult(chooseIntent, SELECT_SONG_FILE_REQUEST_CODE);
How I'm getting the display name now:
String fileName = null;
if (uri.getScheme().equals("content")) {
try (Cursor cursor = getContentResolver().query(uri, null, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
fileName = cursor.getString(
cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
Log.i(TAG, "Filename: " + fileName);
cursor.close();
}
}
}
How can I get the full file name rather than just the display name?
You also can use TITLE in Cursor as URI parameter to get the full name of file
use below code it
Cursor cursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,MediaStore.Audio.Media.TITLE, null, null, null);
Or You can use DATA to get Path of the file
Cursor cursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,MediaStore.Audio.Media.DATA, null, null, null);
All You have to do is let the user choose the music and after selecting with the use of DATA you have full path of song and with TITLE you have full name of file .
Next just upload it.
There are several things that you can do to improve the performance of the query
You are getting all the columns which can be painfully slow.
You can write code like the one below and then check what field you want to use. You will get all the data for all the audio files installed in the device. You can walk through this code in the debugger to see the data that you need.
{
Uri objUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
ContentResolver cr = context.getContentResolver();
String[] entityColumns = {MediaStore.Audio.AudioColumns.DATA,
MediaStore.Audio.AudioColumns.DISPLAY_NAME,
MediaStore.Audio.AudioColumns.TITLE};
cursor = cr.query(
objUri,
entityColumns,
null,
null,
null);
if (cursor != null && cursor.moveToFirst()) {
do {
String filePath = cursor.getString(cursor.getColumnIndex(entityCoumns[0]));
String fileName = cursor.getString(cursor.getColumnIndex(entityColumns[1]));
String fileTitle =
cursor.getString(cursor.getColumnIndex(entityColumns[2]));
} while (cursor.moveToNext());
cursor.close();
}
}
Using this method of audio file retrieval from Android's external storage
Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, null);
can I actually find a resonable way to fetch a genre of the given song? MediaStore class seems to provide everything else - from song's title to its composer info - except for the genre field. Should I use MediaMetadataRetriever then? If so, how drastically can creating a MediaMetadataRetriever instance for every song on a device reduce app's performance?
Maybe there are some better ways to retrieve all audio files from both external and internal storages in android?
As mentioned at Developer's Site,
You can fetch the Genres of the Audio file using MediaStore.Audio.Genres
Sample Code :
private static String[] genresProj = {
MediaStore.Audio.Genres.NAME,
MediaStore.Audio.Genres._ID
};
int idIndex = cursor
.getColumnIndexOrThrow(MediaStore.Audio.Media._ID);
while (cursor.moveToNext()){
int id = Integer.parseInt(mediaCursor.getString(idIndex));
Uri uri = MediaStore.Audio.Genres.getContentUriForAudioId("external", id );
genresCursor = context.getContentResolver().query(uri,
genresProj , null, null, null);
int genreIndex = genresCursor.getColumnIndexOrThrow(MediaStore.Audio.Genres.NAME);
while (genresCursor.moveToNext()) {
Log.d(TAG, "Genre = " +genresCursor.getString(genreIndex));
}
}
}
To fetch other details of the Audio file, please check here .
I'm using the following code to get all audio files (actually their ids so I can play them later with MediaPlayer) from a directory on my android device:
ContentResolver cr = context.getContentResolver();
Uri audioUri = MediaStore.Audio.Media.getContentUriForPath(dir.getPath()); //dir is a File object representing the dir I'm looking in
Cursor audioCursor = cr.query(audioUri, null, null, null, null);
if (audioCursor != null && audioCursor.moveToFirst()) {
int idColumn = audioCursor.getColumnIndex(MediaStore.Audio.Media._ID);
List<Long> fileIds = new ArrayList<>();
do {
long id = audioCursor.getLong(idColumn);
fileIds.add(id);
} while (audioCursor.moveToNext());
return fileIds;
} else {
return null;
}
For some reason however, it returns an array with 2904 ids for any dir I give it. I'm guessing there are 2904 audio files on my device (when I use Uri audioUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI it also returns 2904 ids). So it seems I'm using getContentUriForPath incorrectly, however the android reference doesn't give any information.
How should this work?
Some more info about what I'm trying to do (if it makes any difference in your answer): I'm still working on the code, trying out different things. Ultimately I want to play the files with MediaPlayer. I only need the audio files in that folder directly, ignoring any subfolders. They should also be ordered by filename.
You can't use getContentUriForPath with a folder, there isn't any content registered at this path. In this case getContentUriForPath return the standard EXTERNAL_CONTENT_URI, for this reason you get all the ids available.
This return all the files contained in a particular folder (e.g. /storage/emulated/0/Music/), ignoring any subfolders and ordered by filename:
Cursor audioCursor = context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
null,
MediaStore.Audio.Media.DATA + " LIKE ? AND " + MediaStore.Audio.Media.DATA + " NOT LIKE ?",
new String[]{path + "%", path + "%/%"},
MediaStore.Audio.Media.DISPLAY_NAME + " ASC");
if (audioCursor != null && audioCursor.moveToFirst()) {
int idColumn = audioCursor.getColumnIndex(MediaStore.Audio.Media._ID);
List<Long> fileIds = new ArrayList<>();
do {
long id = audioCursor.getLong(idColumn);
fileIds.add(id);
} while (audioCursor.moveToNext());
return fileIds;
} else {
return null;
}
I use MediaStore to get all video files from Android device. Then I delete some of these videos. Followed I use MediaStore again, and I get all deleted files.
Why MediaStore returns files that are no longer are on the device?
Delete File:
File file = new File(filePath);
file.delete();
Get all video files from device:
public static List<String> getVideoFiles(Context context) {
String[] projection = { MediaStore.Video.Media.DATA };
Cursor cursor = context.getContentResolver().query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, null,
null, null);
List<String> videoList = new ArrayList<String>();
while (cursor.moveToNext()) {
videoList.add(cursor.getString(0));
}
Log.i(Constants.LOG_TAG, "get video files, load: " + videoList.size() + " "
+ videoList.toString());
return videoList;
}
MediaStore updates the list of media is not in real time. It needed time to test the relevance of its database. Try to make a call MediaStore after some time.
Or report manually about updating content.
I have videoplayer app with filebrowser listing all videos on SD card
Code inspired by i want get audio files in sd card
Using ContentResolver, works as expected, but it does not update if the files on card change. I do not mean automatically, but after view/app restart. Not even reinstalling the application helped, still shows the same files. The deleted video file is not visible via PC nor it is possible to play it (This video cannot be played (translation)).
I dumped the data and the problem is not in view caching or elsewhere. I do not implement any caching of my own and failed to find anything on the matter. Thank you
Code:
// acquisition
String[] projection = {
MediaStore.Video.Media._ID,
MediaStore.Video.Media.DISPLAY_NAME,
MediaStore.Video.Media.DURATION,
MediaStore.Video.Media.DATA
};
ContentResolver resolver = getActivity().getContentResolver();
Cursor videoCursor = resolver.query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
null
);
// extraction
while(cursor.moveToNext()) {
cursorIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
filepath = cursor.getString(cursorIndex);
cursorIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME);
filename = cursor.getString(cursorIndex);
cursorIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION);
duration = cursor.getString(cursorIndex);
result[ index++ ] = new VideoFileMetadata(filename, duration, filepath);
}
Edit 1 [14-03-2013]:
I tried adding number + " = " + number to ORDER or WHERE clause to act as a potential query caching buster, but it had no effect (although it's possible it was removed by an optimizer as a useless clause). This time I had reinstalled the application from a different machine using different certificate, but the query result remained the same, listing currently non-existing files.
You should first call cursor.moveToFirst() .
So, your cursor iteration loop should look like
if (cursor.moveToFirst()) {
do {
// cursorIndex = cursor.getColumnIndexOrThrow, etc...
} while (cursor.moveToNext());
}