I use #pskink solution to optimize my query code, reducing consumed time from 3000+ms to 200+ms by using ContentQueryMap.
But I still confused on how to implements JOIN operation on ContentResolver. In my limit experience, I believe consumed time will be reduced to below 100ms by using JOIN. Here is my code. How can I implements JOIN via ContentResolver?
BTW, is any optimization on my code? Thanks!
// scan Music by query table: MediaStore.Audio.AudioColumns .
private void scanMusic() {
Map<String, ContentValues> albumQueryMap = prepareAlbums();
Map<String, ContentValues> artistQueryMap = prepareArtist();
final String[] musicProjection = {
MediaStore.Audio.AudioColumns.DATA,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.ALBUM_ID,
MediaStore.Audio.Media.SIZE,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.DATE_ADDED
};
final String selection = MediaStore.Audio.AudioColumns.IS_MUSIC + " != ? And "
+ MediaStore.Audio.AudioColumns.DURATION + " >= ?";
final String[] selectionArgs = new String[]{"0", "60000"};
Cursor musicCursor = context.getContentResolver().query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
musicProjection,
selection,
selectionArgs,
null
);
if (musicCursor != null) {
while (musicCursor.moveToNext()) {
// scan item music
String musicFilePath = musicCursor.getString(0);
String musicName = musicCursor.getString(1);
String musicArtist = musicCursor.getString(2);
String musicAlbumName = musicCursor.getString(3);
String albumId = musicCursor.getString(4);
String coverPath = albumQueryMap.get(albumId).getAsString(MediaStore.Audio.Albums.ALBUM_ART);
String musicFileSize = Formatter.formatFileSize(MainApplication.getBackgroundContext(), musicCursor.getLong(5));
long musicDuration = musicCursor.getLong(6);
long musicAddDate = musicCursor.getLong(7);
Music itemMusic = new Music(musicFilePath, musicName, musicArtist, musicAlbumName, coverPath, musicDuration, musicFileSize, musicAddDate);
mAllMusicList.add(itemMusic);
}
musicCursor.close();
}
}
// scan Albums by query table: MediaStore.Audio.Albums and cache it.
private Map<String, ContentValues> prepareAlbums() {
final String[] projection = {
MediaStore.Audio.Albums._ID,
MediaStore.Audio.Albums.ALBUM,
MediaStore.Audio.Albums.ALBUM_ART,
MediaStore.Audio.Albums.ARTIST,
MediaStore.Audio.Albums.FIRST_YEAR,
MediaStore.Audio.Albums.LAST_YEAR,
MediaStore.Audio.Albums.NUMBER_OF_SONGS,
};
Cursor cursor = MainApplication.getBackgroundContext().getContentResolver().query(
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
projection,
null,
null,
null);
ContentQueryMap queryMap = new ContentQueryMap(
cursor,
MediaStore.Audio.Albums._ID,
false,
null
);
Map<String, ContentValues> map = queryMap.getRows();
for (String albumId : map.keySet()) {
ContentValues values = map.get(albumId);
String albumName = values.getAsString(MediaStore.Audio.Albums.ALBUM);
String albumArt = values.getAsString(MediaStore.Audio.Albums.ALBUM_ART);
String artist = values.getAsString(MediaStore.Audio.Albums.ARTIST);
String firstYear = values.getAsString(MediaStore.Audio.Albums.FIRST_YEAR);
String lastYear = values.getAsString(MediaStore.Audio.Albums.LAST_YEAR);
int numberOfSongs = values.getAsInteger(MediaStore.Audio.Artists.Albums.NUMBER_OF_SONGS);
Album item = new Album(albumName, albumArt, artist, firstYear, lastYear, numberOfSongs);
mAlbumList.add(item);
}
try {
return map;
} finally {
cursor.close();
queryMap.close();
}
}
// scan Artist by query table:MediaStore.Audio.Artists and cache it.
private Map<String, ContentValues> prepareArtist() {
final String[] projection = {
MediaStore.Audio.Artists._ID,
MediaStore.Audio.Artists.ARTIST,
MediaStore.Audio.Artists.NUMBER_OF_ALBUMS,
MediaStore.Audio.Artists.NUMBER_OF_TRACKS,
};
Cursor cursor = MainApplication.getBackgroundContext().getContentResolver().query(
MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
projection,
null,
null,
null);
ContentQueryMap queryMap = new ContentQueryMap(
cursor,
MediaStore.Audio.Artists._ID,
false,
null
);
Map<String, ContentValues> map = queryMap.getRows();
for (String artistId : map.keySet()) {
ContentValues values = map.get(artistId);
String artist = values.getAsString(MediaStore.Audio.Artists.ARTIST);
int numberOfAlbums = values.getAsInteger(MediaStore.Audio.Artists.NUMBER_OF_ALBUMS);
int numberOfTracks = values.getAsInteger(MediaStore.Audio.Artists.NUMBER_OF_TRACKS);
Artist item = new Artist(artist, numberOfAlbums, numberOfTracks);
mArtistList.add(item);
}
try {
return map;
} finally {
cursor.close();
queryMap.close();
}
}
As #pskink say, just use ContentQueryMap to optimize the query. Why
can ContentQueryMap improve my query code efficiency?
I Wrote Code Like This Before:
private void scanMusic() {
final String[] musicProjection = {
MediaStore.Audio.AudioColumns.DATA,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.ALBUM_ID,
MediaStore.Audio.Media.SIZE,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.DATE_ADDED
};
final String selection = MediaStore.Audio.AudioColumns.IS_MUSIC + " != ? And "
+ MediaStore.Audio.AudioColumns.DURATION + " >= ?";
final String[] selectionArgs = new String[]{"0", "60000"};
Cursor musicCursor = context.getContentResolver().query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
musicProjection,
selection,
selectionArgs,
null
);
if (musicCursor != null) {
while (musicCursor.moveToNext()) {
// scan item music
String musicFilePath = musicCursor.getString(0);
String musicName = musicCursor.getString(1);
String musicArtist = musicCursor.getString(2);
String musicAlbumName = musicCursor.getString(3);
String albumId = musicCursor.getString(4);
// Scan the album form once for each row of the music form
String coverPath = getThumbAlbum(albumId);
String musicFileSize = Formatter.formatFileSize(context, musicCursor.getLong(5));
long musicDuration = musicCursor.getLong(6);
long musicAddDate = musicCursor.getLong(7);
Music itemMusic = new Music(musicFilePath, musicName, musicArtist, musicAlbumName, coverPath, musicDuration, musicFileSize, musicAddDate);
mAllMusicList.add(itemMusic);
}
musicCursor.close();
}
}
private String getThumbAlbum(String albumId) {
ContentResolver resolver = context.getContentResolver();
Uri albumUri = MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
String id = MediaStore.Audio.Albums._ID;
String[] selection = new String[]{MediaStore.Audio.Albums.ALBUM_ART};
String[] selectionArgs = new String[]{albumId};
Cursor cursor = resolver.query(albumUri, selection, id + "=?", selectionArgs, null);
if (cursor != null && cursor.moveToNext()) {
try {
return cursor.getString(0);
} finally {
cursor.close();
}
}
return null;
}
How to optimize my code? The answer is obvious. By caching Albums form query result, we can reduce the query time of Albums form.
By using ContentQueryMap, we can reach the result that we expect。
I Wrote Code Like This After:
private void scanMusic() {
Map<String, ContentValues> albumQueryMap = prepareAlbums();
final String[] musicProjection = {
MediaStore.Audio.AudioColumns.DATA,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.ALBUM_ID,
MediaStore.Audio.Media.SIZE,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.DATE_ADDED
};
final String selection = MediaStore.Audio.AudioColumns.IS_MUSIC + " != ? And "
+ MediaStore.Audio.AudioColumns.DURATION + " >= ?";
final String[] selectionArgs = new String[]{"0", "60000"};
Cursor musicCursor = context.getContentResolver().query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
musicProjection,
selection,
selectionArgs,
null
);
if (musicCursor != null) {
while (musicCursor.moveToNext()) {
// scan item music
String musicFilePath = musicCursor.getString(0);
String musicName = musicCursor.getString(1);
String musicArtist = musicCursor.getString(2);
String musicAlbumName = musicCursor.getString(3);
String albumId = musicCursor.getString(4);
String coverPath = albumQueryMap.get(albumId).getAsString(MediaStore.Audio.Albums.ALBUM_ART);
String musicFileSize = Formatter.formatFileSize(context, musicCursor.getLong(5));
long musicDuration = musicCursor.getLong(6);
long musicAddDate = musicCursor.getLong(7);
Music itemMusic = new Music(musicFilePath, musicName, musicArtist, musicAlbumName, coverPath, musicDuration, musicFileSize, musicAddDate);
mAllMusicList.add(itemMusic);
}
musicCursor.close();
}
}
// Caching the query result of Albums form into a Map, with Which we can get coverPath easily by given key.
private Map<String, ContentValues> prepareAlbums() {
final String[] projection = {
MediaStore.Audio.Albums._ID,
MediaStore.Audio.Albums.ALBUM,
MediaStore.Audio.Albums.ALBUM_ART,
MediaStore.Audio.Albums.ARTIST,
MediaStore.Audio.Albums.FIRST_YEAR,
MediaStore.Audio.Albums.LAST_YEAR,
MediaStore.Audio.Albums.NUMBER_OF_SONGS,
};
Cursor cursor = context.getContentResolver().query(
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
projection,
null,
null,
null
);
ContentQueryMap queryMap = new ContentQueryMap(
cursor,
MediaStore.Audio.Albums._ID,
false,
null
);
try {
return queryMap.getRows();
} finally {
cursor.close();
queryMap.close();
}
}
Before I use ContentQueryMap, the program use 3000+ms on querying with result size == 273. After using ContentQueryMap, the program use just 200+ms, 15x faster, awesome.
Related
I'm trying to import contacts into my app, but struggling with getting the company name. Here's my code:
public List<ContactItem> getContactList(){
ArrayList<ContactItem> contactList = new ArrayList<ContactItem>();
Uri contactUri = ContactsContract.Contacts.CONTENT_URI;
String[] PROJECTION = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.HAS_PHONE_NUMBER,
};
String SELECTION = ContactsContract.Contacts.HAS_PHONE_NUMBER + "='1'";
Cursor contacts = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, PROJECTION, SELECTION, null, null);
if (contacts.getCount() > 0)
{
while(contacts.moveToNext()) {
ContactItem aContact = new ContactItem();
int idFieldColumnIndex = 0;
int nameFieldColumnIndex = 0;
int numberFieldColumnIndex = 0;
int companyFieldColumnIndex = 0;
String contactId = contacts.getString(contacts.getColumnIndex(ContactsContract.Contacts._ID));
nameFieldColumnIndex = contacts.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME);
if (nameFieldColumnIndex > -1)
{
aContact.setName(contacts.getString(nameFieldColumnIndex));
}
// Tried to get a company, but
// this always returns -1
companyFieldColumnIndex = contacts.getColumnIndex(ContactsContract.CommonDataKinds.Organization.COMPANY);
if (companyFieldColumnIndex > -1)
{
Log.d(TAG, "getContactList: starts");
aContact.setCompany(contacts.getString(companyFieldColumnIndex));
}
PROJECTION = new String[] {ContactsContract.CommonDataKinds.Phone.NUMBER};
final Cursor phone = managedQuery(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION, ContactsContract.Data.CONTACT_ID + "=?", new String[]{String.valueOf(contactId)}, null);
if(phone.moveToFirst()) {
while(!phone.isAfterLast())
{
numberFieldColumnIndex = phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
if (numberFieldColumnIndex > -1)
{
aContact.setPhoneNum(phone.getString(numberFieldColumnIndex));
phone.moveToNext();
TelephonyManager mTelephonyMgr;
mTelephonyMgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (!mTelephonyMgr.getLine1Number().contains(aContact.getPhoneNum()))
{
contactList.add(aContact);
}
}
}
}
phone.close();
}
contacts.close();
}
return contactList;
}
I add a comment in my code, where i'm trying to get the contact's company name, but i always get -1. Some contacts have the company name, so something wrong in this part of code. How to get the company properly?
First thing, you are passing Projection which doesn't contains anything related to Company name so of course you won't get it.
Other thing which I am not sure is, you need to pass contact's RawId instead of ID to fetch the Company name. Here's something how you should do it,
String contactId = contacts.getString(contacts.getColumnIndex(ContactsContract.Contacts._ID));
String rawContactId = getRawContactId(contactId);
String companyName = getCompanyName(rawContactId);
& here are the functions you'll need:
private String getRawContactId(String contactId) {
String[] projection = new String[]{ContactsContract.RawContacts._ID};
String selection = ContactsContract.RawContacts.CONTACT_ID + "=?";
String[] selectionArgs = new String[]{contactId};
Cursor c = mContentResolver.query(ContactsContract.RawContacts.CONTENT_URI, projection, selection, selectionArgs, null);
if (c == null) return null;
int rawContactId = -1;
if (c.moveToFirst()) {
rawContactId = c.getInt(c.getColumnIndex(ContactsContract.RawContacts._ID));
}
c.close();
return String.valueOf(rawContactId);
}
and:
private String getCompanyName(String rawContactId) {
try {
String orgWhere = ContactsContract.Data.RAW_CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?";
String[] orgWhereParams = new String[]{rawContactId,
ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE};
Cursor cursor = mContentResolver.query(ContactsContract.Data.CONTENT_URI,
null, orgWhere, orgWhereParams, null);
if (cursor == null) return null;
String name = null;
if (cursor.moveToFirst()) {
name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.COMPANY));
}
cursor.close();
return name;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
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));
}
}
Could anybody help me to make query to Android DB.
I need select all albums in selected folder.
For all songs I make this:
private Cursor allSongs() {
String[] projection = {MediaStore.Audio.Media._ID, // 0
MediaStore.Audio.Media.ARTIST, // 1
MediaStore.Audio.Media.TITLE, // 2
MediaStore.Audio.Media.ALBUM_ID, // 3
MediaStore.Audio.Media.ALBUM, // 4
MediaStore.Audio.Media.DATA, // 5
MediaStore.Audio.Media.DISPLAY_NAME, // 6
MediaStore.Audio.Media.DURATION, //7
MediaStore.Audio.Media.TITLE_KEY //8
};
String selection = android.provider.MediaStore.Audio.Media.DATA + " like ? ";
String MEDIA_PATH = Environment.getExternalStorageDirectory() + "/Music/";
Cursor musicListSDCardCursor = mQuery(
getActivity(),
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
projection, selection, new String[]{"%" + MEDIA_PATH + "%"}, null, 0);
return musicListSDCardCursor;
}
and mQuery code:
public static Cursor mQuery(Context context, Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder, int limit) {
try {
ContentResolver resolver = context.getContentResolver();
if (resolver == null) {
return null;
}
if (limit > 0) {
uri = uri.buildUpon().appendQueryParameter("limit", "" + limit).build();
}
return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
} catch (UnsupportedOperationException ex) {
return null;
}
}
But how make query to MediaStore to get all Albums from this folder /sdcard/Music/ ?
I make it, like:
private Cursor allAlbums() {
Cursor cursor_albums_from_songs = allSongs();
HashSet<String> albumsList = new HashSet<String>();
int count = cursor_albums_from_songs.getCount();
for (int i = 0; i < count; i++) {
cursor_albums_from_songs.moveToPosition(i);
albumsList.add(cursor_albums_from_songs.getString(3));
Log.i("cursor", "" + cursor_albums_from_songs.getString(3));
}
Log.i("allAlbums", "" + albumsList.size());
ArrayList<String> list = new ArrayList<String>(albumsList.size());
list.addAll(albumsList);
int count_ids = list.size();
Cursor[] cursors = new Cursor[count_ids];
for (int i = 0; i < count_ids; i++) {
String selection = MediaStore.Audio.Albums._ID + " = ? ";
String[] selectionArgs = new String[]{list.get(i)};
String[] projection = {MediaStore.Audio.Albums._ID, //0
MediaStore.Audio.Albums.ALBUM, // 1
MediaStore.Audio.Albums.ALBUM_ART, // 2
MediaStore.Audio.Albums.ARTIST, //3
MediaStore.Audio.Albums.FIRST_YEAR, //4
MediaStore.Audio.Albums.NUMBER_OF_SONGS //5
};
Cursor artistListSDCardCursor = mQuery(
mContext,
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
projection, selection, selectionArgs, null, 0);
cursors[i] = artistListSDCardCursor;
}
MergeCursor mMergeCursor = new MergeCursor(cursors);
return mMergeCursor;
}
I am trying to get all contacts in the favourites list of the Android contacts. Currently, I can get all the group ids including the favourite group ID. But it seems that there is no contacts that have the group ID as the favourite group ID.
I'm trying to get All groups id and contacts in each group. After printing two list, I found that the group id of favorite is not in the contact list
ArrayList<String> favGroupId=new ArrayList<String>();
final String[] GROUP_PROJECTION = new String[] {
ContactsContract.Groups._ID, ContactsContract.Groups.TITLE };
Cursor cursor = getContentResolver().query(
ContactsContract.Groups.CONTENT_URI, GROUP_PROJECTION, null,
null, ContactsContract.Groups.TITLE);
while (cursor.moveToNext()) {
String id = cursor.getString(cursor
.getColumnIndex(ContactsContract.Groups._ID));
Log.v("Test",id);
String gTitle = (cursor.getString(cursor
.getColumnIndex(ContactsContract.Groups.TITLE)));
Log.v("Test",gTitle);
if (gTitle.contains("Favorite_")) {
gTitle = "Favorites";
favGroupId.add(id);
}
}
cursor.close();
You can use the STARRED field in the ContactsContract.Contact class. If you change your query to:
Cursor cursor = this.managedQuery(
ContactsContract.Contacts.CONTENT_URI, projection, "starred=?",
new String[] {"1"}, null);
this should return a list of all contacts that appear in the Favorites tab in the default Contacts app on Android.
Complete answer, including intentUriString for opening the contact with an Intent:
Map getFavoriteContacts(){
Map contactMap = new HashMap();
Uri queryUri = ContactsContract.Contacts.CONTENT_URI;
String[] projection = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.STARRED};
String selection =ContactsContract.Contacts.STARRED + "='1'";
Cursor cursor = managedQuery(queryUri, projection, selection, null, null);
while (cursor.moveToNext()) {
String contactID = cursor.getString(cursor
.getColumnIndex(ContactsContract.Contacts._ID));
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.withAppendedPath(
ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactID));
intent.setData(uri);
String intentUriString = intent.toUri(0);
String title = (cursor.getString(
cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)));
contactMap.put(title,intentUriString);
}
cursor.close();
return contactMap;
}
Ty this with Kotlin:
import android.content.Context
import android.provider.ContactsContract
import android.content.Intent
import android.net.Uri
fun getFavoriteContacts(context: Context): Map<*, *> {
lateinit var contactMap : HashMap<String, String>
val queryUri = ContactsContract.Contacts.CONTENT_URI.buildUpon()
.appendQueryParameter(ContactsContract.Contacts.EXTRA_ADDRESS_BOOK_INDEX, "true")
.build()
val projection = arrayOf(
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.STARRED
)
val selection = ContactsContract.Contacts.STARRED + "='1'"
val cursor = context.getContentResolver().query(queryUri,
projection, selection, null, null)
while (cursor.moveToNext()) {
val contactID = cursor.getString(cursor
.getColumnIndex(ContactsContract.Contacts._ID))
val intent = Intent(Intent.ACTION_VIEW)
val uri = Uri.withAppendedPath(
ContactsContract.Contacts.CONTENT_URI, contactID.toString())
intent.data = uri
val intentUriString = intent.toUri(0)
val title = cursor.getString(
cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
contactMap[title] = intentUriString
}
cursor.close()
return contactMap
}
Here is the complete method to get contact details with favorite in Java
ContentResolver contentResolver = context.getContentResolver();
if (contentResolver == null)
return;
String[] fieldListProjection = {
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY,
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER,
ContactsContract.Contacts.HAS_PHONE_NUMBER,
ContactsContract.Contacts.PHOTO_URI
,ContactsContract.Contacts.STARRED
};
String sort = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY + " ASC";
Cursor phones = contentResolver
.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI
, fieldListProjection, null, null, sort);
HashSet<String> normalizedNumbersAlreadyFound = new HashSet<>();
if (phones != null && phones.getCount() > 0) {
while (phones.moveToNext()) {
String normalizedNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
if (Integer.parseInt(phones.getString(phones.getColumnIndex(
ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
if (normalizedNumbersAlreadyFound.add(normalizedNumber)) {
int id = phones.getInt(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID));
String name = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
int fav = phones.getInt(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.STARRED));
boolean isFav;
isFav= fav == 1;
String uri = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.PHOTO_URI));
if(uri!=null){
contactList.add(new FavContact(id,isFav,name,phoneNumber,uri));
}
else{
contactList.add(new FavContact(id,isFav,name,phoneNumber));
}
}
}
}
phones.close();
}
The playlist names can be found by a query on MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI
and then look at the MediaStore.Audio.PlaylistsColumns.NAME column.
There is a data column too, MediaStore.Audio.PlaylistsColumns._DATA, but it is returning null.
The list of songs (MediaStore.Audio.Media.EXTERNAL_CONTENT_URI/id) do not seem to show any playlist affiliation.
Here is a bit from my program:
The gist is that you need the id for the playlist to grab the songs.
Basically you can take my code and change the where statement to .NAME +" = '"+name+"'",
private Cursor getPlaylists(String playlistId){
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._ID+ " = "+playlistId+"",
null,
null);
startManagingCursor(cursor);
cursor.moveToFirst();
playlist_id = cursor.getString(0);
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 = null;
cursor = this.managedQuery(
MediaStore.Audio.Playlists.Members.getContentUri("external",playlist_id2 ),
projection,
MediaStore.Audio.Media.IS_MUSIC +" != 0 ",
null,
null);
}
cManager(cursor,2,1);
return cursor;
}
Here is a working way to get tracks from a play list. Basically it loops the cursor through all the of the playlist query, and each time it gets the id of a member (track) and using that id of the track we can get other data such as path, artist, duration, album etc.
ContentResolver contentResolver = getContentResolver();
Uri playListUri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistID); //playlistID is the _ID of the given playlist
MediaMetadataRetriever mr = new MediaMetadataRetriever();
Cursor cursor = contentResolver.query(playListUri, null, null, null, null);
if(cursor != null)
{
if(cursor.moveToNext()) {
do {
String track_id = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID));
Uri mediaContentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] trackProjection = new String[]{MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.ALBUM, MediaStore.Audio.Media.DATA};
String selection = MediaStore.Audio.Media._ID + "=?";
String[] selectionArgs = new String[]{"" + track_id};
Cursor mediaCursor = contentResolver.query(mediaContentUri, trackProjection, selection, selectionArgs, null);
if (mediaCursor != null) {
if (mediaCursor.getCount() >= 0) {
mediaCursor.moveToPosition(0);
String song_title = mediaCursor.getString(mediaCursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
String song_artist = mediaCursor.getString(mediaCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));
String song_album = mediaCursor.getString(mediaCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
String song_path = mediaCursor.getString(mediaCursor.getColumnIndex(MediaStore.Audio.Media.DATA));
}
}
} while (cursor.moveToNext());
}
}
this is the way to get all playlist from the device
public ArrayList<String> getPlayList() {
ArrayList<String> arrayList=new ArrayList<String>();
String[] proj = {"*"};
Uri tempPlaylistURI = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
// In the next line 'this' points to current Activity.
// If you want to use the same code in other java file then activity,
// then use an instance of any activity in place of 'this'.
Cursor playListCursor= activity.managedQuery(tempPlaylistURI, proj, null,null,null);
if(playListCursor == null){
System.out.println("Not having any Playlist on phone --------------");
return arrayList;//don't have list on phone
}
System.gc();
String playListName = null;
System.out.println(">>>>>>> CREATING AND DISPLAYING LIST OF ALL CREATED PLAYLIST <<<<<<");
for(int i = 0; i <playListCursor.getCount() ; i++)
{
playListCursor.moveToPosition(i);
playListName = playListCursor.getString(playListCursor.getColumnIndex("name"));
System.out.println("> " + i + " : " + playListName );
arrayList.add(playListName);
}
if(playListCursor != null)
playListCursor.close();
return arrayList;
}