I am developing an application in which I list the device contacts, and perform some manipulation with them. I listen to contact changes as described in the following links: Link 1, Link 2
My code is as follows:
public class ContactService extends Service {
private int mContactCount;
Cursor cursor = null;
private int contactStateCheckingFlag=0;
static ContentResolver mContentResolver = null;
public static final String AUTHORITY = "com.example.contacts";
public static final String ACCOUNT_TYPE = "com.example.myapplication.account";
public static final String ACCOUNT = "myapplication";
Account mAccount;
Bundle settingsBundle;
int i=0;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();// Get contact count at start of service
mContactCount = getContactCount();
this.getContentResolver().registerContentObserver(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, true, mObserver);
Cursor curval = getApplicationContext().getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null, null, null, null);
if (curval != null && curval.getCount() > 0) {
curval.getCount();
}
curval.close();
}
private int getContactCount() {
try {
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null,null);
if (cursor != null) {
return cursor.getCount();
} else {
cursor.close();
return 0;
}
} catch (Exception ignore) {
} finally {
cursor.close();
}
return 0;
}
private ContentObserver mObserver = new ContentObserver(new Handler()) {
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
new ContactServiceAsyncClass().execute();
}
};
private class ContactServiceAsyncClass extends AsyncTask<Void, Void, Void> {
ArrayList<Integer> arrayListContactID = new ArrayList<Integer>();
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... arg0) {
// Get the current count of contacts
int currentCount = getContactCount();
// Add New Contact
if (currentCount > mContactCount){
Cursor contactCursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
if (contactCursor.getCount()<=0) {
Log.d("Contact Cursor count"," is zero");
}else {
Log.d("Contact Cursor count", " > 0");
// Fetch all contact ID from cursor
if(contactCursor.moveToFirst()){
do {
int contactID = contactCursor.getInt(contactCursor.getColumnIndex(ContactsContract.Data._ID));
arrayListContactID.add(contactID);
} while (contactCursor.moveToNext());
}
// Sort the array list having all contact ID
Collections.sort(arrayListContactID);
Integer maxID=Collections.max(arrayListContactID);
Log.d("maxID", ""+maxID);
// Get details of new added contact from contact id
String whereName = ContactsContract.Data._ID + " = ?";// Where condition
String[] whereNameParams = new String[] { ""+maxID}; // Pass maxID
Cursor cursorNewContact = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, whereName, whereNameParams, null);
if(cursorNewContact.getCount()<=0){
}else {
if(cursorNewContact.moveToFirst()){
do{
// Fetch new added contact details
} while(cursorNewContact.moveToNext());
}
}
cursorNewContact.close();
}
contactCursor.close();
} else if(currentCount < mContactCount){
// Delete Contact/
// CONTACT DELETED.
} else if(currentCount == mContactCount){
// Update Contact1
}
mContactCount = currentCount;
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
}
I am able to fetch new added contact. The question is how to delete and update contact? How to know which contact is deleted and updated, as the contact change broadcast doesn't specify the id of the contact that changed?
Please provide your valuable suggestions and guide me in detail.
Thank you.
For delete operation,
1.First you store the previous list of contacts id in local database.
for example: you added contact id`s are 123,124,125.
Now we assume your last added contact(125) was deleted.
How we find it?.
simple first get the list of old contact list. and compare with current contact list.
If old contact list element not in the new list, that contact is deleted from phone.
Note: If delete operation complete, you need to update the contact id`s into DB.
For Update operation,
1.Use VERSION flag for indicating any changes in your contact.
2.VERSION default value is 1. if you modify the contacts,it automatically increase to 2.
3.So you need to store old version value in your local DB. and compare the version value increase or not. If increase the VERSION value you need to update this contact.
Refer the official link,
https://developer.android.com/reference/android/provider/ContactsContract.RawContacts.html
For complete project,
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android-apps/4.0.4_r2.1/com/android/exchange/adapter/ContactsSyncAdapter.java?av=f
Related
The situation:
I've added a custom action to a contact in Android following the instructions in this question and the related sample app on GitHub. When pressed I want to dial that contact in my application. I am able to successfully retrieve the Contact in the Activity that is opened when the custom action is pressed.
I execute this:
Cursor cursor = context.getContentResolver().query(data, null, null, null, null);
if (cursor != null) {
newIntent = true;
contact = LocalContactAsync.getContacts(cursor, context.getContentResolver()).get(0);
cursor.close();
}
And the data I retrieve from Android is:
content://com.android.contacts/data/2849
Notice the number 2849 at the end, this is not the native ID of the contact. The native ID of the contact is 459. I am able to successfully retrieve the contact executing this query, the following data returns:
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID);
-returns '2849'
cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)) ;
-returns 'sample samplee' wich is correct
But although this is true:
cursor.getInt(cur.getColumnIndex(
ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0)
The following function returns an empty cursor:
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{id}, null);
-id = 2849 in this case but if I fill in 459 I retrieve the right amount of telephone numbers
The real contact has 3 numbers so it should return 3 numbers.
How am I able to fix this?
Edited:
This is how I retrieve numbers, to be clear: I get the correct name, but the following query returns null while the contact has numbers.
ArrayList<Number> numbers = new ArrayList<>();
if (cur.getInt(cur.getColumnIndex(
ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0) {
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{id}, null);
while (pCur.moveToNext()) {
numbers.add(new nl.coffeeit.clearvoxnexxt.objects.dto.Number(pCur.getString(pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER))));
}
pCur.close();
}
return numbers;
Please note that I do not request an intent, I receive it through a custom action that is added to a native contact, like Viber and WhatsApp do:
Full code LocalContacts Async:
private static final String TAG = "LocalContactAsync";
private static List<Contact> contacts;
private Context context;
private boolean refreshOtherFragments;
private boolean renew;
private synchronized List<Contact> getContacts(Context context) {
if (!renew && contacts != null) {
return contacts;
}
ContentResolver cr = context.getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
if (cur != null && cur.getCount() > 0) {
contacts = getContacts(cur, cr);
cur.close();
return contacts;
}
if (cur != null) {
cur.close();
}
return new ArrayList<>();
}
public static List<Contact> getContacts(Cursor cur, ContentResolver cr) {
List<Contact> contacts = new ArrayList<>();
while (cur.moveToNext()) {
String id = getId(cur);
String name = getName(cur);
ArrayList<Number> numbers = getNumbers(cur, cr, id);
if (name != null) {
contacts.add(new Contact(id, name, numbers));
}
}
Log.d(TAG, "amount of contacts" + contacts.size());
return contacts;
}
private static ArrayList<Number> getNumbers(Cursor cur, ContentResolver cr, String id) {
ArrayList<Number> numbers = new ArrayList<>();
if (cur.getInt(cur.getColumnIndex(
ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0) {
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{id}, null);
while (pCur.moveToNext()) {
numbers.add(getNumber(pCur));
}
pCur.close();
}
return numbers;
}
private static Number getNumber(Cursor pCur) {
return new nl.coffeeit.clearvoxnexxt.objects.dto.Number(pCur.getString(pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER)));
}
private static String getId(Cursor cur) {
return cur.getString(
cur.getColumnIndex(ContactsContract.Contacts._ID));
}
private static String getName(Cursor cur) {
return cur.getString(cur.getColumnIndex(
ContactsContract.Contacts.DISPLAY_NAME));
}
Code for Number DTO:
public class Number implements Parcelable, Serializable {
#SerializedName("number")
#Expose
public String number;
#SerializedName("type")
#Expose
public String type = "";
#SerializedName("inherited")
#Expose
public Boolean inherited = false;
public Number(String number) {
this.number = number;
}
protected Number(Parcel in) {
number = in.readString();
type = in.readString();
byte inheritedVal = in.readByte();
inherited = inheritedVal == 0x02 ? null : inheritedVal != 0x00;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(number);
dest.writeString(type);
if (inherited == null) {
dest.writeByte((byte) (0x02));
} else {
dest.writeByte((byte) (inherited ? 0x01 : 0x00));
}
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<Number> CREATOR = new Parcelable.Creator<Number>() {
#Override
public Number createFromParcel(Parcel in) {
return new Number(in);
}
#Override
public Number[] newArray(int size) {
return new Number[size];
}
};
public Number setNumber(String number) {
this.number = number;
return this;
}
}
The first thing to notice is that a call to the contacts picker like this:
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
will return a Uri like this:
content://com.android.contacts/contacts/lookup/3163r328-4D2941473F314D2941473F3131/328
The second to last path (3163r....) is the lookup key, while 328 is the NAME_RAW_ID.
Compare this with the Intent you get from the sample application. This contains an Uri that looks like this:
content://com.android.contacts/data/2849
As you have said, calling the content resolver with this Uri is not sufficient to retrieve phone numbers, although it may be used to retrieve the name of the contact and the id. So we will use the incomplete Intent Uri to construct a new Lookup Uri that we can use to get the phone numbers.
Let's add the following methods to your LocalContactAsync (I won't refactor anything you have done so far, I'll just add in the style you have used):
public static Uri getLookupUri(Cursor cur) {
return getContentUri(getLookupKey(cur), getNameRawId(cur));
}
private static String getLookupKey(Cursor cur) {
return cur.getString(
cur.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
}
private static String getNameRawId(Cursor cur) {
return cur.getString(cur.getColumnIndex(ContactsContract.Contacts.NAME_RAW_CONTACT_ID));
}
private static Uri getContentUri(String lookupKey, String nameRawId) {
return new Uri.Builder()
.scheme("content")
.authority("com.android.contacts")
.appendPath("contacts")
.appendPath("lookup")
.appendPath(lookupKey)
.appendPath(nameRawId)
.build();
}
Let's alter the ViewingActivity inside the sample application so that it actually retrieves the contact details. We can now do that with the following code inside onResume():
#Override
protected void onResume() {
super.onResume();
Uri uri = getIntent().getData();
Cursor intentCursor = this.getContentResolver().query(uri, null, null, null, null);
Contact contact = null;
if (intentCursor != null) {
intentCursor.moveToFirst();
Uri lookupUri = LocalContactAsync.getLookupUri(intentCursor);
Cursor lookupCursor = this.getContentResolver().query(lookupUri, null, null, null, null);
contact = LocalContactAsync.getContacts(lookupCursor, this.getContentResolver()).get(0);
intentCursor.close();
lookupCursor.close();
}
}
The contact will now contain the phone numbers as required.
How can i fetch the last inserted contact number from the android phone book. I already done with the fetching all the unique contact numbers and its count from the database and i have also made a ContentObserver for listening the updates related to phone book.but now i want last inserted contact number. I am putting my whole code over here.
This is my code to fetch all the contacts :-
Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
while (phones.moveToNext()) {
phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
listNumbers.add(phoneNumber);
}
ArrayList<String> unique = removeDuplicates(listNumbers);
for (String element : unique) {
System.out.println(element);
}
sb = new StringBuffer();
for (String item : unique) {
if (sb.length() > 0) {
sb.append(',');
}
sb.append(item);
}
numbersString = sb.toString();
// Log.e("BUFFER", numbersString.toString());
phones.close();
Log.e("SIZE", unique.size() + "");
And Now this is the code for contentobserver :-
private ContentObserver mObserver = new ContentObserver(new Handler()) {
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
final int currentCount = getContactCount();
if (currentCount < mContactCount) {
// DELETE HAPPEN.
Log.e("STATUS===>", "Deletion");
} else if (currentCount == mContactCount) {
// UPDATE HAPPEN.
} else {
// INSERT HAPPEN.
Log.e("STATUS===>", "Insertion");
}
mContactCount = currentCount;
}
};
In onchange INSERT Condition i want that newly added contact number.
I m making one app in which I want to have all records from my android mobile 4.0 to my android application. I have done this also. but problem is I have almost 200 contacts in my phonebook but I m getting only 90 records randomly in my application. I have tried a lot. but nothing solution I found out. can any one has solution? below is my code :
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
Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?", new
String[]{id}, null);
while (pCur.moveToNext())
{
// Do something with phones
String pnumber
=pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contactids.add(id);
names.add(name.trim().toLowerCase());
PhoneNumbers.add(pnumber);
}
pCur.close();
}
else
{
String pnumber="";
contactids.add(id);
names.add(name);
PhoneNumbers.add(pnumber);
}
}
finally I have done with following code. But the problem with this is it can't fetch records if contacts are more then 1500. and till 1500 records process is very slow.
public class MyActivity extends Activity {
public Cursor cur;
public int j=0;
#Override
public void onCreate(Bundle savedInstanceState)
{
cur=getContacts();
startManagingCursor(cur);
cur.moveToFirst();
Button btn=(Button)findViewById(R.id.button1);
btn1.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
contactids=new ArrayList<String>();
names=new ArrayList<String>();
PhoneNumbers=new ArrayList<String>();
try
{
new showdialog1(MyActivity.this).execute();
}
catch (Exception e)
{
// TODO: handle exception
e.printStackTrace();
}
}
});
}
class showdialog1 extends AsyncTask<Void, Void, Void>
{
public showdialog1(Activity act) {
super.onPreExecute();
}
#Override
protected void onPreExecute() {
dialog = ProgressDialog.show(MyActivity.this,"title", "message");
}
#Override
protected Void doInBackground(Void... params) {
if (cur.getCount() > 0)
{
do
{
String id = cur.getString(
cur.getColumnIndex(ContactsContract.Contacts._ID));
String name = cur.getString(
cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
Cursor pCur = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +"=" +id, null, null);
if(pCur.getCount()>0)
{
while (pCur.moveToNext())
{
// Do something with phones
String pnumber=pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contactids.add(id);
names.add(name.trim().toLowerCase());
PhoneNumbers.add(pnumber);
}
pCur.close();
}
else
{
String pnumber="";
contactids.add(id);
names.add(name.trim().toLowerCase());
PhoneNumbers.add(pnumber);
}
}while (cur.moveToNext());
cur.close();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
dialog.dismiss();
int contactidsize=contactids.size();
int namesize=contactids.size();
int numbersize=contactids.size();
saverecords();
return;
}
}
}
you get your contact list of your phone..
try this code and after that if you want to use contact from list you select that contace and use details of that contact (ex: number, name ,email.id etc..)
plz goto below link
/*** USE this CODE ******/
int PICK_CONTACT=1;
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
startActivityForResult(intent, PICK_CONTACT);`
You are using the condition:
Integer.parseInt(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER ))) > 0)
which does not consider the contacts that doesnot have phone numbers .
Hope this works
See ContactManager http://developer.android.com/resources/samples/ContactManager/index.html
Create and initialize boolean variable mShowInvisible - if set to true, it will list all contacts regardless of user preference
/**
* Obtains the contact list for the currently selected account.
*
* #return A cursor for for accessing the contact list.
*/
private Cursor getContacts(){
// Run query
Uri uri = ContactsContract.Contacts.CONTENT_URI;
String[] projection = new String[] {
ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME
};
String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '"(mShowInvisible ? "0" : "1") + "'";
String[] selectionArgs = null;
String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
return managedQuery(uri, projection, selection, selectionArgs, sortOrder);
}
I have two buttons inside of my application, one for next and one for prev. I want the next button to get the next record inside of my database and display it inside of my view, and the prev button to get the previous record and display it inside of my view. How would I call the next or previous record? I have looked for tutorials and stuff but didn't find any. I anyone has a tutorial please share with me. Thanks for any help. I wish I had some code to provide but I really don't know where to start.
I use an int to pull the record from the dbase.
From my ContactView class
static long record = 1;
public void getData() {
DBase db = new DBase(this);
db.open();
lastRecord = db.lRec();
firstRecord = db.fRec();
rRec = db.getRec(record);
db.close();
}
then my query is from my Dbase class
public String[] getRec(long record) {
record = ContactView.record;
String[] columns = new String[] { KEY_ROWID, KEY_ONE, KEY_TWO,
KEY_THREE, KEY_FOUR, KEY_FIVE, KEY_SIX };
Cursor c = ourDatabase.query(DATABASE_TABLE, columns, KEY_ROWID + "="
+ record, null, null, null, null);
if (c != null && c.moveToFirst()) {
String rRec = c.getString(0);
String rOne = c.getString(1);
String rTwo = c.getString(2);
String rThree = c.getString(3);
String rFour = c.getString(4);
String rFive = c.getString(5);
String rSix = c.getString(6);
String[] rData = { rRec, rOne, rTwo, rThree, rFour,
rFive, rSix };
return rData;
}
return null;
}
and the next few are from my ContactView class
my buttons
#Override
public void onClick(View arg0) {
switch (arg0.getId()) {
case R.id.bSQLvPrev:
recordMinus();
display();
break;
case R.id.bSQLvNext:
recordPlus();
display();
break;
}
}
and the methods they call
public void display() {
etSQLvRec.setText(rRec[0]);
etSQLvOne.setText(rRec[1]);
etSQLvTwo.setText(rRec[2]);
etSQLvThree.setText(rRec[3]);
etSQLvFour.setText(rRec[4]);
etSQLvFive.setText(rRec[5]);
etSQLvSix.setText(rRec[6]);
}
public void recordPlus() {
record++;
}
public void recordMinus() {
record--;
}
That will get the record from the database based on the "record" variable, and the buttons increment it, or decrement it, it also skips any "empty" records.
EDIT OK, I had changed some stuff around since I lasted used my db, so use the next recordPlus() and recordMinus() code instead
public void recordPlus() {
if (record < lastRecord) {
record++;
} else {
record = firstRecord;
}
getData();
do {
if (record < lastRecord) {
record++;
} else {
record = firstRecord;
}
getData();
} while (rRec == null);
}
public void recordMinus() {
if (record == 1) {
record = lastRecord;
} else {
record--;
}
getData();
do {
if (record == 1) {
record = lastRecord;
} else {
record--;
}
getData();
} while (rRec == null);
}
And you'll need my fRec() and lRec() which find the first and last records in the DB
public long fRec() {
Cursor c = ourDatabase.query(DATABASE_TABLE, new String[] { "min(" +
KEY_ROWID
+ ")" }, null, null, null, null, null);
c.moveToFirst();
long rowID = c.getInt(0);
return rowID;
}
}
public long lRec() {
long lastRec = 0;
String query = "SELECT ROWID from Table order by ROWID DESC limit 1";
Cursor c = ourDatabase.rawQuery(query, null);
if (c != null && c.moveToFirst()) {
lastRec = c.getLong(0);
}
return lastRec;
}
I can catch the event when contact is modified. But I want to catch that modified contact details like CONTACT_ID, name, etc... Please can you help me to do that. my code is follow.
public class TestContentObserver extends Activity {
int contactCount = 0;
final String[] projection = new String[] { RawContacts.CONTACT_ID, RawContacts.DELETED };
Cursor curval;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
curval = getContentResolver().query(RawContacts.CONTENT_URI, projection, null, null, null);
contactCount = curval.getCount();
curval.registerContentObserver(new ContentObserver(new Handler()) {
#Override
public void onChange(boolean selfChange) {
getChangedContactDetails();
}
#Override
public boolean deliverSelfNotifications() {
return true;
}
});
}
public void getChangedContactDetails(){
// how can I catch the affected contact details
}
}
Have a look at: http://developer.android.com/resources/articles/contacts.html, section "Lookup URI"
when registering your ContentObserver you can just register an URI that points directly to the contact you are interested in. then your observer will only get notified when your interesting contact is modified.
Uri lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey);
getContentResolver().registerContentObserver(lookupUri, false, new ContentObserver(){});
Lookup key key is unique for each record