All of us have added new contact in our mobile’s address book. Sometimes developers like us have also added contacts programmatically. This article is all about detecting that new addition of a contact in contact book. Means, whenever a new contact is added in phone book, our android app will get notified and work accordingly(namely storing, syncing etc.). Lets discuss on this interesting topic.
RECEPIE
- First of all we will create our activity.
- Next checking of permission related to contacts.
- Then we will create a Observer class.
- Create a new service where we will add content observer.
- Start the service to detect new contact addition.
Discussion and Code
As mentioned above, we have created a new activity ContactWatchActivity which is our launcher activity. Here we will check for contact reading permission and initiate the service.
In our AndroidManifest.xml we will add a permission i.e. READ_CONTACTS to get access of user contacts in phone book. It will look like
1 2 3 4 5 |
<uses-permission android:name="android.permission.READ_CONTACTS" /> |
Next, we will add two more packages i.e. Services and Utils to keep the code well structured. We will add our Service class and Observer class here.
CONTENT OBSERVER for New Contact
Now the question is what is Content Observer and it purposes? It’s an observer that receives call back for changes to content. The content may be contacts and images, call logs, user-defined or locally saved data It listens to changes of your contents if implemented properly. To use it we need two main things to do
- Implement subclass of ContentObserver
- Register content observer to listen to the changes made.
So, to detect our contact changes we will create our class MyContentObserver inside Utils package extending ContentObserver as super class. Here we will override onChange method and write our logic inside it. It simply provides uri and a selfChange parameter to detect changes.
Our trick is to query ContactsContract.Contacts.CONTENT_URI and move to last position to get the newly added contact. Yes, it’s that simple. After that we will check if it is a valid contact and then Toast it out. So, the code for the MyContentObserver is
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
package com.digitstory.testapplication.Utils; import android.Manifest; import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Handler; import android.provider.ContactsContract; import android.support.v4.app.ActivityCompat; import android.widget.Toast; public class MyContentObserver extends ContentObserver { private Context context; public MyContentObserver(Handler handler) { super(handler); } public MyContentObserver(Handler handler, Context context) { super(handler); this.context = context; } @Override public void onChange(boolean selfChange, Uri uri) { super.onChange(selfChange, uri); if (!selfChange) { try { if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) { ContentResolver cr = context.getContentResolver(); Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null); if (cursor != null && cursor.getCount() > 0) { //moving cursor to last position //to get last element added cursor.moveToLast(); String contactName = null, photo = null, contactNumber = null; String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID)); if (Integer.parseInt(cursor.getString(cursor.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); if (pCur != null) { while (pCur.moveToNext()) { contactNumber = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); if (contactNumber != null && contactNumber.length() > 0) { contactNumber = contactNumber.replace(" ", ""); } contactName = pCur.getString(pCur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); String msg = "Name : " + contactName + " Contact No. : " + contactNumber; //Displaying result Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); } pCur.close(); } } cursor.close(); } } } catch (Exception e) { e.printStackTrace(); } } } } |
SERVICE
Next we will add our service class in Services package where we will register our ContentObserver. We are using this service class to run it in background. It is mainly helpful if anyone wants the service to run in background even after the app is closed. The service class will look like
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
package com.digitstory.testapplication.Services; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Process; import android.provider.ContactsContract; import android.widget.Toast; import com.digitstory.testapplication.Utils.MyContentObserver; public class ContactWatchService extends Service { private Looper mServiceLooper; private ServiceHandler mServiceHandler; private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { try { //Register contact observer startContactObserver(); } catch (Exception e) { e.printStackTrace(); } } } private void startContactObserver(){ try{ Toast.makeText(getApplicationContext(),"ContactWatchService Started",Toast.LENGTH_SHORT).show(); //Registering contact observer getApplication().getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true,new MyContentObserver(new Handler(),getApplicationContext())); }catch (Exception e){ e.printStackTrace(); } } @Override public void onCreate() { // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process's // main thread, which we don't want to block. We also make it // background priority so CPU-intensive work will not disrupt our UI. HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); // Get the HandlerThread's Looper and use it for our Handler mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // For each start request, send a message to start a job and deliver the // start ID so we know which request we're stopping when we finish the job Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; mServiceHandler.sendMessage(msg); // If we get killed, after returning from here, restart return START_STICKY; } @Override public IBinder onBind(Intent intent) { // We don't provide binding, so return null return null; } @Override public void onDestroy() { super.onDestroy(); try{ //Code below is commented. //Turn it on if you want to run your service even after your app is closed /*Intent intent=new Intent(getApplicationContext(), ContactWatchService.class); startService(intent);*/ }catch (Exception e){ e.printStackTrace(); } } } |
For reference related to service class like this please read this article.
After adding the service we need add it in our AndroidManifest.xml. Below is the full manifest code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.digitstory.testapplication"> <uses-permission android:name="android.permission.READ_CONTACTS" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".Activity.ContactWatchActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".Services.ContactWatchService" android:enabled="true" android:exported="false" /> </application> </manifest> |
Now, in our launcher activity(ContactWatchActivity) we will start the service after checking permission for READ_CONTACTS. See the following code snippet.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
package com.digitstory.testapplication.Activity; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import com.digitstory.testapplication.R; import com.digitstory.testapplication.Services.ContactWatchService; public class ContactWatchActivity extends AppCompatActivity { public final static int MY_PERMISSIONS_READ_CONTACTS = 0x1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_contact_watch); startContactLookService(); } private void startContactLookService() { try { if (ActivityCompat.checkSelfPermission(ContactWatchActivity.this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {//Checking permission //Starting service for registering ContactObserver Intent intent = new Intent(ContactWatchActivity.this, ContactWatchService.class); startService(intent); } else { //Ask for READ_CONTACTS permission ActivityCompat.requestPermissions(ContactWatchActivity.this, new String[]{Manifest.permission.READ_CONTACTS}, MY_PERMISSIONS_READ_CONTACTS); } } catch (Exception e) { e.printStackTrace(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); //If permission granted if (requestCode == MY_PERMISSIONS_READ_CONTACTS && grantResults[0] == PackageManager.PERMISSION_GRANTED) { startContactLookService(); } } } |
Download and Run
That’s all. Now run your app and minimise it. Then add a new contact to your phone book. If everything is fine then you will see resulting Toast message.
You can find the full source code here.
If you find this article helpful, spread it then! Share it to inspire us. Subscribe us to get more stuff like this.