Reputation: 13511
To detect when an outgoing call is answered, I tried creating a PhoneStateListener
and listening for TelephonyManager
's CALL_STATE_RINGING
, CALL_STATE_OFFHOOK
, and CALL_STATE_IDLE
, from this question, but it does not seem to work, as explained below.
First, I registered the following permission in the manifest:
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
Then, a BroadcastReceiver
called OutCallLogger
that catches the NEW_OUTGOING_CALL
event whenever an outgoing call is made:
<receiver android:name=".listener.OutCallLogger">
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
Next, my implementation of OutCallLogger
. I set up a boolean called noCallListenerYet
to avoid attaching a new PhoneStateListener
to the TelephonyManager
whenever onReceive()
is invoked.
public class OutCallLogger extends BroadcastReceiver {
private static boolean noCallListenerYet = true;
@Override
public void onReceive(final Context context, Intent intent) {
number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
if (noCallListenerYet) {
final TelephonyManager tm = (TelephonyManager) context.getSystemService(
Context.TELEPHONY_SERVICE);
tm.listen(new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
Log.d(This.LOG_TAG, "RINGING");
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.d(This.LOG_TAG, "OFFHOOK");
break;
case TelephonyManager.CALL_STATE_IDLE:
Log.d(This.LOG_TAG, "IDLE");
break;
default:
Log.d(This.LOG_TAG, "Default: " + state);
break;
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
noCallListenerYet = false;
}
}
}
Now, when I make an outgoing call in my device, CALL_STATE_RINGING
is NEVER invoked. I always only get printouts of "IDLE" to "OFFHOOK" when the other line starts ringing, nothing when the call is answered, and a printout of "IDLE" again when the call is ended.
How can I reliably detect when an outgoing call is answered in Android, or is that even possible?
Upvotes: 25
Views: 27240
Reputation: 6128
Here your answer is that you have implemented CallStateListener in OutGoingCallReceiver which is wrong. You have to implement CallStateListener in PhoneStateListener
I have also tried this thing in my earlier project, I had faced the same issue, then I solved it like as below. I took 3 classes as below.
AutoCallReceiver: Register the TelephonyManager with PhoneStateListener.LISTEN_CALL_STATE
CallStateListener which listens three states as TelephonyManager.CALL_STATE_IDLE
,TelephonyManager.CALL_STATE_OFFHOOK
,TelephonyManager.CALL_STATE_RINGING
3.OutGoingCallReceiver which handles out going call
public class OutGoingCallReceiver extends BroadcastReceiver {
/* onReceive will execute on out going call */
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "OutGoingCallReceiver", Toast.LENGTH_SHORT).show();
}
}
public class CallStateListener extends PhoneStateListener {
String number=""; // variable for storing incoming/outgoing number
Context mContext; // Application Context
//Constructor that will accept Application context as argument
public CallStateListener(Context context) {
mContext=context;
}
// This function will automatically invoke when call state changed
public void onCallStateChanged(int state,String incomingNumber)
{
boolean end_call_state=false; // this variable when true indicate that call is disconnected
switch(state) {
case TelephonyManager.CALL_STATE_IDLE:
// Handling Call disconnect state after incoming/outgoing call
Toast.makeText(mContext, "idle", Toast.LENGTH_SHORT).show();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
// Handling outgoing call
Toast.makeText(mContext, "OFFHOOK", Toast.LENGTH_SHORT).show();
// saving outgoing call state so that after disconnect idle state can act accordingly
break;
case TelephonyManager.CALL_STATE_RINGING:
Toast.makeText(mContext, "RINGING", Toast.LENGTH_SHORT).show();
break;
}
}
}
public class AutoCallReceiver extends BroadcastReceiver {
/* onReceive will execute on call state change */
@Override
public void onReceive(Context context, Intent intent) {
// Instantiating PhoneStateListener
CallStateListener phoneListener=new CallStateListener(context);
// Instantiating TelephonyManager
TelephonyManager telephony = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
// Registering the telephony to listen CALL STATE change
telephony.listen(phoneListener,PhoneStateListener.LISTEN_CALL_STATE);
}
}
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<application ...>
<receiver android:name=".OutGoingCallReceiver">
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
<receiver android:name=".AutoCallReceiver">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
</application>
Upvotes: -3
Reputation: 310
Since Android 5.0 this is possible for system apps. But you need to use the hidden Android API.
I got it to work like this:
<uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
<receiver android:name=".listener.OutCallLogger">
<intent-filter>
<action android:name="android.intent.action.PRECISE_CALL_STATE" />
</intent-filter>
</receiver>
public class OutCallLogger extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getIntExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, -2) {
case PreciseCallState.PRECISE_CALL_STATE_IDLE:
Log.d(This.LOG_TAG, "IDLE");
break;
case PreciseCallState.PRECISE_CALL_STATE_DIALING:
Log.d(This.LOG_TAG, "DIALING");
break;
case PreciseCallState.PRECISE_CALL_STATE_ALERTING:
Log.d(This.LOG_TAG, "ALERTING");
break;
case PreciseCallState.PRECISE_CALL_STATE_ACTIVE:
Log.d(This.LOG_TAG, "ACTIVE");
break;
}
}
}
You can find all possible call states in PreciseCallState.java and all extras that the intent contains in TelephonyRegistry.java.
Upvotes: 13
Reputation: 448
Please pay your attention at:
tm.listen(new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
Log.d(This.LOG_TAG, "RINGING");
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.d(This.LOG_TAG, "OFFHOOK");
break;
case TelephonyManager.CALL_STATE_IDLE:
Log.d(This.LOG_TAG, "IDLE");
break;
default:
Log.d(This.LOG_TAG, "Default: " + state);
break;
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
Do you see "incomingNumber" argument? Yes, that code just can only detect your phone-call-state when there is an incoming-phone-call to your device.
Upvotes: 0
Reputation: 485
It looks like the RINGING state is reached only by incoming calls. Outgoing calls change from IDLE to OFFHOOK, so looking at the Phone State maybe is not possible to achieve this.
I think that it could be possible using internal functions, look at this: What does the different Call states in the Android telephony stack represent?
Upvotes: 3
Reputation: 13
You could do the following... not very precise but could do the trick:
You use the another receiver for android.intent.action.PHONE_STATE and in the onReceive you do the following:
if (intent.getAction().equals("android.intent.action.PHONE_STATE")) {
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(new PhoneStateListener() {
public void onCallStateChanged(int state, String incomingNumber) {
switch(state) {
case TelephonyManager.CALL_STATE_IDLE:
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
break;
case TelephonyManager.CALL_STATE_RINGING:
break;
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
}
In the CALL_STATE_OFFHOOK case you check that the last stored state was NEW_OUTGOING_CALL and that the no more than aprox. 10 seconds have passed since the last state change. This means that the phone initiated a call at most 10 seconds ago and that now he is in the offhook state (meaning active call) without passing through idle or ringing. This could mean that the call was answered.
Upvotes: -2
Reputation: 1716
Maybe try to use CallManager? Check out http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.3_r1/com/android/internal/telephony/CallManager.java. I also found CallManager.java among the SDK files on my computer. The following text from the linked page seems promising:
Register for getting notifications for change in the Call State Call.State This is
called PreciseCallState because the call state is more precise than the Phone.State
which can be obtained using the android.telephony.PhoneStateListener Resulting events
will have an AsyncResult in Message.obj. AsyncResult.userData will be set to the obj
argument here. The h parameter is held only by a weak reference.
1051
1052 public void registerForPreciseCallStateChanged(Handler h, int what, Object obj){
1053 mPreciseCallStateRegistrants.addUnique(h, what, obj);
1054 }
I haven't tried to code anything, so really don't know if it can do what you want, but I am curious to know.
Upvotes: 0