My app can't read incoming number when API is below 21. How can I make it work? I tried this approach: http://mmarvick.github.io/blog/blog/lollipop-multiple-broadcastreceiver-call-state/ but doesn't work
Code:
public class PhoneStateBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "PhoneStateBroadcast";
Context mContext;
String incoming_nr;
private static int prev_state;
static CustomPhoneStateListener customPhoneListener;
#Override
public void onReceive(Context context, Intent intent) {
TelephonyManager telephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); //TelephonyManager object
if(customPhoneListener == null){
customPhoneListener = new CustomPhoneStateListener();
telephony.listen(customPhoneListener, PhoneStateListener.LISTEN_CALL_STATE);
}
Bundle bundle = intent.getExtras();
String phoneNr = bundle.getString("incoming_number");
incoming_nr = phoneNr;
Log.v(TAG, "phoneNr: " + phoneNr);
mContext = context;
}
public class CustomPhoneStateListener extends PhoneStateListener {
private static final String TAG = "CustomPhoneStateListnr";
#Override
public void onCallStateChanged(int state, String incomingNumber){
if(incomingNumber != null && incomingNumber.length() > 0){
incoming_nr = incomingNumber;
}
//if(prev_state == state) return;
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);
if(prev_state == TelephonyManager.CALL_STATE_OFFHOOK){
Intent i = new Intent(mContext, PostCallPromptActivity.class);
i.putExtra(Constants.EXTRA_CALL_LOG_NUMBER, incomingNumber);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(i);
prev_state = state;
//Answered Call which is ended
}
if((prev_state == TelephonyManager.CALL_STATE_RINGING)){
prev_state = state;
//Rejected or Missed call
}
break;
}
}
}
}
Related
Here I'm Calling PhoneStateChangeListener class in onCreate method.
the code is:
PhoneStateChangeListener pscl = new PhoneStateChangeListener(MainActivity.this,true);
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(pscl, PhoneStateListener.LISTEN_CALL_STATE);
PhoneStateChangeListener class is:
class PhoneStateChangeListener extends PhoneStateListener {
Context context;
Boolean doRecording=false;
public PhoneStateChangeListener(Context context,Boolean doRecording) {
this.context = context;
this.doRecording=doRecording;
}
public void onCallStateChanged(int state, String incomingNumber) {
Log.d("CallRecorder", "PhoneListener::onCallStateChanged state:" + state + " incomingNumber:" + incomingNumber);
//Intent callIntent = new Intent(context, RecordService.class);
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
Log.d("CallRecorder", "CALL_STATE_IDLE, stoping recording");
Boolean stopped = context.stopService(new Intent(context, RecordService.class));
Log.e("CallRecorder", "CALL_STATE_IDLE starting recording---------------------------------"+isMyServiceRunning((getClass())));
Log.i("CallRecorder", "stopService for RecordService returned " + stopped);
break;
case TelephonyManager.CALL_STATE_RINGING:
Log.d("CallRecorder", "CALL_STATE_RINGING");
doRecording=false;
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
if(doRecording) {
Log.d("CallRecorder", "CALL_STATE_OFFHOOK starting recording");
Log.e("CallRecorder", "CALL_STATE_OFFHOOK starting recording---------------------------------" + isMyServiceRunning((getClass())));
Intent callIntent = new Intent(context, RecordService.class);
ComponentName name = context.startService(callIntent);
if (null == name) {
Log.e("CallRecorder", "startService for RecordService returned null ComponentName");
} else {
Log.i("CallRecorder", "startService returned " + name.flattenToString());
}
}
break;
}
}
}
The phonestatechangelistener is working well below nougat, Issue is when a call starts it calls CALL_STATE_OFFHOOK and the service gets started but when the call is ended it doesn't call CALL_STATE_IDLE and thus service is not stopped this problem is there above marshmallow.
Just deactivate your PhoneStateListener when your call ends:
private void deactivatePhoneStateListener(){
if(mPhoneStateListener != null){
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
}
mPhoneStateListener = null;
}
Activate it again whenever you need it:
private void activatePhoneStateListener(Context context){
mPhoneStateListener = new MyPhoneStateListener();
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
I have a BroadcastReceiver that listens for the calls and when they end the AlertDialog appears. On Android 5.1 and lower it works all well, but on Android 6.0 (Marshmallow) and higher after some time when someone calls dialog does not appear after the call. It shows when app is manually started again and if there was more than one call - all the dialogs that should have been shown immediately are showed at once which is not good.
Anyone know what this problem is about?
code:
public class PhoneStateBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "PhoneStateBroadcast";
Context mContext;
private static String incoming_nr;
private static int prev_state;
static CustomPhoneStateListener customPhoneListener;
#Override
public void onReceive(Context context, Intent intent) {
if (ContextCompat.checkSelfPermission(
context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED){
TelephonyManager telephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); //TelephonyManager object
if(customPhoneListener == null){
customPhoneListener = new CustomPhoneStateListener();
telephony.listen(customPhoneListener, PhoneStateListener.LISTEN_CALL_STATE);
}
}
if(Intent.ACTION_NEW_OUTGOING_CALL.equals(intent.getAction())){
incoming_nr = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
} else {
Bundle bundle = intent.getExtras();
String phoneNr = bundle.getString("incoming_number");
if(phoneNr != null)incoming_nr = phoneNr;
}
Log.v(TAG, "phoneNr: " + incoming_nr);
mContext = context;
}
public class CustomPhoneStateListener extends PhoneStateListener {
private static final String TAG = "CustomPhoneStateListnr";
#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);
if(prev_state == TelephonyManager.CALL_STATE_OFFHOOK){
Realm realm = Realm.getDefaultInstance();
Contact contact = realm.where(Contact.class)
.contains(Constants.EXTRA_CONTACT_NUMBER, incoming_nr)
.findFirst();
if(contact != null && !contact.getPopupFlag())
break;
Intent i = new Intent(mContext, PostCallPromptActivity.class);
i.putExtra(Constants.EXTRA_CALL_LOG_NUMBER, incoming_nr);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(i);
prev_state = state;
//Answered Call which is ended
}
if((prev_state == TelephonyManager.CALL_STATE_RINGING)){
prev_state = state;
//Rejected or Missed call
}
break;
}
}
}
}
Manifest:
<receiver android:name=".postcall.PhoneStateBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE">
</action>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
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>
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/
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.
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
}
}
}
}
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);
}
}
}
}
}
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;
}
}
}
}
I am new user here. I want to make call forwarding functionality in my app and.
Suppose I call to X number then it forward to Y number automatically. Here I write my code that I tested. But its not working. Please help me.. I need it by tonight end... Please
public class PhoneStateReceiver extends BroadcastReceiver
{
private Context mContext;
private boolean isRinging = false;
private boolean callReceived = false;
TelephonyManager telephonyManager;
public static CustomPhoneStateListener customPhoneListener;
private String default_number = "x";
private String callForwardNumber = "y";
#Override
public void onReceive(Context context, Intent intent)
{
telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
customPhoneListener = new CustomPhoneStateListener();
telephonyManager.listen(customPhoneListener,PhoneStateListener.LISTEN_CALL_STATE
| PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
| PhoneStateListener.LISTEN_SERVICE_STATE);
mContext = context;
if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL))
{
if (getResultData()!=null && getResultData().equalsIgnoreCase(default_number))
{
String url = "tel:"+"**21*"+ callForwardNumber+Uri.encode("#");
Intent intent1 = (new Intent(Intent.ACTION_CALL, Uri.parse(url)));
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent1);
// setResultData(callForwardNumber);
}
}
}
private class CustomPhoneStateListener extends PhoneStateListener
{
#Override
public void onCallForwardingIndicatorChanged(boolean cfi) {
Log.e("","onCallForwardingIndicatorChanged CFI ="+cfi);
super.onCallForwardingIndicatorChanged(cfi);
}
#Override
public void onCallStateChanged(int state, String incomingNumber)
{
switch (state)
{
case TelephonyManager.CALL_STATE_RINGING:
// Toast.makeText(mContext, "It was a ringing call from "+incomingNumber,Toast.LENGTH_SHORT).show();
isRinging = true;
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
// Toast.makeText(mContext, "It was a offhook call from "+incomingNumber,Toast.LENGTH_SHORT).show();
callReceived = true;
break;
case TelephonyManager.CALL_STATE_IDLE:
if(isRinging == true && callReceived == false)
{
// Toast.makeText(mContext, "It was a missed call from "+incomingNumber,Toast.LENGTH_SHORT).show();
// Intent intent = new Intent(mContext, AddActivity.class);
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// mContext.startActivity(intent);
telephonyManager.listen(null, PhoneStateListener.LISTEN_NONE);
}
else if(isRinging == true && callReceived == true)
{
// Toast.makeText(mContext, "Call Received.",Toast.LENGTH_SHORT).show();
}
else if(isRinging == false && callReceived == true)
{
// Toast.makeText(mContext, "Call Ended.",Toast.LENGTH_SHORT).show();
// Intent intent = new Intent(mContext, AddActivity.class);
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// mContext.startActivity(intent);
telephonyManager.listen(null, PhoneStateListener.LISTEN_NONE);
}
break;
default:
break;
}
}
}
}
I am trying to display a message everytime I start a call or everytime I receive a call. I made a code which works for INCOMING call but not for OUTGOING ones.
I DID read the different posts about this subject.
Could anyone tell me why this following code shows me (of course this code does not do everything I mentionned previously) :
- "OUTGOING" when i receive a call
- "INCOMING" then "OUTGOING" when i start a call
/* From MainActivity */
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
context.startService(new Intent(context, SvcCall.class));
}
/* From SvcCall */
public class SvcCall extends Service
{
private static final String ACTION_OUT = "android.intent.action.PHONE_STATE";
private static final String ACTION_IN = "android.intent.action.NEW_OUTGOING_CALL";
private CallBr br_call;
#Override
public void onCreate()
{
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
final IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_OUT);
filter.addAction(ACTION_IN);
this.br_call = new CallBr();
this.registerReceiver(this.br_call, filter);
return (START_STICKY);
}
public class CallBr extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(ACTION_IN))
Toast.makeText(context, "INCOMING", Toast.LENGTH_LONG).show();
else if (intent.getAction().equals(ACTION_OUT))
Toast.makeText(context, "OUTGOING", Toast.LENGTH_LONG).show();
}
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
/* From Manifest */
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
This is the class I use to react to incoming and outgoing phonecalls. Its set up so you just have to derive and override the ones you care about. It also tells you wheter the call is starting, ending, or was not picked up:
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;
}
}
}
Here is my update.
Actually, I tried both ways (one BR vs two BR) and both worked well thanks to your answer. Everything is not perfect at this moment. I am working on it. I'll show you how I do with one BR (because it was the object of my question).
public class SvcCall extends Service
{
Context _context;
private static final String ACTION_IN = "android.intent.action.PHONE_STATE";
private static final String ACTION_OUT = "android.intent.action.NEW_OUTGOING_CALL";
private CallBr br_call;
#Override
public void onCreate()
{
super.onCreate();
this._context = getApplicationContext();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
final IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_OUT);
filter.addAction(ACTION_IN);
this.br_call = new CallBr();
this.registerReceiver(this.br_call, filter);
return (START_STICKY);
}
public class CallBr extends BroadcastReceiver
{
Bundle bundle;
String state;
String inCall, outCall;
public boolean wasRinging = false;
#Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(ACTION_IN))
{
if ((bundle = intent.getExtras()) != null)
{
state = bundle.getString(TelephonyManager.EXTRA_STATE);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING))
{
inCall = bundle.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
wasRinging = true;
Toast.makeText(context, "IN : " + inCall, Toast.LENGTH_LONG).show();
}
else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK))
{
if (wasRinging == true)
Toast.makeText(context, "ANSWERED", Toast.LENGTH_LONG).show();
}
else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE))
{
wasRinging = false;
Toast.makeText(context, "REJECT || DISCO", Toast.LENGTH_LONG).show();
}
}
}
else if (intent.getAction().equals(ACTION_OUT))
{
if ((bundle = intent.getExtras()) != null)
{
outCall = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
Toast.makeText(context, "OUT : " + outCall, Toast.LENGTH_LONG).show();
}
}
}
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import java.util.Date;
public abstract class PhoneCallReceiver2 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(context);
}
//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);
//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;
}
}
//Derived classes should override these to respond to specific events of interest
protected abstract void onIncomingCallStarted(Context context, String number, Date start);
protected abstract void onOutgoingCallStarted(Context context, String number, Date start);
protected abstract void onIncomingCallEnded(Context context, String number, Date start, Date end);
protected abstract void onOutgoingCallEnded(Context context, String number, Date start, Date end);
protected abstract void onMissedCall(Context context, 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
Context savedContext;
public PhonecallStartEndDetector(Context savedContext) {
this.savedContext = savedContext;
}
//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(savedContext, 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(savedContext, 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(savedContext, savedNumber, callStartTime);
} else if (isIncoming) {
onIncomingCallEnded(savedContext, savedNumber, callStartTime, new Date());
} else {
onOutgoingCallEnded(savedContext, savedNumber, callStartTime, new Date());
}
break;
}
lastState = state;
}
}
}
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
//CALL_STATE_IDLE;
Toast.makeText(getApplicationContext(), "CALL_STATE_IDLE", Toast.LENGTH_LONG).show();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
//CALL_STATE_OFFHOOK;
Toast.makeText(getApplicationContext(), "CALL_STATE_OFFHOOK", Toast.LENGTH_LONG).show();
break;
case TelephonyManager.CALL_STATE_RINGING:
//CALL_STATE_RINGING
Toast.makeText(getApplicationContext(), incomingNumber, Toast.LENGTH_LONG).show();
Toast.makeText(getApplicationContext(), "CALL_STATE_RINGING", Toast.LENGTH_LONG).show();
break;
default:
break;
}
Dont forget to make a permission..
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>