Store and iterate through query results - java

I have a music application and its worked perfectly up until now.
I have more than 1000 songs inside my SD card. Which gets displayed inside a recycler view.
Before I didn't need the album art for a particular song but now I want to make my app a bit better and I found a way to get album art for every song.
What I tried:
ContentResolver contentResolver=getContentResolver();
Uri musicUri= MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor=contentResolver.query(musicUri,null,MediaStore.Audio.Media.IS_MUSIC + "=1",null,null);
if(cursor!=null && cursor.moveToFirst()){
int titleColumn = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
int artistColumn = cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);
int idColumn = cursor.getColumnIndex(MediaStore.Audio.Media._ID);
int imgSrcColumn = cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID);
do {
long thisID = cursor.getLong(idColumn);
String thisTitle = cursor.getString(titleColumn);
String thisArtist = cursor.getString(artistColumn);
long thisImg = cursor.getLong(imgSrcColumn);
Cursor cursorx = contentResolver.query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
new String[] {MediaStore.Audio.Albums._ID, MediaStore.Audio.Albums.ALBUM_ART},
MediaStore.Audio.Albums._ID+ "=?",
new String[] {String.valueOf(thisImg)},
null);
try {
if (cursorx.moveToFirst()) {
String path = cursorx.getString(cursorx.getColumnIndex(MediaStore.Audio.Albums.ALBUM_ART));
this.songs.add(new Song(thisID, thisTitle, thisImg,thisArtist,path));
}
cursorx.close();
}
catch (NullPointerException e)
{
e.printStackTrace();
}
} while (cursor.moveToNext());
Now this thing works and I get the result as expected but this takes too much time for my app to load the songs i.e. the song list.
I think other music player very much do the same but their loading time is way less. Like if I say play music, it takes about 2-3 seconds at max. Mine takes about 10-12 seconds, even more so.
Maybe it's because I am querying my SD card every time I need the album art.
Cursor cursorx = contentResolver.query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
new String[] {MediaStore.Audio.Albums._ID, MediaStore.Audio.Albums.ALBUM_ART},
MediaStore.Audio.Albums._ID+ "=?",
new String[] {String.valueOf(thisImg)},
null);
Any way to store the results and then loop through it? Like we do in php or any other language, get the results once from the DB and then loop through it. Instead of calling it every time.
Any better method is appreciated too!
P.s: Before using this thing ,app had the load time of 1 seconds at max.

Related

Hashmap to link albumid with album name?

I'm trying to display the album names and album artwork from the songs on my device.
I tried using a comparator to sort the album names, but when i sort the album names the artwork doesn't match the album name.
How can i link the albumid with the matching artwork?
I use picasso to display the album art and.
I get the album art from MediaStore.Audio.Media.ALBUM_ID;
Then i tried using a hashmap to link albumid with album name.
But im getting this error;
E/Couldn't execute task: java.lang.NullPointerException: Attempt to invoke interface method 'boolean android.database.Cursor.moveToFirst()' on a null object reference
// Creating the map "Album IDs" -> "Album Names"
albumIdToAlbumNameMap = new HashMap<>();
//This is what we'll ask of the albums
String[] albumColumns = {
SONG_ALBUMID,
SONG_ALBUM,
};
// Querying the album database
cursor = resolver.query(musicUri, albumColumns, null, null, null);
// Iterating through the results and filling the map.
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext())
albumIdToAlbumNameMap.put(cursor.getString(0), cursor.getString(1));
cursor.close();
// Map Song IDs to Album IDs
songIdToAlbumIdMap = new HashMap<>();
// For each album, we'll query the databases
for (String albumID : albumIdToAlbumNameMap.keySet()) {
Uri uri = MediaStore.Audio.Albums.getContentUri(albumID);
cursor = resolver.query(uri, new String[] { SONG_ID }, null, null, null);
// Iterating through the results, populating the map
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
long currentSongID = cursor.getLong(cursor.getColumnIndex(SONG_ID));
songIdToAlbumIdMap.put(Long.toString(currentSongID), albumID);
}
cursor.close();
}
final String musicsOnly = MediaStore.Audio.Media.IS_MUSIC + "=1";
// Actually querying the system
cursor = resolver.query(musicUri, columns, musicsOnly, null, null);
if (cursor != null && cursor.moveToFirst())
{
// NOTE: I tried to use MediaMetadataRetriever, but it was too slow.
// Even with 10 songs, it took like 13 seconds,
do {
// Creating a song from the values on the row
QuerySongs song = new QuerySongs(cursor.getInt(cursor.getColumnIndex(SONG_ID)),
cursor.getString(cursor.getColumnIndex(SONG_FILEPATH)));
song.setTitle (cursor.getString(cursor.getColumnIndex(SONG_TITLE)));
song.setArtist (cursor.getString(cursor.getColumnIndex(SONG_ARTIST)));
song.setAlbumID (cursor.getInt(cursor.getColumnIndexOrThrow(SONG_ALBUMID)));
// Using the previously created genre maps
// to fill the current song genre.
String currentGenreID = songIdToGenreIdMap.get(Long.toString(song.getId()));
String currentGenreName = genreIdToGenreNameMap.get(currentGenreID);
String currentAlbumID = songIdToAlbumIdMap.get(Long.toString(song.getId()));
String currentAlbumName = albumIdToAlbumNameMap.get(currentAlbumID);
song.setGenre(currentGenreName);
song.setAlbum(currentAlbumName);
// Adding the song to the global list
songs.add(song);
}
while (cursor.moveToNext());
}
else
{
// What do I do if I can't find any songs?
}
cursor.close();
Here does the error occur
// Iterating through the results, populating the map
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
long currentSongID = cursor.getLong(cursor.getColumnIndex(SONG_ID));
songIdToAlbumIdMap.put(Long.toString(currentSongID), albumID);
}
Seems like you need to fix your query input to avoid null in cursor.

Whatsapp contacts duplicating my contact list

I created a small activity that displays all contacts with phone numbers in my phone. However, there are duplicates for contacts that have whatsapp installed. For example, if John is in my contact list and he has a whatsapp account as well, the list would look like this:
...
Jake
John
John
JP
...
This is my code for assigning the cursor to the the adapter which links to a listview.
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String sortOrder = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC";
final Cursor cursor = getContentResolver().query(uri, null, null, null, sortOrder);
String[] from = {ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER, ContactsContract.CommonDataKinds.Phone._ID};
int[] to = {android.R.id.text1};
adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_multiple_choice, cursor, from, to, 0);
EDIT
With this code, I confirmed that the duplicates have 0 value for the ContactsContract.CommonDataKinds.Phone.TYPE which means it is a custom contact (Whatsapp). The rest are 2 which means it is a normal contact.
I need to figure out a query where it doesn't use any contacts where ContactsContract.CommonDataKinds.Phone.TYPE == 0
I know it might be a bit late. However I was having the same issue.
ContactsContract.CommonDataKinds.Phone.TYPE as per the android documentation refers not to whether it is a custom contact but to what type of contact it is i.e. Mobile(2), Home(1) or Work(3). Whatsapp contacts will be a 2 - mobile.
I used the following function to remove the duplicates (not sure if there is a better way):
private boolean checkDuplicate(String position) {
LinkedHashMap<String, Integer> map;
Integer testNull;
map=new LinkedHashMap<>();
testNull=map.get(position);
if(testNull==null) {
testNull=1;
map.put(position, testNull);
return false;
}
else {
testNull=testNull+1;
if(testNull==2) {
return true;
}
else {
map.put(position, testNull);
return false;
}
}
}

How to fetch Cover Art for MEDIA (MediaStore.Audio.Media) in android (API 19)

So i'm trying to build a very simple music player (I started working with android just a few days ago). Till now I wrote the code to retrieve songs informations. However I'm not able to retrieve the album art for every single media.
I know there is a column ALBUM_ART in MediaStore.Audio.Album (I tried to retrieve the Album art for each album and it worked), but I want to assign the album art to each song and I don't know how to do it.
Surfing the net I found this kind of code:
Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart");
Uri albumArtUri = ContentUris.withAppendedId(sArtworkUri, albumId);
But it doesn't seem to work, all I get is null. Can someone explain me how to retrieve the album art for each song?
String[] columns = { android.provider.MediaStore.Audio.Albums._ID,
android.provider. MediaStore.Audio.Albums.ALBUM,android.provider.MediaStore.Audio.Albums.ALBUM_ART };
int album_column_index=0;
Cursor cursor = getActivity().managedQuery(
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, columns, null,
null, null);
if (cursor.moveToFirst()) {
do {
arrAlbumNAme.add( cursor.getString(cursor
.getColumnIndex(android.provider.MediaStore.Audio.Albums.ALBUM)));
album_column_index = cursor.getColumnIndexOrThrow(android.provider.MediaStore.Audio.Albums._ID);
//cursor.moveToPosition(position);
albumID = cursor.getInt(album_column_index);
Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart");
Uri uri = ContentUris.withAppendedId(sArtworkUri, albumID);
arrAlbumArt.add(cursor.getString(cursor
.getColumnIndex(android.provider.MediaStore.Audio.Albums.ALBUM_ART)));
} while (cursor.moveToNext());
}
Inside adapter
holder.image_icon.setImageUri(arrAlbumArt.get(position));

Android set contact photo good quality without pixilization

I'm developing app to provide and set pictures for contacts from social networks, and I'm already figure out how to set picture to contact, but when I do this programmatically picture is stored in lower quality.
For example here is 2 examples, first picture is set with my app, second with native android contacts app (source picture is exactly the same for both cases):
First example
With my app:
With native app:
Second example
With my app:
With native app:
You can see pixilization on the hands and other parts.
The code I've used to set contact picture:
public static boolean setContactPhoto(long contactId, byte[] photo) {
ContentResolver cr = context.getContentResolver();
ContentValues values = new ContentValues();
long photoId = -1;
long rawContactId = -1;
Cursor rawContactsCursor = cr.query(
ContactsContract.RawContacts.CONTENT_URI,
new String[]{ContactsContract.RawContacts._ID},
String.format("%s=%d", ContactsContract.RawContacts.CONTACT_ID, contactId),
null,
null
);
while (rawContactsCursor.moveToNext()) {
rawContactId = rawContactsCursor.getLong(rawContactsCursor.getColumnIndex(ContactsContract.RawContacts._ID));
String where = String.format(
"%s=%d AND %s=='%s'",
ContactsContract.Data.RAW_CONTACT_ID,
rawContactId,
ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE
);
Cursor dataCursor = cr.query(
ContactsContract.Data.CONTENT_URI,
new String[]{ContactsContract.Data._ID},
where,
null,
null
);
if (dataCursor.moveToFirst()) {
photoId = dataCursor.getLong(dataCursor.getColumnIndex(ContactsContract.Data._ID));
dataCursor.close();
break;
}
dataCursor.close();
}
rawContactsCursor.close();
if (rawContactId < 0) return false;
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1);
values.put(ContactsContract.CommonDataKinds.Photo.PHOTO, photo);
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
if (photoId < 0) {
return cr.insert(ContactsContract.Data.CONTENT_URI, values) != null;
} else {
return cr.update(ContactsContract.Data.CONTENT_URI, values, String.format("%s=%d", ContactsContract.Data._ID, photoId), null) == 1;
}
}
My phone has 540x960 resolution, so source pictures are 960x960, this is strange, but if I try to set 540x960 picture it is cropped from all sides and scaled up, so pixilization is bigger.
So, how to avoid such pixilization setting up contact picture programmatically?
As described in the documentation, the .photo field hold the "Thumbnail photo of the raw contact."
I think you have to set the PHOTO_FILE_ID used to "accessing full-size photos by photo file ID".
See the related ContactsContract.DisplayPhoto

Populating ListView with cursor info from database, android app

I am working on a project with 2 other people, and as you can probably guess its a bit tought working with someone elses code. The app we are developing gets info from the internet, each item consisting of a title, link, post, and date. This info is read into a cursor and then output all in a single string buffer. This is obviously useless if you want to display the info neatly in an emulator window. I was hoping someone could help me understand how to work with the info to make it easy to use (i.e. each entry in one TextView, with all entries in a listview).
private void showEvents() {
SQLiteDatabase db = post.getWritableDatabase();
Cursor cursor= db.rawQuery("SELECT "+TITLE+", "+LINK+", "+POST+", "+DATE+" FROM "+TABLE_NAME+" "+"ORDER BY "+DATE+" DESC;",null);
startManagingCursor(cursor);
TextView tv = new TextView(this);
// Stuff them all into a big string
StringBuilder builder = new StringBuilder("");
while (cursor.moveToNext()) {
String title = cursor.getString(0);
String link = cursor.getString(1);
String post = cursor.getString(2);
String date= cursor.getString(3);
builder.append(title).append(": " );
builder.append(link).append(": " );
builder.append(post).append(": " );
builder.append(date).append("\n" );
}
// Display on the screen
tv.setText(builder);
this.setContentView(tv);
}
This code basically just outputs everything, unformmatted onto the screen. I've been trying to figure how to enter the while loop to capture each entity (which is 1 of title, link,post,date) and go from there.
Any advice is appreciated.
I'd suggest you read a book. One that will cover these topics is "Android in Practice", from Manning Publications.
There are several important required elements for this, but the key concept is an "Adapter", which connects views with data. You would end up defining a POJO to represent the data in each record, and your custom Adapter maps from that POJO into view fields.
Every time you enter this while loop, you are reading another line (because of the moveToNext())
while (cursor.moveToNext()) {
String title = cursor.getString(0);
String link = cursor.getString(1);
String post = cursor.getString(2);
String date= cursor.getString(3);
builder.append(title).append(": " );
builder.append(link).append(": " );
builder.append(post).append(": " );
builder.append(date).append("\n" );
}

Categories

Resources