Detecting Incoming Calls on Android

Once in a while I come across an innovative mobile app, which has the potential to improve the way I do day-to-day tasks. One such app I use is MightyText. It allows to send SMS from computer using a Chrome extension. Also, it notifies about incoming calls. I like the ease of typing text messages from the comfort of a real keyboard and knowing who is calling when the phone is not around, and it rings. I wanted to try recreating some of the aspects of this app, in this post I am going implement incoming call detection and sending the notifications to Chrome extension.

I use an Android-based phone, so I will be implementing this for Android platform. I have programmed in Java years back. However, I been using C# as the primary language for programming. I have toyed with Xamarin few times, but never developed a serious application. If your don’t already know, Xamarin provides tools for creating cross-platform native applications for Android and iOS using C#. In addition to C# being familiar language, there is a good potential for reusing code across platforms using Xamarin. It wouldn’t be possible to cover nuances of cross-platform app development using Xamarin, so this app is going to be tied to Android.

Xamarin is built on top on Mono, an open-source implementation of .Net Framework. Xamarin offers two commercial products for Android and iOS, the free version has some limitations, but it is perfectly good for developing small applications.

I have divided this post into two parts, and the first part is going to focus on detecting the incoming calls, while the second part will target sending out the notifications. For the first part, I will be only displaying the local notifications(on the same phone), which doesn’t serve any real purpose, but it’s good enough for testing. We won’t need much of UI for this part, so we are going to create just a simple activity, showing the current state of the service and a button to star the service if it isn’t running.

Following is the app running on my Galaxy S3

What would it take to implement

  • Telephony Service: Registering with the Telephony Service would allow to monitor the phone status changes(ringing, off-huck, etc.)
  • Service: Service would allow to run our app continuously in the background
  • Broadcast Receiver: We need to start the service when device boots, broadcast receiver would be notified when device is started
  • Notifications: Core of the application - notifying the user about an incoming call. As mentioned earlier, this part of the post will be creating local notifications only, next part would cover creating the remote notifications.

All right, so let’s get started.

Create a Phone State Listener

Create a class which inherits from the PhoneStateListener. Override the OnCallStateChanged method which will be called on the status change. The first argument provides the status and second provides the incoming phone number. If the updated status is in ‘Ringing’ state, display the local notification.

	public class PhoneCallDetector: PhoneStateListener
	{
		Context context;

		public PhoneCallDetector(Context context_)
		{
			this.context = context_;
		}

		public override void OnCallStateChanged (CallState state, string incomingNumber)
		{
			if (state == CallState.Ringing) {
				ShowNotification("Incommming call detected from " + incomingNumber);
				base.OnCallStateChanged (state, incomingNumber);
			}
		}

	}

Showing local notification is straight forward, following is the ShowNotification helper method. Here, I am using NotificationCompat, which is available in API level 16(SDK version 4.1) and higher only.

private void ShowNotification(string message)
{
	NotificationCompat.Builder builder = new NotificationCompat.Builder(this.context)
		.SetContentTitle("CallMinder detected call")
		.SetSmallIcon(Android.Resource.Drawable.SymActionCall)
		.SetContentText(message);

	NotificationManager notificationManager = (NotificationManager) this.context.GetSystemService(Context.NotificationService);
	notificationManager.Notify(101, builder.Build());
}

Create a Service and Register with the Telephony Service

Now we need to register this basic phone call detector with the Telephony Service. We could do this in a service, on a service start. Create a PhoneCallService by inheriting form the Service class and decorating it with [Service] attribute. OnStartCommand gets reference to the Telephony Service and registers an instance of our PhoneCallDetector to listen for the call status change events.

	[Service]
	public class PhoneCallService: Service
	{
		public override void OnCreate ()
		{
			base.OnCreate ();
		}

		//this will not be called, however it is require to override
		public override IBinder OnBind (Intent intent)
		{
			throw new NotImplementedException ();
		}

		public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
		{
			base.OnStartCommand (intent, flags, startId);

			var callDetactor = new PhoneCallDetector (this);
			var tm = (TelephonyManager)base.GetSystemService (TelephonyService);
			tm.Listen (callDetactor, PhoneStateListenerFlags.CallState);

			return StartCommandResult.Sticky;
		}
	}

Create Broadcast Receiver

We need to start the service when device finishes booting. Inherit from the BroadcastReceiver class and add the IntentFilter attribute with intent to be notified when boot is completed.

	[BroadcastReceiver]
	[IntentFilter (new[]{Intent.ActionBootCompleted})]
	public class StartupReceiver: BroadcastReceiver
	{
		public override void OnReceive (Context context, Intent intent)
		{
			Intent serviceStart = new Intent (context, typeof(PhoneCallService));
			context.StartService (serviceStart);
		}
	}

UI

For now, UI just needs a message indicating state of our background service and a button to start the service in case it isn’t running already. Following is the code for the main activity.

[Activity (Label = "CallDetector", MainLauncher = true)]
	public class Status: Activity
	{
		Button startButton;
		TextView messageText;

		protected override void OnCreate (Bundle bundle)
		{
			base.OnCreate (bundle);

			SetContentView (Resource.Layout.Main);
		
			startButton = FindViewById<Button> (Resource.Id.startService);
			messageText = FindViewById<TextView> (Resource.Id.statusMessage);
			
			startButton.Click += delegate {
				Intent serviceStart = new Intent (this, typeof(PhoneCallService));
				this.StartService(serviceStart);

				SetUIState();
			};

			SetUIState ();
		}

		private void SetUIState()
		{
			bool serviceRunning = IsServiceRunning();
			startButton.Enabled = !serviceRunning;
			messageText.Text = serviceRunning ? "CallMinder service is running, its monitoring your calls." : "CallMinder service isn't running, please click button to start the srvice.";
		}

		private bool IsServiceRunning()
		{
			ActivityManager activityManager = (ActivityManager)GetSystemService (ActivityService);
			var serviceInstance = activityManager.GetRunningServices(int.MaxValue).ToList().FirstOrDefault(service => service.Service.ShortClassName.Equals("calldetector.PhoneCallService"));
			return serviceInstance != null;
		}
	}

That’s all we need to see it in action. Launch the Android emulator and deploy the application.

Simulate Incoming Calls

You could deploy the app to your phone and make a call from another phone to test the application. However, it would be nice if we could simulate incoming calls within emulator itself during development. Launch another instance of emulator and dial the port number of the emulator on which your application is running. You should be able to see our application detecting the call and showing the notification on emulator as shown in the screenshot below

Permissions

Following are two permissions required for this application. Right click the project and go to Options > Android Application > Require Permissions for selecting these permissions.

_READ_PHONESTATE: This would allow us to listen to the phone state change events _RECEIVE_BOOTCOMPLETED: This would allow us to be notified when device finishes booting

Complete code could be found here: https://github.com/patelsan/CallDetector

Stay tuned, Next part of the post would cover sending the remote notifications.