24

I managed to prepare an activity when the phone is ringing. Now I need to know how to cancel this activity, when I answer the phone or I reject the call.Do I call EXTRA_STATE_IDLE or EXTRA_STATE_OFFHOOK ?

Any ideas?

Manifest

    <receiver android:name=".IncomingBroadcastReceiver">
        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE" />
        </intent-filter>
    </receiver>

IncomingBroadcastReceiver java Class

public class IncomingBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
        // If an incoming call arrives
        if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) { //Did my work }
Dinith Rukshan Kumara
  • 638
  • 2
  • 10
  • 19
user1163234
  • 2,407
  • 6
  • 35
  • 63

6 Answers6

27

The above answer is completely wrong in case of outgoing calls. In Android there is no way by which one detect whether the call was actually answered (in case of outgoing calls). The moment you dial a number, the off_hook state is fired. This is one of the drawbacks of Android programming.

frogatto
  • 28,539
  • 11
  • 83
  • 129
kkreddy
  • 297
  • 3
  • 2
  • True! Discovered the same when playing around with the BroadcastReceivers. – nr1 Jun 12 '15 at 17:40
  • 3
    "*In android there is no way by which one detect whether the call was actually answered (in case of outgoing calls).*" -- then how does stock dialer detects/show the duration? Try this at your convenience: make a call to another number for 20 seconds and end the call at your end. For the last dialed number, your dialer should show 0min0secs duration. Now make a call again but pick up the call at another end for 5-10 seconds. End the call at your end and your **dialer would show the duration the call was picked up for**! – user47 Dec 09 '15 at 09:26
  • Yes, I have tested this thoroughly in my app www.callrecorder.cc/app There is no way to detect if an outgoing call is answered or not as of now. As soon as a number is dialled, phone goes to the OFF_HOOK state. – Darshan Gowda Nov 21 '16 at 17:16
  • 1
    You can setup a timer, starts from `off_hook` event. Count +30 seconds, and if call didn't end, you can safely assume it is answered (and terminate it if you want, like this http://stackoverflow.com/a/8380418/3441905) – FindOut_Quran Nov 26 '16 at 13:37
  • 1
    @Firelord, the stock dialer makes use of hidden PreciseCallState https://github.com/android/platform_frameworks_base/blob/master/telephony/java/android/telephony/PreciseCallState.java – swooby Apr 07 '17 at 08:08
22

in your onReceive:

PhoneStateChangeListener pscl = new PhoneStateChangeListener();
TelephonyManager tm = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(pscl, PhoneStateListener.LISTEN_CALL_STATE);

separate class:

private class PhoneStateChangeListener extends PhoneStateListener {
    public static boolean wasRinging;
    String LOG_TAG = "PhoneListener";
    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        switch(state){
            case TelephonyManager.CALL_STATE_RINGING:
                 Log.i(LOG_TAG, "RINGING");
                 wasRinging = true;
                 break;
            case TelephonyManager.CALL_STATE_OFFHOOK:
                 Log.i(LOG_TAG, "OFFHOOK");

                 if (!wasRinging) {
                     // Start your new activity
                 } else {
                     // Cancel your old activity
                 }

                 // this should be the last piece of code before the break
                 wasRinging = true;
                 break;
            case TelephonyManager.CALL_STATE_IDLE:
                 Log.i(LOG_TAG, "IDLE");
                 // this should be the last piece of code before the break
                 wasRinging = false;
                 break;
        }
    }
}

All you need to do is write some code to check if the previous state was 'ringing'. If the current state is idle and the previous state was ringing, they cancelled the call. If the current state is offhook and the previous state was ringing, they answered the call.

Andreas
  • 2,007
  • 5
  • 26
  • 37
  • Thanks this looks like the correct answer! I am a rookie at this...How do I check previous state of the phone?Thanks for your time!!! – user1163234 Mar 13 '12 at 14:15
  • 1
    just add the code you need to OFFHOOK and IDLE. If you either pick up the phone (OFFHOOK) or reject the call (IDLE), the code will be executed. No need to check for a previous state: the default state of the phone is idle, so until someone calls, it won't change (i'm assuming that the activity starts when someone calls). Just make sure that you check if your activity is still active for the CALL_STATE_IDLE, since it could already have been closed if you picked up the phone, then closed the call. – Andreas Mar 13 '12 at 14:28
  • Thanks! I have an issue...I need to raise a new activity when a new outgoing call is issued.Where can I do this in the onCallStateChanged? – user1163234 Mar 13 '12 at 15:55
  • 3
    One more question What about an outgoing call that is answered? – user1163234 Mar 13 '12 at 17:47
  • so I put my code for awnsering the call in " if (!wasRinging) { // Start your new activity }"??? – user1163234 Mar 14 '12 at 08:12
  • right now I am using offhook to detect an outgoing call also how can I implement both? – user1163234 Mar 14 '12 at 08:28
  • yes, you put the code for starting your new activity in the `if (!wasRinging) { // Start your new activity }`. If your phone was not previously ringing (i.e. outgoing call), it'll start that activity. If you want to use the code for the incoming call (i.e. cancel your other activity), put it in the `else { }` – Andreas Mar 14 '12 at 08:43
  • please take alook at my new question http://stackoverflow.com/questions/9705314/how-to-get-outgoing-call-is-answered-in-android Thanks!!! – user1163234 Mar 14 '12 at 15:52
  • 1
    the last bit of code in `case TelephonyManager.CALL_STATE_OFFHOOK`, should it be `wasRinging = false;` instead? – Duc Apr 27 '13 at 10:12
  • And how to get a phone number in case of a rejected call? –  Dec 31 '15 at 10:57
  • @jawanam I believe this is what you'll need: http://developer.android.com/reference/android/telephony/PhoneNumberUtils.html#getNumberFromIntent%28android.content.Intent,%20android.content.Context%29 – Andreas Jan 04 '16 at 07:59
  • not working. if the call is answered or rejected, onCallStateChanged is not called, therefore none of event will be called. Any idea or fix for this? thanks – ralphgabb Jul 29 '16 at 06:19
7

Following are the states which it goes through in different scenarios:

1) Answering Received call

CALL_STATE_RINGING => CALL_STATE_OFFHOOK (After Answering call) => CALL_STATE_IDLE (After End call) 

2) Rejecting / Not Answering (Missed) Received call

CALL_STATE_RINGING => CALL_STATE_IDLE (After End call)  

3) Dialing call

CALL_STATE_OFFHOOK (After dialing) => CALL_STATE_IDLE (After End call) 

Code

  int prev_state=0;


  public class CustomPhoneStateListener extends PhoneStateListener {  

        private static final String TAG = "CustomPhoneStateListener";  

        @Override  
        public void onCallStateChanged(int state, String incomingNumber){  

            if(incomingNumber!=null&&incomingNumber.length()>0) incoming_nr=incomingNumber;   

            switch(state){  
                case TelephonyManager.CALL_STATE_RINGING:  
                        Log.d(TAG, "CALL_STATE_RINGING");  
                        prev_state=state;  
                        break;  
                case TelephonyManager.CALL_STATE_OFFHOOK:  
                Log.d(TAG, "CALL_STATE_OFFHOOK");  
                prev_state=state;  
                break;  
                case TelephonyManager.CALL_STATE_IDLE:  
                    Log.d(TAG, "CALL_STATE_IDLE==>"+incoming_nr);  
                    NumberDatabase database=new NumberDatabase(mContext);  
                    if((prev_state==TelephonyManager.CALL_STATE_OFFHOOK)){  
                        prev_state=state;  
                        //Answered Call which is ended  
                    }  
                    if((prev_state==TelephonyManager.CALL_STATE_RINGING)){  
                        prev_state=state;  
                        //Rejected or Missed call  
                    }  
                    break;  

            }  
        }  
    }  

In your receiver

onReceive(Context context, Intent intent) {  
        TelephonyManager telephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); //TelephonyManager object  
        CustomPhoneStateListener customPhoneListener = new CustomPhoneStateListener();  
        telephony.listen(customPhoneListener, PhoneStateListener.LISTEN_CALL_STATE);          //Register our listener with TelephonyManager  

        Bundle bundle = intent.getExtras();  
        String phoneNr= bundle.getString("incoming_number");  

        mContext=context;  
 }  
Nigam Patro
  • 2,760
  • 1
  • 18
  • 33
Karthi
  • 13,624
  • 10
  • 53
  • 76
  • 2
    Tnaks! Any way I can find out how do I know if an outgoing call is answered? – user1163234 Mar 18 '12 at 10:01
  • 19
    This is wrong. CALL_STATE_OFFHOOK gets called immediately when the phone is dialed. Seriously, how many of you have posted this in stackoverflow without even testing this? – Johann Mar 05 '13 at 14:46
  • [Please read this carefully](http://stackoverflow.com/questions/2250455/detect-if-an-outgoing-call-has-been-answered) – Harpreet Jul 08 '13 at 13:26
  • @AndroidDev did you find a solution for this – ralphgabb Jul 29 '16 at 06:22
3

below is a code of detecting outgoing call by accessibility events -

Add a class which extends AccessibilityService in your projects -

public class CallDetection extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
     acquireLock(this);
    Log.d("myaccess","after lock");
    if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
        Log.d("myaccess","in window changed");
        AccessibilityNodeInfo info = event.getSource();
        if (info != null && info.getText() != null) {
            String duration = info.getText().toString();
            String zeroSeconds = String.format("%02d:%02d", new Object[]{Integer.valueOf(0), Integer.valueOf(0)});
            String firstSecond = String.format("%02d:%02d", new Object[]{Integer.valueOf(0), Integer.valueOf(1)});
            Log.d("myaccess","after calculation - "+ zeroSeconds + " --- "+ firstSecond + " --- " + duration);
            if (zeroSeconds.equals(duration) || firstSecond.equals(duration)) {
                Toast.makeText(getApplicationContext(),"Call answered",Toast.LENGTH_SHORT).show();
               // Your Code goes here
            }
            info.recycle();
        }
    }
}


@Override
protected void onServiceConnected() {
    super.onServiceConnected();
    Toast.makeText(this,"Service connected",Toast.LENGTH_SHORT).show();
    AccessibilityServiceInfo info = new AccessibilityServiceInfo();
    info.eventTypes = AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
    info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
    info.notificationTimeout = 0;
    info.packageNames = null;
    setServiceInfo(info);
}

@Override
public void onInterrupt() {

}
}

But to get the function event.getSource() working you have to specify some of your service configuration through xml, so create a xml folder in your project and add a xml file called serviceconfig.xml (you can give any name you want.

The content of serviceconfig is below -

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/callDetection"
android:accessibilityEventTypes="typeWindowContentChanged"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
/>

You can find more about serviceconfig in Here

Now add your service in you Manifest file like this -

<service android:name=".CallDetection"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
        android:label="@string/callDetection">
        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService" />
        </intent-filter>
        <meta-data
            android:name="android.accessibilityservice"
            android:resource="@xml/serviceconfig" />
</service>

And youre done, just run the app and go to Accessibility settings in your phone, you will find an option named as detection (or whatever name you have given as your service description), switch that on to give accesibility permissions for you app.

Now you will see a toast when call is answered.

you can Code any code you want in there, also you can call a callback function in your activity

Most important - Dont call your call window(android dialer window) untill the call is answered, otherwise this will not work.

Note - As android doesn't provide any solution to detect if the call is answered or not, this is the best alternative i have made, hope it works for you.

kaustav07
  • 284
  • 3
  • 17
  • Could you please add an example showing how to apply your solution in the question's specific case? As it stands, your answer is little more than a comment with a link to another question's answer. – Fred Gandt Jul 11 '17 at 22:45
  • previously i added the whole code and example to more than one answer, but one moderator from stackoverflow removed my answer saying not to add duplicate ans and thats why i added the link here and actual ans to one question only. – kaustav07 Jul 13 '17 at 16:33
  • I believe _duplicate answers_ are only considered a problem if they are **exact** duplicates. Since there are differences between the questions, and there should therefore be differences in the answers, I personally (as a lowly commoner) would prefer to see complete and context relevant answers instead of (potentially breakable) links to somewhat relevant answers elsewhere. – Fred Gandt Jul 13 '17 at 22:35
  • added full answer – kaustav07 Jul 18 '17 at 15:42
  • This looks promising. – Sharp Edge Oct 22 '17 at 16:07
0
//
public class myService extends InCallService 
{
    // Here... :)
    @Override public void onCanAddCallChanged(boolean canAddCall) 
    {
        super.onCanAddCallChanged(canAddCall);  
    }
}
armagedescu
  • 1,758
  • 2
  • 20
  • 31
0

To detect that a call is received, you can detect a "hello" voice. "hello" voice is the frequency (voice activity) outside of Call progress Frequency. For reference you can have a look at this datasheet part: https://www.cmlmicro.com/products/call-progress-and-voice-detector/