10

In my android application, I have created a BroadcastReceiver that detects incoming call; my code is running very well. If there is an incoming call (EXTRA_STATE_RINGING), I can see my incommingNumber in the logcat, also when the user answered the call (EXTRA_STATE_OFFHOOK)

  • I used shared preferences to store incoming number (String) in ringing state, then get it in Off HOOK state.

This is my code, it works perfectly:

 public class IncomingCallReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        
           String incomingNumber = null ;
          if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_RINGING)) 
          {
            // Ringing state
            // Phone number 
              incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
             
              Log.i("test2", incomingNumber);
              
              SharedPreferences myPrefs = context.getSharedPreferences("myPrefs",Context.MODE_WORLD_WRITEABLE);
              SharedPreferences.Editor prefsEditor = myPrefs.edit();
              prefsEditor.putString("Incomingnumber", incomingNumber);
              //Not forgot to commit.
              prefsEditor.commit();

              
          } 
          
          else if  (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) 
          {
              //get the incoming number 
              SharedPreferences myPrefs = context.getSharedPreferences("myPrefs",Context.MODE_WORLD_READABLE);
              String incomNumber = myPrefs.getString("Incomingnumber", incomingNumber);
              
              // This code will execute when the call is answered
              
              Log.i("test2", incomNumber);
              Toast.makeText(context,incomNumber, Toast.LENGTH_LONG).show();
               
          }

BUT: My problem that my code can display a toast when the user just Accept the incoming call.

So, I need to know how I can detect the end of an answered the incoming call to do something else (display an alert dialog or launch an activity)

Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
Zied R.
  • 4,964
  • 2
  • 36
  • 67
  • How to get number in toast in outgoing call disconnect? – Aditya Jun 05 '15 at 13:29
  • Take a look at [this](https://stackoverflow.com/questions/15563921/how-to-detect-incoming-calls-in-an-android-device/15564021#15564021) – amrezzd Aug 20 '18 at 11:02

1 Answers1

27

I've posted this here before- here's my class for detecting incoming calls start and end, outgoing calls start and end, and missed calls.

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.PhoneStateListener;
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
    static PhonecallStartEndDetector listener;
    String outgoingSavedNumber;
    protected Context savedContext;


    @Override
    public void onReceive(Context context, Intent intent) {
        savedContext = context;
        if(listener == null){
            listener = new PhonecallStartEndDetector();
        }

        //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")) {
            listener.setOutgoingNumber(intent.getExtras().getString("android.intent.extra.PHONE_NUMBER"));
            return;
        }

        //The other intent tells us the phone state changed.  Here we set a listener to deal with it
        TelephonyManager telephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); 
        telephony.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
    }

    //Derived classes should override these to respond to specific events of interest
    protected abstract void onIncomingCallStarted(String number, Date start);
    protected abstract void onOutgoingCallStarted(String number, Date start);
    protected abstract void onIncomingCallEnded(String number, Date start, Date end); 
    protected abstract void onOutgoingCallEnded(String number, Date start, Date end);
    protected abstract void onMissedCall(String number, Date start);

    //Deals with actual events
    public class PhonecallStartEndDetector extends PhoneStateListener {
        int lastState = TelephonyManager.CALL_STATE_IDLE;
        Date callStartTime;
        boolean isIncoming;
        String savedNumber;  //because the passed incoming is only valid in ringing

        public PhonecallStartEndDetector() {}

        //The outgoing number is only sent via a separate intent, so we need to store it out of band
        public void setOutgoingNumber(String number){
            savedNumber = number;
        }

        //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
        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            super.onCallStateChanged(state, incomingNumber);
            if(lastState == state){
                //No change, debounce extras
                return;
            }
            switch (state) {
                case TelephonyManager.CALL_STATE_RINGING:
                    isIncoming = true;
                    callStartTime = new Date();
                    savedNumber = incomingNumber;
                    onIncomingCallStarted(incomingNumber, callStartTime);
                    break;
                case TelephonyManager.CALL_STATE_OFFHOOK:
                    //Transition of ringing->offhook are pickups of incoming calls.  Nothing donw on them
                    if(lastState != TelephonyManager.CALL_STATE_RINGING){
                        isIncoming = false;
                        callStartTime = new Date();
                        onOutgoingCallStarted(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(savedNumber, callStartTime);
                    }
                    else if(isIncoming){
                        onIncomingCallEnded(savedNumber, callStartTime, new Date());                        
                    }
                    else{
                        onOutgoingCallEnded(savedNumber, callStartTime, new Date());                                                
                    }
                    break;
            }
            lastState = state;
        }

    }



}
Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127
  • "The receiver will be recreated whenever android feels like it". Why wouldn't that include `static`s? – dacwe Sep 22 '14 at 10:37
  • Static objects are only kicked out of memory when the app itself is. Which is always a possibility, but it only happens in low memory conditions, and only if memory can't be freed in other ways. Meanwhile a separate BroadcastReceiver object is created for each event. So a static will last longer than the Receiver will. If you want to go one extra step, you can log data to the file system but that has its own complications around getting rid of stale data if you're stopped or rebooted. Also a newer version of this code is on my website gabesechansoftware.com with some cleanups. – Gabe Sechan Sep 22 '14 at 20:49
  • Excellent.. Works perfectly. – Santhosh Oct 17 '14 at 04:32
  • @SANTHOSH Can you please help me to receive BroadcastReceiver in my MainActivity? – reegan29 Feb 16 '17 at 15:46
  • This implementation resembles the one from https://stackoverflow.com/questions/15563921/how-to-detect-incoming-calls-in-an-android-device However, the one from this thread contains a bug because of the static listener. The static listener is a non static inner class, which implies that it holds a reference to the outer class. Therefore, once the outer class is recreated (for instance by Android), the new outer class instance will stold hold the same static listener that references to the old outer class instance! – Kristof P. Aug 27 '20 at 07:05