'Why does phone state listener get fired multiple times for same state for one call?
I am working on fetching recent call log as call get disconnected(outgoing , incoming) either answered or unanswered.
I am using Phone state listener to fire broadcast when call get disconnected but it getting fired multiple time for one call why so..??
So Please tell me how to fire receiver only once for one call.
here is my code
public class BroadcastReceiver extends android.content.BroadcastReceiver{
static boolean iscallended= true;
Context mContext;
TelephonyManager telephony;
private static final String TAG = "CustomBroadcastReceiver";
CustomPhoneStateListener customPhoneStateListener;
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
mContext = context;
Bundle extras = intent.getExtras();
if (extras != null) {
String state = extras.getString(TelephonyManager.EXTRA_STATE);
Log.w("DEBUG", state);
telephony = (TelephonyManager)context.getSystemService(context.TELEPHONY_SERVICE);
if(customPhoneStateListener==null)
{
customPhoneStateListener = new CustomPhoneStateListener();
telephony.listen(customPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
}
}
private class CustomPhoneStateListener extends PhoneStateListener
{
private static final String TAG = "CustomPhoneStateListener";
Handler handler=new Handler();
@Override
public void onCallStateChanged(int state, String incomingNumber) {
// TODO Auto-generated method stub
System.out.println(iscallended+ " value of iscancelled ");
switch (state)
{
case TelephonyManager.CALL_STATE_RINGING:
if(!incomingNumber.equalsIgnoreCase(""))
{
//YOUR CODE HERE
}
break;
case TelephonyManager.CALL_STATE_IDLE:
if(iscallended)
{
iscallended = false;
System.out.println("IDLE called");
Toast.makeText(mContext, "IDLE", Toast.LENGTH_SHORT).show();
Intent it = new Intent(mContext,MainActivity.class);
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(it);
}
break;
default:
break;
}
super.onCallStateChanged(state, incomingNumber);
telephony.listen(customPhoneStateListener, PhoneStateListener.LISTEN_NONE);
}
}
}
Here's receiver in manifest
<receiver android:name="com.example.calllogs.BroadcastReceiver">
<intent-filter >
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
</receiver>
Solution 1:[1]
Here is your answer :
https://stackoverflow.com/a/5497316/3479012
also specify permission in your manifest to access phone state.
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
also have a look at this http://karanbalkar.com/2014/02/detect-incoming-call-and-call-hangup-event-in-android/
Solution 2:[2]
Yes you will get that.
private class MyPhoneStateListener extends PhoneStateListener {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
//Hangup
case TelephonyManager.CALL_STATE_IDLE:
Log.d("IDLE", "Call Idle" + state);
if (isCallEnded) {
isCallEnded = false;
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity());
.setCancelable(false)
.setPositiveButton(getString(R.string.Yes), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
// your method
}
})
.setNegativeButton(getString(R.string.No), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
alertBuilder.show();
}
break;
//Outgoing
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.d("OFFHOOK", "Call OffHook" + state);
isCallEnded = true;
break;
//Incoming
case TelephonyManager.CALL_STATE_RINGING:
Log.d("RINGING", "Call Ringing" + state);
break;
}
}
}
The above is sample, when you look at the device log you'll definitely see offhook multiple times as well as IDLE state.
Try to calculate that, it should be okay.
Solution 3:[3]
To avoid repetitive triggering, use MyPhoneStateListener
as object and check lastCallState
. Call makeMyPhoneStateListener
from ServiceReceiver
.
class MyPhoneStateListener () : PhoneStateListener() {
companion object {
var lastCallState: Int? = null
lateinit var context: Context
fun makeMyPhoneStateListener(_context: Context): MyPhoneStateListener
{
val myPhoneStateListener = MyPhoneStateListener()
context = _context
return myPhoneStateListener
}
}
override fun onCallStateChanged(state: Int, incomingNumber: String) {
when (state) {
TelephonyManager.CALL_STATE_IDLE -> {
if (lastCallState!= TelephonyManager.CALL_STATE_IDLE){
// some code for CALL_STATE_IDLE
lastCallState = TelephonyManager.CALL_STATE_IDLE
}
}
TelephonyManager.CALL_STATE_OFFHOOK -> {
if (lastCallState!= TelephonyManager.CALL_STATE_OFFHOOK) {
// some code for CALL_STATE_OFFHOOK
lastCallState = TelephonyManager.CALL_STATE_OFFHOOK
}
}
TelephonyManager.CALL_STATE_RINGING -> {
if (lastCallState!= TelephonyManager.CALL_STATE_RINGING) {
// some code for CALL_STATE_RINGING
lastCallState = TelephonyManager.CALL_STATE_RINGING
}
}
}
}
Solution 4:[4]
I have overcame this problem using this simple trick
In your receiver class
public class Receiver extends BroadcastReceiver {
public static final String TAG = "MADARA";
private static boolean callAnswered = false;
private static boolean callRinging = false;
private static boolean callEnded = false;
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.PHONE_STATE")){
if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
if (!callAnswered){
Log.d(TAG, "Call answered");
callAnswered = true;
new Handler(Looper.getMainLooper()).postDelayed(() -> callAnswered = false, 2000);
}
}
else if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_IDLE)) {
if (!callEnded){
Log.d(TAG, "Call ended");
callEnded = true;
new Handler(Looper.getMainLooper()).postDelayed(() -> callEnded = false, 2000);
}
}
else if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_RINGING)) {
if (!callRinging){
Log.d(TAG, "Call Ringing");
callRinging = true;
new Handler(Looper.getMainLooper()).postDelayed(() -> callRinging = false, 2000);
}
}
}
}
}
Solution 5:[5]
you will find the below description in Android Devlopers documnetation here
Broadcast intent action indicating that the call state on the device has changed.
The EXTRA_STATE extra indicates the new call state. If a receiving app has Manifest.permission.READ_CALL_LOG permission, a second extra EXTRA_INCOMING_NUMBER provides the phone number for incoming and outgoing calls as a String.
If the receiving app has Manifest.permission.READ_CALL_LOG and Manifest.permission.READ_PHONE_STATE permission, it will receive the broadcast twice; one with the EXTRA_INCOMING_NUMBER populated with the phone number, and another with it blank. Due to the nature of broadcasts, you cannot assume the order in which these broadcasts will arrive, however you are guaranteed to receive two in this case. Apps which are interested in the EXTRA_INCOMING_NUMBER can ignore the broadcasts where EXTRA_INCOMING_NUMBER is not present in the extras (e.g. where Intent#hasExtra(String) returns false).
Now listener fired twice one for call log permission and the other for phone state permission, only one of them will return with phone number and the other will be Null.
So, you should first check if phone number returen empty or not like this
String stateString = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
String savedNumber = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
Now You should first check if savedNumber is null or not
Here is full onReceive method
private static String savedNumber;
int state;
Context ctx;
@Override
public void onReceive(Context context, @NonNull Intent intent) {
Log.d("TAG101", "onReceive: " + intent.getAction().toString());
ctx = context;
state = -1;
if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
} else {
String stateString = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
savedNumber = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
if(savedNumber != null) {
if (stateString.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
state = TelephonyManager.CALL_STATE_OFFHOOK;
} else if (stateString.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
state = TelephonyManager.CALL_STATE_IDLE;
if (state == 0) {
onCallEnded();
}
} else if (stateString.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
state = TelephonyManager.CALL_STATE_RINGING;
}
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Community |
Solution 2 | EngineSense |
Solution 3 | David Buck |
Solution 4 | Nur Alam |
Solution 5 | Mohamed Ahmed |