How to detect incoming calls, in an Android device?

I’m trying to make an app like, when a call comes to the phone I want to detect the number. Below is what I tried, but it’s not detecting incoming calls.

I want to run my MainActivity in background, how can I do that?

  • Activity.showDialog when activity may not be in foreground
  • Android SQLite select * from table where name like %key% using prepared statements
  • nokiax reports java.lang.RuntimeException: native typeface cannot be made
  • Share an image with a content provider in Android app
  • Adding header to all request with Retrofit 2
  • Accessing Android Contact Group Names
  • I had given the permission in manifest file.

    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    

    Is there anything else should I provide in the manifest?

    public class MainActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.test_layout);
       }
    
       public class myPhoneStateChangeListener extends PhoneStateListener {
           @Override
           public void onCallStateChanged(int state, String incomingNumber) {
               super.onCallStateChanged(state, incomingNumber);
               if (state == TelephonyManager.CALL_STATE_RINGING) {
                   String phoneNumber =   incomingNumber;
               }
           }
       }
    }
    

    Related posts:

    Android: Custom Spinner Layout
    android: AbsListView.OnScrollListener SCROLL_STATE_IDLE is not called after SCROLL_STATE_TOUCH_SCROL...
    Android - Display a video thumbnail from a URL
    FileProvider throws exception on GetUriForFile
    Replace common path parameters in retrofit 2 with okhttp
    DeadObjectException with stacktrace pointing to : android.app.ApplicationPackageManager.getPackageIn...
  • Can't generate APK Release because of GCM SenderId Android
  • Can I use WPF's Full-screen UI on Xamarin?
  • How to add Android Support Repository to Android Studio?
  • EditText without auto-correction, etc
  • setCurrentItem in ViewPager not scroll immediately in the correct position
  • Integrate ZXing in Android Studio
  • 5 Solutions collect form web for “How to detect incoming calls, in an Android device?”

    Here’s what I use to do this:

    Manifest:

    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
    
    <!--This part is inside the application-->
        <receiver android:name=".CallReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
            </intent-filter>
        </receiver>
    

    My base reusable call detector

    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 abstract void onIncomingCallReceived(Context ctx, String number, Date start);
        protected abstract void onIncomingCallAnswered(Context ctx, String number, Date start);
        protected abstract void onIncomingCallEnded(Context ctx, String number, Date start, Date end);
    
        protected abstract void onOutgoingCallStarted(Context ctx, String number, Date start);      
        protected abstract void onOutgoingCallEnded(Context ctx, String number, Date start, Date end);
    
        protected abstract 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;
                    onIncomingCallReceived(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);                     
                    }
                    else
                    {
                        isIncoming = true;
                        callStartTime = new Date();
                        onIncomingCallAnswered(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;
        }
    }
    

    Then to use it, simply derive a class from it and implement a few easy functions, whichever call types you care about:

    public class CallReceiver extends PhonecallReceiver {
    
        @Override
        protected void onIncomingCallReceived(Context ctx, String number, Date start)
        {
            //
        }
    
        @Override
        protected void onIncomingCallAnswered(Context ctx, String number, Date start)
        {
            //
        }
    
        @Override
        protected void onIncomingCallEnded(Context ctx, String number, Date start, Date end)
        {
            //
        }
    
        @Override
        protected void onOutgoingCallStarted(Context ctx, String number, Date start)
        {
            //
        } 
    
        @Override 
        protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end)
        {
            //
        }
    
        @Override
        protected void onMissedCall(Context ctx, String number, Date start)
        {
            //
        }
    
    }
    

    In addition you can see a writeup I did on why the code is like it is on my blog. Gist link: https://gist.github.com/ftvs/e61ccb039f511eb288ee

    EDIT: Updated to simpler code, as I’ve reworked the class for my own use

    private MyPhoneStateListener phoneStateListener = new MyPhoneStateListener();
    

    to register

    TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
    telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
    

    and to unregister

    TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
    telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
    

    this may helps you and also add require permision

    public class PhoneListener extends PhoneStateListener
    {
        private Context context;
        public static String getincomno;
    
        public PhoneListener(Context c) {
            Log.i("CallRecorder", "PhoneListener constructor");
            context = c;
        }
    
        public void onCallStateChanged (int state, String incomingNumber)
        {
    
            if(!TextUtils.isEmpty(incomingNumber)){
            // here for Outgoing number make null to get incoming number
            CallBroadcastReceiver.numberToCall = null;
            getincomno = incomingNumber;
            }
    
            switch (state) {
            case TelephonyManager.CALL_STATE_IDLE:
    
                break;
            case TelephonyManager.CALL_STATE_RINGING:
                Log.d("CallRecorder", "CALL_STATE_RINGING");
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK:
    
                break;
            }
        }
    }
    

    @Gabe Sechan, thanks for your code. It works fine except the onOutgoingCallEnded(). It is never executed. Testing phones are Samsung S5 & Trendy. There are 2 bugs I think.

    1st: a pair of brackets is missing.

    //....
    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{ // this one is missing
                    if(isIncoming){
                          onIncomingCallEnded(context, savedNumber, callStartTime, new Date());                       
                    }
                    else{
                          onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());                                               
                    }
                }// this one is missing
                break;
    //..
    

    2nd: “lastState” is not updated by the “state” if it is at the end of the function. It should be replaced to the first line of this function by

    public void onCallStateChanged(Context context, int state, String number) {
        int lastStateTemp = lastState;
        lastState = state;
        // todo replace all the "lastState" by lastStateTemp from here.
        if(lastStateTemp  == state){
            //No change, debounce extras
            return;
        }
        //....
    

    Additional I’ve put “lastState” and “savedNumber” into shared preference as you suggested.

    Just tested it with above changes. Bug fixed at least on my phones.

    Here is a simple method which can avoid the use of PhonestateListener and other complications.
    So here we are receiving the 3 events from android such as RINGING,OFFHOOK and IDLE. And in order to get the all possible state of call,we need to define our own states like RINGING, OFFHOOK, IDLE, FIRST_CALL_RINGING, SECOND_CALL_RINGING.
    It can handle every states in a phone call.
    Please think in a way that we are receiving events from android and we will define our on call states. See the code.

    public class CallListening  extends BroadcastReceiver {
        private static final String TAG ="broadcast_intent";
        public static String incoming_number;
        private String current_state,previus_state,event;
        public static Boolean dialog= false;
        private Context context;
        private SharedPreferences sp,sp1;
        private SharedPreferences.Editor spEditor,spEditor1;
        public void onReceive(Context context, Intent intent) {
            //Log.d("intent_log", "Intent" + intent);
            dialog=true;
            this.context = context;
            event = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
            incoming_number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
            Log.d(TAG, "The received event : "+event+", incoming_number : " + incoming_number);
            previus_state = getCallState(context);
            current_state = "IDLE";
            if(incoming_number!=null){
                updateIncomingNumber(incoming_number,context);
            }else {
                incoming_number=getIncomingNumber(context);
            }
            switch (event) {
                case "RINGING":
                    Log.d(TAG, "State : Ringing, incoming_number : " + incoming_number);
                if((previus_state.equals("IDLE")) || (previus_state.equals("FIRST_CALL_RINGING"))){
                        current_state ="FIRST_CALL_RINGING";
                    }
                    if((previus_state.equals("OFFHOOK"))||(previus_state.equals("SECOND_CALL_RINGING"))){
                        current_state = "SECOND_CALL_RINGING";
                    }
    
                    break;
                case "OFFHOOK":
                    Log.d(TAG, "State : offhook, incoming_number : " + incoming_number);
                    if((previus_state.equals("IDLE")) ||(previus_state.equals("FIRST_CALL_RINGING")) || previus_state.equals("OFFHOOK")){
                        current_state = "OFFHOOK";
                    }
                    if(previus_state.equals("SECOND_CALL_RINGING")){
                        current_state ="OFFHOOK";
                        startDialog(context);
                    }
                    break;
                case "IDLE":
                    Log.d(TAG, "State : idle and  incoming_number : " + incoming_number);
                    if((previus_state.equals("OFFHOOK")) || (previus_state.equals("SECOND_CALL_RINGING")) || (previus_state.equals("IDLE"))){
                        current_state="IDLE";
                    }
                    if(previus_state.equals("FIRST_CALL_RINGING")){
                        current_state = "IDLE";
                        startDialog(context);
                    }
                    updateIncomingNumber("no_number",context);
                    Log.d(TAG,"stored incoming number flushed");
                    break;
            }
            if(!current_state.equals(previus_state)){
                Log.d(TAG, "Updating  state from "+previus_state +" to "+current_state);
                updateCallState(current_state,context);
    
            }
        }
        public void startDialog(Context context) {
            Log.d(TAG,"Starting Dialog box");
            Intent intent1 = new Intent(context, NotifyHangup.class);
            intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent1);
    
        }
        public void updateCallState(String state,Context context){
            sp = PreferenceManager.getDefaultSharedPreferences(context);
            spEditor = sp.edit();
            spEditor.putString("call_state", state);
            spEditor.commit();
            Log.d(TAG, "state updated");
    
        }
        public void updateIncomingNumber(String inc_num,Context context){
            sp = PreferenceManager.getDefaultSharedPreferences(context);
            spEditor = sp.edit();
            spEditor.putString("inc_num", inc_num);
            spEditor.commit();
            Log.d(TAG, "incoming number updated");
        }
        public String getCallState(Context context){
            sp1 = PreferenceManager.getDefaultSharedPreferences(context);
            String st =sp1.getString("call_state", "IDLE");
            Log.d(TAG,"get previous state as :"+st);
            return st;
        }
        public String getIncomingNumber(Context context){
            sp1 = PreferenceManager.getDefaultSharedPreferences(context);
            String st =sp1.getString("inc_num", "no_num");
            Log.d(TAG,"get incoming number as :"+st);
            return st;
        }
    }
    Android Babe is a Google Android Fan, All about Android Phones, Android Wear, Android Dev and Android Games Apps and so on.