Android - How to detect outgoing call is answered

2019-03-16 18:05发布

问题:

Is there any way to detect outgoing call is successfully received or answered ? I am using Intent.ACTION_CALL for dialing a call and PhoneCallListener to find the state of a call when outgoing call answered but I couldn't have been achieving this. Is this possible in android ?

回答1:

After deeply working on this issue, I reached this conclusion:

  1. PhoneStateListener won't work for outgoing calls, it calls OFFHOOK instead of RINGING and OFFHOOK is never called on ANSWER.

  2. Using NotificationListenerService, you can listen to posted notifications related to outgoing calls. You can do something like the below code. The issue here is that I'm not able to get the notification text from some Samsung phones, also the text itself might change a lot from one phone to another. Also it requires API 18 and above.

    public class NotificationListener extends NotificationListenerService {
    
        private String TAG = this.getClass().getSimpleName();
    
        @Override
        public void onNotificationPosted(StatusBarNotification sbn) {
            Log.i(TAG, "Notification Posted");
            Log.i(TAG, sbn.getPackageName() +
                    "\t" + sbn.getNotification().tickerText +
                    "\t" + sbn.getNotification().extras.getString(Notification.EXTRA_TEXT);
    
            Bundle extras = sbn.getNotification().extras;
    
            if ("Ongoing call".equals(extras.getString(Notification.EXTRA_TEXT))) {
                startService(new Intent(this, ZajilService.class).setAction(ZajilService.ACTION_CALL_ANSWERED));
            } else if ("Dialing".equals(extras.getString(Notification.EXTRA_TEXT))) {
                startService(new Intent(this, ZajilService.class).setAction(ZajilService.ACTION_CALL_DIALING));
            }
        }
    
        @Override
        public void onNotificationRemoved(StatusBarNotification sbn) {
            Log.i(TAG, "********** onNotificationRemoved");
            Log.i(TAG, "ID :" + sbn.getId() + "\t" + sbn.getNotification().tickerText + "\t" + sbn.getPackageName());
        }
    }
    
  3. Using AccessibilityService, it is more basic than NotificationListenerService and I think it is supported by all APIs. But also using AccessibilityService, some phones don't publish useful events in case of call Answer. In most phones, an event wil be raised once the call answered, with call duration; Its printout looks like this:

onAccessibilityEvent EventType: TYPE_WINDOW_CONTENT_CHANGED; EventTime: 21715433; PackageName: com.android.incallui; MovementGranularity: 0; Action: 0 [ ClassName: android.widget.TextView; Text: []; ContentDescription: 0 minutes 0 seconds;

onAccessibilityEvent EventType: TYPE_WINDOW_CONTENT_CHANGED; EventTime: 21715533; PackageName: com.android.incallui; MovementGranularity: 0; Action: 0 [ ClassName: android.widget.TextView; Text: []; ContentDescription: 0 minutes 1 seconds;

  1. API 23 has a new class, Call. it has more detailed call states; STATE_ACTIVE. You can replace the default InCallUI of the phone by your own UI through InCallService I didn't try to use this yet, but anyway, it is just limited to API 23, Marshmallow.

As a conclusion, You need to build a solution combined with NotificationListener and AccessibilityService, in order to cover all phone, hopefully.



回答2:

You need to use a combination of RINGING state and OFFHOOK state to figure out if the call is answered.

See this thread for actual code on how to achieve this.



回答3:

I know its been a while but i hope to be helpful for someone still looking for a solution!

I recently had to work on a similar project where i needed to capture the ringing state of an outgoing call and the only way i could find was using the hidden Api of native dial-up App. This would only be possible for android > 5.0 because of the api changes. This was tested on Android 5.0.1, and worked like a charm. (p.s. you would need a rooted device for it to work, because you need to install your app as a System application (google how!) which will then be able to detect the outgoing call states).

For the record, PhoneStateListener doesn't work for detecting the outgoing call states as mentioned in many posts.

First, add this permission in the manifest file,

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

Then define you broadcastreceiver, (here is a sample code!)

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;

public class MyBroadcastReceiver extends BroadcastReceiver
{
    @Override
    public void onReceive(Context context, final Intent intent)
    {
        switch (intent.getIntExtra("foreground_state", -2)) {
            case 0: //  PreciseCallState.PRECISE_CALL_STATE_IDLE:
                System.out.println("IDLE");
                break;
            case 3: //  PreciseCallState.PRECISE_CALL_STATE_DIALING:
                System.out.println("DIALING");
                break;
            case 4: //  PreciseCallState.PRECISE_CALL_STATE_ALERTING:
                System.out.println("ALERTING");
                break;
            case 1: //  PreciseCallState.PRECISE_CALL_STATE_ACTIVE:
                System.out.println("ACTIVE");
                break;
        }

    }
} 

I replaced some of the constants with their values because i saw a lot of confusion among the folks unfamiliar with the concept of reflection (so for ease). Alerting is basically the state when receiver is actually ringing! and that does not include the call setup time which means that the call has been received successfully!. There are other options in precise_call_state class (which i didn't explore), to help you with capturing the call being answered!