I want to prevent my music player app from scanning the directories for audio files everytime the app launches. How can I do that?
I have been using the following code to scan the audio files.
public void getSongList() {
ContentResolver contentResolver=getContentResolver();
Uri musicUri=android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor musicCursor = contentResolver.query(musicUri, null, null, null, null);
if(musicCursor!=null && musicCursor.moveToFirst()) {
//get columns
int titleColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media.TITLE);
int idColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media._ID);
int artistColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media.ARTIST);
int albumIDColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID);
//add songs to list
do {
long thisId = musicCursor.getLong(idColumn);
String thisTitle = musicCursor.getString(titleColumn);
String thisArtist = musicCursor.getString(artistColumn);
long thisAlbumID=musicCursor.getLong(albumIDColumn);
Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart");
Bitmap albumArtBitMap=null;
Uri albumArtUri = ContentUris.withAppendedId(sArtworkUri, thisAlbumID);
try {
albumArtBitMap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), albumArtUri);
Matrix m = new Matrix();
m.setRectToRect(new RectF(0, 0, albumArtBitMap.getWidth(), albumArtBitMap.getHeight()), new RectF(0, 0, 300, 300), Matrix.ScaleToFit.CENTER);
albumArtBitMap = Bitmap.createBitmap(albumArtBitMap, 0, 0, albumArtBitMap.getWidth(), albumArtBitMap.getHeight(), m, true);
} catch (IOException e) {
e.printStackTrace();
}
songList.add(new Song(thisId, thisTitle, thisArtist,albumArtBitMap));
}
while (musicCursor.moveToNext());
}
}
I want the app to only scan when there are new files. Because If I scan the whole SD card every time then it'll take too much time for starting the app. Please Help me with that
No need to keep all the songs in local app list.
To show the mp3list you can use content provider cursor list adapter query with limit (on scroll query page by page )
To search use directly the contentprovider query method.
Only keep a playslist on you local database pointing to mp3 uri.
this link might helps you :
How to update listview whose data was queried from database through SimpleCursorAdapter?
Every time you start the app see how much space you have on your device.
File path = Environment.getDataDirectory();
megaBytesAvailable(path)
public static float megaBytesAvailable(File file) {
StatFs stat = new StatFs(file.getPath());
long bytesAvailable = (long)stat.getBlockSizeLong() * (long)stat.getAvailableBlocksLong();
return bytesAvailable / (1024.f * 1024.f);
}
Save it to your app's cache as a variable and compare it every time you start the app, if it's greater then you know you need to scan.
if(comparedVariable < megaBytesAvailable(music_directory_path)){
getSongList();
//Save it again to compare next time if more storage is used
comparedVariable = megaBytesAvailable(music_directory_path);
//Save it to SharedPrefs for next boot up comparison
}
I think #royrok answer can help you, where #royrok checking the playlist in mediastore instead rescan the sdcard. Below I include #royrok answer.
Rather than rescan the card, the app iterates through all the playlists in MediaStore and checks the length of the _data field. I discovered that for all the lists with no associated M3U file, this field was always empty. Then it was just a case of finding the source code for the original android music app, finding the delete method and using that to delete any playlists with a length of 0. I've renamed the app PlaylistPurge (since it doesn't 'rescan' anymore) and am posting the code below:
package com.roryok.PlaylistPurge;
import java.util.ArrayList;
import java.util.List;
import android.app.ListActivity;
import android.content.ContentUris;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
public class PlaylistPurge extends ListActivity {
private List<String> list = new ArrayList<String>();
private final String [] STAR= {"*"};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ListAdapter adapter = createAdapter();
setListAdapter(adapter);
}
/**
* Creates and returns a list adapter for the current list activity
* #return
*/
protected ListAdapter createAdapter()
{
// return play-lists
Uri playlist_uri= MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
Cursor cursor= managedQuery(playlist_uri, STAR, null,null,null);
cursor.moveToFirst();
for(int r= 0; r<cursor.getCount(); r++, cursor.moveToNext()){
int i = cursor.getInt(0);
int l = cursor.getString(1).length();
if(l>0){
// keep any playlists with a valid data field, and let me know
list.add("Keeping : " + cursor.getString(2) + " : id(" + i + ")");
}else{
// delete any play-lists with a data length of '0'
Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, i);
getContentResolver().delete(uri, null, null);
list.add("Deleted : " + cursor.getString(2) + " : id(" + i + ")");
}
}
cursor.close();
// publish list of retained / deleted playlists
ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
return adapter;
}
}
Here's a link to a post on my blog about the app http://roryok.com/blog/index.php/2010/07/23/clearing-out-deleted-playlists-in-android/
UPDATE
I've found an article about Querying And Removing Media From The Android MediaStore, I included the content below.
.
.
.
Android provides a way to register different type of media, such as audio, video, and images, for consumption by any app. This is convenient if your app is, say, a music player or an image editor. Android's MediaStore is the provider for this meta data, and includes information about the media such as title, artist, size, and location.
If your application does any sort of media content creation, such as image editing or downloading audio from an external website, then you generally want to make that content accessible from any other apps that can consume it. When you create a file you can use the MediaScannerConnection to add the file and its metadata to the MediaStore.
If you delete the file from the file system, the metadata remains in the MediaStore until Android scans the system for new media, which typically happens when the system first boots up or can be called explicitly called in such a way:
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + Environment.getExternalStorageDirectory() )));
While this method works, it is time and resource consuming, as basically the entire file system must be re-scanned. An alternative is to explicitly delete the file from the MediaStore. We're going to discuss two ways to do this. The first is to query to MediaStore for the content, based on some predicate, and delete based on the unique ID the MediaStore identifies it by. The second, and easier, way to do it is to just specify the predicate in the delete statement. In this example, I'm going to be deleting an audio file based on its file name and path, but you can easily use this to delete any type of media based on any known information (such as video duration, or image dimensions).
In querying the MediaStore, you should think of it as an SQL database. You need to form your query by specifying the table (the MediaStore's external content table), the columns you need (the content’s ID), and the where clause (how to identify the content). To perform the actual query, we’re going to use the ContentResolver's query() method.
String[] retCol = { MediaStore.Audio.Media._ID };
Cursor cur = context.getContentResolver().query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
retCol,
MediaStore.MediaColumns.DATA + "='" + filePath + "'", null, null);
if (cur.getCount() == 0) {
return;
}
cur.moveToFirst();
int id = cur.getInt(cur.getColumnIndex(MediaStore.MediaColumns._ID));
cur.close();
The first argument to query() specifies the columns we want to be returned, which in this case is only "_ID". The second argument specifies that we want to look at the media stored on the external SD card (which would be internal storage on devices with no SD card). The third argument is the predicate which specifies what content we're looking for. In this case, I'm identifying the file by its path in the file system (which is what is stored in the MediaColumns.DATA column). The fourth and fifth columns are the predicate's arguments and the ordering, respectively. I'm including my predicate's arguments in the predicate itself so that's not necessary, and if your only looking for one piece of content and your predicate is specific enough to just return one row then the ordering doesn't matter.
It is very important to make the predicate specific enough so that you're guaranteed to get the exact ID you're looking for. In my case, I know that there can be only one file at a particular location, but you could use a combination of any columns (such as title, artist, and album) to find the content. Check out the MediaColumns for all the possibilities.
Once you perform the actual query, you'll want to check to see whether the MediaStore actually contains the content you're trying to delete. If you don't handle this in some way your app will crash while trying to iterate through the cursor. Once you confirm that the query returned some data, grab the ID by moving the cursor to its first position, reading the “_ID” column, and closing the cursor. It's very important that you remember to close the cursor once you've finished using it. Your app won't crash, but you'll get memory leaks and complaints in LogCat.
Now that we have the ID that the MediaStore associated with our content, we can call ContentResolver's delete() method similar to how we called its query() method.
Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
id);
context.getContentResolver().delete(uri, null, null);
The delete() method takes 3 arguments: the Uri to be deleted, the predicate, and the predicate arguments. We form the Uri by appending the ID we discovered by querying the MediaStore to the Uri of the audio files on external storage. Since we know exactly which row we want to delete, we don't need to specify the predicate or the predicate's arguments.
The second method to delete the content from the MediaStore takes advantage of the fact that querying and deleting from it are performed almost identically.
context.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
MediaStore.MediaColumns.DATA + "='" + path + "'", null);
We can use the predicate of the delete() method to specify exactly what we want to delete, rather than having to query for it beforehand. While this method is more efficient (no extra query, no cursors to deal with), it has some pitfalls. You have no way of explicitly confirming what you're deleting. You're also not able to do advanced queries with this method, such as if you wanted to delete the most recently added content (which you could do by ordering the query based on the DATE_ADDED column). However, both ways give you a way to confirm what you've deleted since the delete() method returns the number of rows that it deleted as an integer.
I tried to read the complete profile information such a (Full name, phone, adresse, mail .... ).
I have searched everywhere for a good example code. I tried many ways (Uri => Cursor) to access the Profile.
At this time I can fetch just the Full name (of the Profile contact), nothing more.
I can fetch data of other contacts using an Intent, sending to the Contacts app, BUT I CAN'T READ THE PROFILE CONTACT (JUST FULL NAME).
I have added the READ_PROFILE permission in the manifest file.
With the following code I get the Full Name (I can also access first and last name separately ):
Uri uriProfile = Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI,
ContactsContract.Contacts.Data.CONTENT_DIRECTORY);
Cursor cursorProfile = this.getContentResolver().query(uriProfile,
null, null, null, null);
String projection = Profile.DISPLAY_NAME;
String profileName = cursorProfile.getString(cursorProfile.getColumnIndex(projection);
But when I use this the following projection to get Phone Number, it returns an error and the app stops working:
String projection = ContactsContract.CommonDataKinds.Phone.NUMBER
I found a solution using this code:
Get an URI Profile
Crate a cursor pointing to the URI content with null projection. Because for the profile, data are saved differently than a normal contact.
Point the cursor to the wanted data using MIMETYPE.
Uri uriProfile = Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI,
ContactsContract.Contacts.Data.CONTENT_DIRECTORY);
Cursor cursorProfile = getApplicationContext().getContentResolver().query(uriProfile,
null, null, null, null);
String cursorProfile_MIMIETYPE = cursorProfile.getString(cursorProfile.getColumnIndex("MIMETYPE"));
I am working with Androids contacts and trying to get particular pieces of data. I can already get emails, phone numbers, their name, etc. However I am having difficulty getting the relationship field.
http://developer.android.com/reference/android/provider/ContactsContract.CommonDataKinds.Relation.html
So my goal is: Given a particular userid (from the contact database on Android), figure out their relation field.
This should work. The idea is to connect search in the Data table but use stuff from CommonDataKinds. This is done in where clause ... Data.MIMETYPE == CommonDataKinds.Relation.CONTENT_ITEM_TYPE. This will get you the row with all the Relation stuff.
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract.CommonDataKinds.Relation;
import android.provider.ContactsContract.Data;
import android.util.Log;
...
public void logCatTheRelation(long contactId){
Uri uri = Data.CONTENT_URI;
String where = String.format(
"%s = ? AND %s = ?",
Data.MIMETYPE,
Relation.CONTACT_ID);
String[] whereParams = new String[] {
Relation.CONTENT_ITEM_TYPE,
Long.toString(contactId),
};
String[] selectColumns = new String[]{
Relation.NAME,
// add additional columns here
};
Cursor relationCursor = this.getContentResolver().query(
uri,
selectColumns,
where,
whereParams,
null);
try{
if (relationCursor.moveToFirst()) {
Log.d("gizm0", relationCursor.getString(
relationCursor.getColumnIndex(Relation.NAME)));
}
Log.d("gizm0", "sadly no relation ... ");
}finally{
relationCursor.close();
}
}
I have an AutoCompleteTextView and i want it to autocomplete contact names on input. The problem is that Contacts.People has been deprecated. The new documentation says to use ContactsContract but i can't find any proper tutorial. Can anyone give me a sample code or point me to an appropriate tutorial.
Thanks.
Android Contact API For 2.0
Granting Access
Before an application can query the contact records access must be granted through the AndroidManifest.xml file stored in the root of the project. Add the following uses-permission belows the uses-sdk statement.
<uses-permission android:name="android.permission.READ_CONTACTS" />
Querying The Android Contact Database
Retrieving Contact Details
Basic contact information stored in Contacts table with detailed information stored in individual tables for normalization. In Android 2.0 to query the base contact records the URI to query is stored in ContactsContract.Contacts.CONTENT_URI.
package higherpass.TestContacts;
import android.app.Activity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
public class TestContacts extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
if (cur.getCount() > 0) {
while (cur.moveToNext()) {
String id = cur.getString(
cur.getColumnIndex(ContactsContract.Contacts._ID));
String name = cur.getString(
cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if (Integer.parseInt(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
//Query phone here. Covered next
}
}
}
}
}
This application starts off as any other Android application. First create a ContentResolver instance in cr. Then use the ContentResolver instance to query the database and return a Cursor with the contacts list. The query is performed against the URI stored in ContactsContract.Contacts.CONTENT_URI. Next check if the cursor contains records and if so loop through them. The record ID field is stored in the id variable. This will be used as a where parameter later. Also the display name field is stored in the string name.
Read more about Working With Android Contacts
Given a contact id, I can obtain various contact details (like name, phone, email-id, etc) by making different queries for a each of these fields.
But is there a method to obtain all the details associated with this contact id by making a single query?
Had to change a bit of the tutorial on Content Providers since it referenced deprecated classes, this might help.
import android.provider.ContactsContract.Contacts;
import android.database.Cursor;
// Form an array specifying which columns to return, you can add more.
String[] projection = new String[] {
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone
ContactsContract.CommonDataKinds.Email
};
Uri contacts = ContactsContract.Contacts.CONTENT_LOOKUP_URI;
// id of the Contact to return.
long id = 3;
// Make the query.
Cursor managedCursor = managedQuery(contacts,
projection, // Which columns to return
null, // Which rows to return (all rows)
// Selection arguments (with a given ID)
ContactsContract.Contacts._ID = "id",
// Put the results in ascending order by name
ContactsContract.Contacts.DISPLAY_NAME + " ASC");