Perform slow task on lots of data in RecyclerView - android

I am building a music player that gets the BPM ID3v2 tag from all mp3 files on a device and allows sorting and filtering by BPM. To get the tag, I am using the mp3agic library. It turns out that this tag fetching takes some time for each song, and performing the fetch when adding each song to my list of songs renders the player without function for a while. I am not sure if I should use an AsyncTask on the entire list when it is done building or an AsyncTask for each item to retrieve its BPM and insert it into the song or even using some other, faster method of getting the BPM value. Can anyone offer any guidance?
Here is my code to get the BPM:
public int getBpmFromId(long id) {
int bpm = -1;
Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, Long.toString(id));
try {
Mp3File file = new Mp3File(getRealPathFromURI(getApplicationContext(), uri));
if(file.hasId3v2Tag()) {
ID3v2 id3v2Tag = file.getId3v2Tag();
bpm = id3v2Tag.getBPM();
Log.d("MP3AGIC", "Got BPM for track: " + id + ": " + bpm);
}
} catch (Exception e) {
e.printStackTrace();
}
return bpm;
}
and here is the code I am using to build my list of songs:
public void getSongList() {
ContentResolver musicResolver = getContentResolver();
Uri musicUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String selectionMimeType = MediaStore.Files.FileColumns.MIME_TYPE + "=?";
String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0 AND " + selectionMimeType;
String sortOrder = null;
String[] projection = null;
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("mp3");
String[] selectionArgsMp3 = new String[] { mimeType };
Cursor musicCursor = musicResolver.query(musicUri, projection, selection, selectionArgsMp3, sortOrder);
if(musicCursor != null && musicCursor.moveToFirst()) {
int idColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media._ID);
int titleColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
int artistColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);
do {
long id = musicCursor.getLong(idColumn);
String title = musicCursor.getString(titleColumn);
String artist = musicCursor.getString(artistColumn);
//bpm processing
int bpm = getBpmFromId(id);
songList.add(new Song(id, title, artist));
} while (musicCursor.moveToNext());
}
if(musicCursor != null)
musicCursor.close();
}
EDIT: It seems that what might be taking so long is getting the file path from the Uri, which is done in this method:
public String getRealPathFromURI(Context context, Uri contentUri) {
Cursor cursor = null;
try {
String[] proj = { MediaStore.Images.Media.DATA };
cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
I'm not sure how, or if I can, optimize this.

The decision to use one asynctask for the entire list or use the list as a queue for many asynctasks depends on your desire for concurrency.
If you plan to parallelize the tag-getting, then use multiple asynctasks (this would be good if the operation involves a lot of disk IO rather than CPU time).
Otherwise if you plan to execute them in sequence, then you should use only one asynctask. There is no need to create, then destroy tens/hundreds of background threads. This is inefficient.

Related

Android 8.1 lost responsiveness

When I launch my application after upgrading from android 7 to 8.1 I noticed that it lost a lot of responsiveness.
When I'm trying to get songs from device memory I get a lot of errors like:
E/SEC_DRM_PLUGIN_Omafl: OmaPlugin::onOpenDecryptSession(fd)::Drm2IsDrmFileByExtFd::file is NOT DRM by extension
That works much slower than on the Android 7.
Code:
public ArrayList<Song> getSongList() {
MediaMetadataRetriever metaRetriver = new MediaMetadataRetriever();
String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0";
ArrayList<Song> songList = new ArrayList<>();
ContentResolver musicResolver = context.getContentResolver();
Uri musicUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor musicCursor = musicResolver.query(musicUri, null, selection, null, null);
if (musicCursor != null && musicCursor.moveToFirst()) {
int titleColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
int idColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media._ID);
int artistColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);
int durationColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media.DURATION);
int column_index = musicCursor.getColumnIndex(MediaStore.Audio.Media.DATA);
do {
long id = musicCursor.getLong(idColumn);
String path = musicCursor.getString(column_index);
String title = musicCursor.getString(titleColumn);
String artist = musicCursor.getString(artistColumn);
String duration = musicCursor.getString(durationColumn);
try {
metaRetriver.setDataSource(path);
songList.add(new Song(id, title, artist, path, duration));
}catch (Exception exc){}
} while (musicCursor.moveToNext());
}
return songList;
}
I had to delete the one line of code that was causing the error when trying to get every single song from memory:
mediaMetadataRetriver.setDataSource(pathOfSong);
Hope it will help you.

How to get songs and other media from an Album ID?

I want to get songs and other MEDIA details from Album Id. All I have is Album Id and I tried many solutions but none of them succeed.
My code snippet:
ContentResolver contentResolver = getContentResolver();
Uri mediaUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, albumId);
Log.wtf("SKJDBKJ", mediaUri.toString());
Cursor mediaCursor = contentResolver.query(mediaUri, null, null, null, null);
// if the cursor is null.
if(mediaCursor != null && mediaCursor.moveToFirst())
{
Log.wtf("DSJK", "entered cursor");
//get Columns
int titleColumn = mediaCursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM);
int idColumn = mediaCursor.getColumnIndex(MediaStore.Audio.Media._ID);
int artistColumn = mediaCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);
// Store the title, id and artist name in Song Array list.
do
{
long thisId = mediaCursor.getLong(idColumn);
String thisTitle = mediaCursor.getString(titleColumn);
String thisArtist = mediaCursor.getString(artistColumn);
Log.wtf("Title", thisTitle);
// Add the info to our array.
songArrayList.add(new Song(thisId, thisTitle, thisArtist));
}
while (mediaCursor.moveToNext());
// For best practices, close the cursor after use.
mediaCursor.close();
}
Log for mediaUri returns path to current album, e.g. : content://media/external/audio/media/41.
Someone tell me how do I do it?
I have done it by myself.You can write simple sqlite query for 'selection' string on contentResolver.query .This example can show you how to get songs by album Id. I think this is best way.
ArrayList<Song> songs = new ArrayList<>();
String selection = "is_music != 0";
if (albumId > 0) {
selection = selection + " and album_id = " + albumId;
}
String[] projection = {
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.ALBUM_ID
};
final String sortOrder = MediaStore.Audio.AudioColumns.TITLE + " COLLATE LOCALIZED ASC";
Cursor cursor = null;
try {
Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
cursor = getActivity().getContentResolver().query(uri, projection, selection, null, sortOrder);
if (cursor != null) {
cursor.moveToFirst();
int position = 1;
while (!cursor.isAfterLast()) {
Song song = new Song();
song.setTitle(cursor.getString(0));
song.setDuration(cursor.getLong(4));
song.setArtist(cursor.getString(1));
song.setPath(cursor.getString(2));
song.setPosition(position);
song.setAlbumId(cursor.getLong(6));
cursor.moveToNext();
}
}
} catch (Exception e) {
Log.e("Media", e.toString());
} finally {
if (cursor != null) {
cursor.close();
}
}
I have figured it out myself after a LOT of trial and errors. I don't know if it's the best and safest way to do so, but as far as it's working I am happy.
I changed my code a bit and compared Album IDs. Here's the snippet:
ContentResolver contentResolver = getContentResolver();
Uri mediaUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Log.wtf("SKJDBKJ", mediaUri.toString());
Cursor mediaCursor = contentResolver.query(mediaUri, null, null, null, null);
// if the cursor is null.
if(mediaCursor != null && mediaCursor.moveToFirst())
{
Log.wtf("DSJK", "entered cursor");
//get Columns
int titleColumn = mediaCursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
int idColumn = mediaCursor.getColumnIndex(MediaStore.Audio.Media._ID);
int artistColumn = mediaCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);
int albumId = mediaCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID);
// Store the title, id and artist name in Song Array list.
do
{
long thisId = mediaCursor.getLong(idColumn);
long thisalbumId = mediaCursor.getLong(albumId);
String thisTitle = mediaCursor.getString(titleColumn);
String thisArtist = mediaCursor.getString(artistColumn);
// Add the info to our array.
if(this.albumId == thisalbumId)
{
Log.wtf("SAME2SAME", String.valueOf(thisalbumId));
Log.wtf("SAME2SAME", String.valueOf(this.albumId));
songArrayList.add(new Song(thisId, thisTitle, thisArtist));
}
}
while (mediaCursor.moveToNext());
// For best practices, close the cursor after use.
mediaCursor.close();
}
I changed:
Albums to Media in MediaStore.Audio.Audio.xxx.
Got the album Id of Media.
Compared that to album Id I receive from bundle extras.
Only add those songs in the arrayList.
I guess this'll be the way for Artists too.
when you put the cursor.movenext function in to the while loop, the first line of
MediaStore database will be passed without saving the data because the function move the cursor and then check so its better to make a value instead of putting the movenext function into the while.
try {
Projection = new String[]{
MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ALBUM
, MediaStore.Audio.Media.ARTIST,MediaStore.Audio.Media.DATA, MediaStore.Audio.Media.SIZE};
Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, Projection, null, null, null);
cursor.moveToFirst();
for (int i = 0; i < 5; i++) {
columns[i] = cursor.getColumnIndex(Projection[i]);
}
Music tempMusic = new Music();
while (next) {
tempMusic.setName(cursor.getString(columns[0]));
tempMusic.setAlbum(cursor.getString(columns[1]));
tempMusic.setArtist(cursor.getString(columns[2]));
tempMusic.setUri(cursor.getString(columns[3]));
tempMusic.setSize(cursor.getDouble(columns[4]));
if(!cursor.moveToNext())
{
next=false;
}
musicss.add(tempMusic);
tempMusic = new Music();
}
public Bitmap getAlbumArt(Long albumId) {
Bitmap albumArt = null;
try {
final Uri AlbumArtUri = Uri.parse("content://media/external/audio/albumart");
Uri uri = ContentUris.withAppendedId(AlbumArtUri, albumId);
ParcelFileDescriptor pfd = GlobalSongList.getInstance().getContentResolver().openFileDescriptor(uri, "r");
if (pfd != null) {
FileDescriptor fd = pfd.getFileDescriptor();
albumArt = BitmapFactory.decodeFileDescriptor(fd);
}
} catch (Exception e) {
}
return albumArt;
}
ALBUM_ID is a field in the MediaStore and you can query it in the same way you query for title,artist,etc.
public Long getAlbumId(String id)
{
Cursor musicCursor;
String[] mProjection = {MediaStore.Audio.Media._ID, MediaStore.Audio.Media.ALBUM_ID};
String[] mArgs = {id};
musicCursor = musicResolver.query(musicUri, mProjection, MediaStore.Audio.Media._ID + " = ?", mArgs, null);
musicCursor.moveToFirst();
Long albumId=musicCursor.getLong(musicCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
return albumId;
}
This is how I did.
Works perfect.
String selection= MediaStore.Audio.Media.IS_MUSIC+"!=0";
String[] projection={
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Albums.ALBUM_ID,
MediaStore.Audio.Media.DURATION
};
Cursor cursor=getActivity().managedQuery(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,projection,selection,null,"title asc");
List<String> data=new ArrayList<>();
List<String> songs=new ArrayList<>();
List<String> artists=new ArrayList<>();
List<String> albums=new ArrayList<>();
List<String> album_id=new ArrayList<>();
List<String> durations=new ArrayList<>();
cursor.moveToFirst();
while (cursor.moveToNext()){
if(cursor.getString(5).equals(mAlbumId)) {
data.add(cursor.getString(1));
songs.add(cursor.getString(2));
artists.add(cursor.getString(3));
albums.add(cursor.getString(4));
album_id.add(mAlbumId);
durations.add(cursor.getString(6));
}
}

Android: How to list mp3 albums

I want to try develop simple android music player.
I have this method below to list album title, but it gives duplicate albums list.
public void getAlbumList() {
//query external audio
Activity a=getActivity();
ContentResolver musicResolver = a.getContentResolver();
Uri musicUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
//String[] projection = null;
//String sortOrder = null;
String selectionMimeType = MediaStore.Files.FileColumns.MIME_TYPE + "=?";
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("mp3");
String[] selectionArgsMp3 = new String[]{ mimeType };
Cursor musicCursor = musicResolver.query(musicUri, null, selectionMimeType, selectionArgsMp3, null);
//iterate over results if valid
if(musicCursor!=null && musicCursor.moveToFirst()){
//get columns
int atitleColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media.ALBUM);
int idColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media.ALBUM_ID);
//int bitmap = musicCursor.getColumnIndex
// (android.provider.MediaStore.Audio.Albums.ALBUM_ART);
//add songs to list
while (musicCursor.moveToNext()){
long thisId = musicCursor.getLong(idColumn);
String thisaTitle = musicCursor.getString(atitleColumn);
albumList.add(new Album(thisId, thisaTitle));
}
}
}
I have tried How to use MediaStore query to get Artists without duplicates? but it doesn't work.
Thanks in advance.
add this to your Album class
#Override
public boolean equals(Object obj) {
if (obj instanceof Album) {
Album album = (Album) obj;
//here we compare if the 2 album objects are same or not (edit code as needed)
return (album.getaTitle().equals(this.aTitle) && album.getId == this.Id);
} else {
return false;
}
}
#Override
public int hashCode() {
int hashcode = 0;
hashcode = this.Id*20;
hashcode += this.aTitle.hashCode();
//here we generate the album object hashcode using Id and artistname
return hashcode;
}
then instead of adding your objects to a list, use this :
HashSet<Album> albumList = new HashSet<Album>();
albumList.add(new Album(thisId, thisaTitle));
//use simple for each loop to iterate
for(Album album:albumList){
System.out.println(album);
}
A hashset aromatically handles duplications

How to display music name by playlist name Android programming

There are some similar questions have been posted here, I have used those suggestions and tried it in different ways, but still no results for getting out, my codes is like this:
private Cursor getPlaylists(String playlistName) {
Cursor cursor = null;
String[] projection1 = { MediaStore.Audio.Playlists._ID,
MediaStore.Audio.Playlists.NAME };
cursor = this.managedQuery(
MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, projection1,
MediaStore.Audio.Playlists.NAME + "=\"" + playlistName + "\"", null,
null); // I need to put "" for the string, otherwise sqlite errors for no such table
startManagingCursor(cursor);
cursor.moveToFirst();
String playlist_id = cursor.getString(0);
Long playlist_id2 = cursor.getLong(0);
if (playlist_id2 > 0) {
String[] projection = {
MediaStore.Audio.Playlists.Members.AUDIO_ID,
MediaStore.Audio.Playlists.Members.ARTIST,
MediaStore.Audio.Playlists.Members.TITLE,
MediaStore.Audio.Playlists.Members._ID
};
cursor = getContentResolver().query(MediaStore.Audio.Playlists.Members.getContentUri("external",playlist_id2),
projection,
null,
null,
null);
}
return cursor;
But after these codes, how can I exactly get music list names? does then store in projection, but my projection variable are always no music list information inside. If I need to iterate this cursor again to get music list names, how can I do it, I have tried this way.. but it won't work.
// startManagingCursor(cursor);
for (boolean hasItem = cursor.moveToFirst(); hasItem; hasItem = cursor
.moveToNext()) {
String musicName = cursor.getString(cursor
.getColumnIndex(MediaStore.Audio.Playlists.Members.TITLE));
Log.i(LOGGING_TAG, musicName);
}
it looks like hasItem is null as always. I am stuck on this for couple of hours, maybe that is a stupid question but any suggestions would be graceful.
If I understand you correctly, you want to list the tracks on a given playlist.
This is how I do it. Once you have your cursor, you just loop around it by getting the values you need.
see examples below :
public Cursor getPlaylistTracks(Context context, Long playlist_id) {
Uri newuri = MediaStore.Audio.Playlists.Members.getContentUri("external",playlist_id);
ContentResolver resolver = context.getContentResolver();
String _id = MediaStore.Audio.Playlists.Members._ID;
String audio_id = MediaStore.Audio.Playlists.Members.AUDIO_ID;
String artist = MediaStore.Audio.Playlists.Members.ARTIST;
String album = MediaStore.Audio.Playlists.Members.ALBUM;
String title = MediaStore.Audio.Playlists.Members.TITLE;
String duration = MediaStore.Audio.Playlists.Members.DURATION;
String location = MediaStore.Audio.Playlists.Members.DATA;
String composer = MediaStore.Audio.Playlists.Members.COMPOSER;
String playorder = MediaStore.Audio.Playlists.Members.PLAY_ORDER;
String date_modified = MediaStore.Audio.Playlists.Members.DATE_MODIFIED;
String[] columns = { _id, audio_id, artist, album, title, duration,
location, date_modified, playorder, composer };
Cursor cursor = resolver.query(newuri, columns, null, null, null);
return cursor;
}
if (cursor!= null){
cursor.moveToFirst();
int artistColumn = cursor.getColumnIndex(MediaStore.Audio.Playlists.Members.ARTIST);
int durationColumn = cursor.getColumnIndex(MediaStore.Audio.Playlists.Members.DURATION);
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext())
{
artist = cursor.getString(artistColumn);
duration = duration + cursor.getLong(durationColumn);
// do something else
}
cursor.close();
}

Getting all MP3 files on the external card

What i'm trying to achieve is getting all the mp3 files'URL and Title in an arraylist to return it. All from the folder: /ext_card/Music/ and all subfolders.
the options i've explored all seem to get stuck, and all the answers i've looked up on the internet point to mediastore, but i have no idea how to use it
And don't give me the RTFM bullshit, because the explanations i've found for mediastore is retarded at best.
SO can anyone please help me out or give me an example of a simple working mediastore query for all mp3 files?
Use this it will help you to get all MP3 Song from SD CARD
private static ArrayList<genericSongClass> songs = null;
public void BindAllSongs() {
/** Making custom drawable */
String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0";
final String[] projection = new String[] {
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.DATA};
final String sortOrder = MediaStore.Audio.AudioColumns.TITLE
+ " COLLATE LOCALIZED ASC";
try {
// the uri of the table that we want to query
Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
// query the db
cursor = getBaseContext().getContentResolver().query(uri,
projection, selection, null, sortOrder);
if (cursor != null) {
songs = new ArrayList<genericSongClass>(cursor.getCount());
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
GSC = new genericSongClass();
GSC.songTitle = cursor.getString(0);
GSC.songArtist = cursor.getString(1);
GSC.songData = cursor.getString(2);
songs.add(GSC);
cursor.moveToNext();
}
}
} catch (Exception ex) {
} finally {
if (cursor != null) {
cursor.close();
}
}
}
public class genericSongClass {
String songTitle = "";
String songArtist = "";
String songData = "";
String isChecked = "false";
}
Here is a recursive way to do such thing,
getMp3s(Arraylist<File> list, File dir){
File[] files = dir.listFiles();
for(File file : files){
if(file.isDirectory()){
getMp3s(list, file);
}else{
//add mp3s to list here
}
}
}

Categories

Resources