Skip to content

Instantly share code, notes, and snippets.

@ftvs
Last active October 11, 2023 10:05
Show Gist options
  • Save ftvs/e61ccb039f511eb288ee to your computer and use it in GitHub Desktop.
Save ftvs/e61ccb039f511eb288ee to your computer and use it in GitHub Desktop.
Detecting an incoming call coming to an Android device. Remember to set the appropriate permissions in AndroidManifest.xml as suggested in the Stackoverflow link. Usage example in comments. Source: Gabe Sechan http://stackoverflow.com/a/15564021/264619 Explanation: http://gabesechansoftware.com/is-the-phone-ringing/#more-8
package com.gabesechan.android.reusable.receivers;
import java.util.Date;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
public abstract class PhonecallReceiver extends BroadcastReceiver {
//The receiver will be recreated whenever android feels like it. We need a static variable to remember data between instantiations
private static int lastState = TelephonyManager.CALL_STATE_IDLE;
private static Date callStartTime;
private static boolean isIncoming;
private static String savedNumber; //because the passed incoming is only valid in ringing
@Override
public void onReceive(Context context, Intent intent) {
//We listen to two intents. The new outgoing call only tells us of an outgoing call. We use it to get the number.
if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
}
else{
String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
int state = 0;
if(stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)){
state = TelephonyManager.CALL_STATE_IDLE;
}
else if(stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
state = TelephonyManager.CALL_STATE_OFFHOOK;
}
else if(stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)){
state = TelephonyManager.CALL_STATE_RINGING;
}
onCallStateChanged(context, state, number);
}
}
//Derived classes should override these to respond to specific events of interest
protected void onIncomingCallStarted(Context ctx, String number, Date start){}
protected void onOutgoingCallStarted(Context ctx, String number, Date start){}
protected void onIncomingCallEnded(Context ctx, String number, Date start, Date end){}
protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end){}
protected void onMissedCall(Context ctx, String number, Date start){}
//Deals with actual events
//Incoming call- goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
//Outgoing call- goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
public void onCallStateChanged(Context context, int state, String number) {
if(lastState == state){
//No change, debounce extras
return;
}
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
isIncoming = true;
callStartTime = new Date();
savedNumber = number;
onIncomingCallStarted(context, number, callStartTime);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
//Transition of ringing->offhook are pickups of incoming calls. Nothing done on them
if(lastState != TelephonyManager.CALL_STATE_RINGING){
isIncoming = false;
callStartTime = new Date();
onOutgoingCallStarted(context, savedNumber, callStartTime);
}
break;
case TelephonyManager.CALL_STATE_IDLE:
//Went to idle- this is the end of a call. What type depends on previous state(s)
if(lastState == TelephonyManager.CALL_STATE_RINGING){
//Ring but no pickup- a miss
onMissedCall(context, savedNumber, callStartTime);
}
else if(isIncoming){
onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
}
else{
onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
}
break;
}
lastState = state;
}
}
@shahzadafridi
Copy link

There is no state of answer call. I mean If I call someone and they attend the call then this can be also considered as a state like others (CALL_STATE_OFFHOOK, CALL_STATE_IDLE, CALL_STATE_RINGING ). I don't see such state in all example that exists on the internet. If anyone has info or any reference I would be grateful for that.

@1isten
Copy link

1isten commented Aug 13, 2019

Thank you @ftvs for your nice share! The code works like a charm except for Android 6.0 and above we need to manually popup a dialog to ask the user for the READ_PHONE_STATE permission. For those folks who have trouble implementing this, I've created a repo and the implemented apk: https://github.com/1isten/WhoIsThat/releases/latest, hope that helps.

@catman85
Copy link

Thank you!

@LunevNF
Copy link

LunevNF commented Oct 1, 2019

methods from class CallReceiver never calls (onIncoming and so on) if you don't implement in Manifest class CallReceiver.

@Basit-Iqbal
Copy link

why u guys need it in MainActivity when u already implement in manifest..

@LunevNF
Copy link

LunevNF commented Oct 31, 2019

Maybe for callbacks implementation? Don't think so?

@Basit-Iqbal
Copy link

yup may be..

@LunevNF
Copy link

LunevNF commented Oct 31, 2019

This lib and call_log not interested now, because Google removing apps, if they cannot be used as default phone app.

@Basit-Iqbal
Copy link

how u sure about that becose presently m working on such kind of project?

@LunevNF
Copy link

LunevNF commented Oct 31, 2019

Because we can't publish our app without default installing and with call_log permission. Old apps with this permissions will be removed soon. It's an answer from Google play support team via email. So, we already remove permissions and make call detecting another way. Google play team accepts our app

@Basit-Iqbal
Copy link

what do you think about this app https://play.google.com/store/apps/details?id=com.personlization.magiccall ? it also uses same permissions.

@LunevNF
Copy link

LunevNF commented Oct 31, 2019

If user can replace default incoming call application (with photo, number and accept/decline call buttons), this app will be leave and active. Google not remove your app. If your app cannot replace default incoming call app (for answering call), Google will remove this app from store :(
Put changes to your app for default using in operation system and all be okay

@Basit-Iqbal
Copy link

thank u so much :)

@LunevNF
Copy link

LunevNF commented Oct 31, 2019

Okay, good luck!:)

@ri3tykity
Copy link

I want to read only incoming call number in marshmallow and above. What should I do? My app got rejected by google so I removed the READ_CALL_LOG and everything is OK. But I want to implement the incoming call read in android. Any idea how to build that? Or any suggestion on how to make default calling app in android.
Thanks in advanced.

@LunevNF
Copy link

LunevNF commented Nov 12, 2019

I want to read only incoming call number in marshmallow and above. What should I do? My app got rejected by google so I removed the READ_CALL_LOG and everything is OK. But I want to implement the incoming call read in android. Any idea how to build that? Or any suggestion on how to make default calling app in android.
Thanks in advanced.

Handle incoming calls via accessibility

@ehp006
Copy link

ehp006 commented Feb 27, 2020

Is there a similar method to detect VoIP incoming call start and end in Android programtically ? Thank you

@LunevNF
Copy link

LunevNF commented Feb 27, 2020

Is there a similar method to detect VoIP incoming call start and end in Android programtically ? Thank you

No. VoIP working over Internet and have private protocol. Look info about SIP.

@amrut192
Copy link

In case of Multi SIM (Dual SIM) devices, is there any way to find out which SIM is receiving the incoming call ?

@LunevNF
Copy link

LunevNF commented Jul 31, 2020

This sample not working for multiple calls (second incoming, for example)

@arsalankhan994
Copy link

not working on oreo devices

@LunevNF
Copy link

LunevNF commented Sep 13, 2020

not working on oreo devices

Working

@arsalankhan994
Copy link

not working on oreo devices

Working

have you test this on app kill state?

@Mateeen
Copy link

Mateeen commented Oct 17, 2020

Regarding changes makes on Android Pie, (See https://developer.android.com/about/versions/pie/android-9.0-changes-all#restrict-access-phone-numbers),

You must replace lines 28 - 42 by

TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(new PhoneStateListener() {
     @Override
     public void onCallStateChanged(int state, String phoneNumber) {
          onCustomCallStateChanged(context, state, phoneNumber);
     }
}, PhoneStateListener.LISTEN_CALL_STATE);

And rename onCallStateChanged() method in PhonecallReceiver class by onCustomCallStateChanged()

That worked for me Thanks

@Mateeen
Copy link

Mateeen commented Oct 17, 2020

not working on oreo devices

Working

have you test this on app kill state?

it is not working when app removed from recent or its process killed

@arulwastaken
Copy link

not working on oreo devices

Working

have you test this on app kill state?

it is not working when app removed from recent or its process killed

Run foreground service and put the code inside it

@mxgordon
Copy link

I read something about how android.permission.READ_PHONE_STATE wasn't allowed on non-system apps, is this true? Will this code allow me to procedurally answer calls?

@1isten
Copy link

1isten commented Nov 25, 2020

Regarding changes makes on Android Pie, (See https://developer.android.com/about/versions/pie/android-9.0-changes-all#restrict-access-phone-numbers),

You must replace lines 28 - 42 by

TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(new PhoneStateListener() {
     @Override
     public void onCallStateChanged(int state, String phoneNumber) {
          onCustomCallStateChanged(context, state, phoneNumber);
     }
}, PhoneStateListener.LISTEN_CALL_STATE);

And rename onCallStateChanged() method in PhonecallReceiver class by onCustomCallStateChanged()

Thank you

@KingUttu
Copy link

KingUttu commented Apr 5, 2021

Works all good just give these permissions and ask from user in an activity
android.permission.PROCESS_OUTGOING_CALLS
android.permission.READ_CALL_LOG
android.permission.READ_PHONE_STATE

Also, android.permission.READ_CALL_LOG is used to get phone numbers too no change in code Enjoy

@SouliTaleb
Copy link

for voip, if you are not using mode in_communication then use this to check if voip app in incoming : https://developer.android.com/reference/android/media/AudioManager.html#MODE_IN_COMMUNICATION

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment