Phone call state - android

How do we know programmatically that the call I am making is received in the other side ?
I know that there are some phone states like IDLE, OFFHOOK and RINGING.
I want to be notified that the outgoing call I am making is received, is disconnected by the other side or is unattended by the other side.
Please suggest.

Even though there is only three states available in android telephony manager,it is very easy to identify state for all situations in a phone call.
So here we are receiving the 3 events from android such as RINGING,OFFHOOK and IDLE. And in order to get the waiting state of a call,we need to define our own states like RINGING, OFFHOOK, IDLE, FIRST_CALL_RINGING, SECOND_CALL_RINGING.
Please think in a way that we are receiving events from android and we will define our on call states.
Here is the method to get call states at onReceive method of broadcast-receiver without registering phonestatelistener and escaping from other complications. See the code.
public class CallListening extends BroadcastReceiver {
static CustomPhoneStateListener customPhoneListener;
private static final String TAG ="broadcast_intent";
public static String incoming_number;
private String current_state,previus_state,event;
public static Boolean dialog= false;
private Context context;
private SharedPreferences sp,sp1;
private SharedPreferences.Editor spEditor,spEditor1;
public void onReceive(Context context, Intent intent) {
//Log.d("intent_log", "Intent" + intent);
dialog=true;
this.context = context;
event = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
incoming_number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
Log.d(TAG, "The received event : "+event+", incoming_number : " + incoming_number);
previus_state = getCallState(context);
current_state = "IDLE";
if(incoming_number!=null){
updateIncomingNumber(incoming_number,context);
}else {
incoming_number=getIncomingNumber(context);
}
switch (event) {
case "RINGING":
Log.d(TAG, "State : Ringing, incoming_number : " + incoming_number);
if((previus_state.equals("IDLE")) || (previus_state.equals("FIRST_CALL_RINGING"))){
current_state ="FIRST_CALL_RINGING";
}
if((previus_state.equals("OFFHOOK"))||(previus_state.equals("SECOND_CALL_RINGING"))){
current_state = "SECOND_CALL_RINGING";
}
break;
case "OFFHOOK":
Log.d(TAG, "State : offhook, incoming_number : " + incoming_number);
if((previus_state.equals("IDLE")) ||(previus_state.equals("FIRST_CALL_RINGING")) || previus_state.equals("OFFHOOK")){
current_state = "OFFHOOK";
}
if(previus_state.equals("SECOND_CALL_RINGING")){
current_state ="OFFHOOK";
startDialog(context);
}
break;
case "IDLE":
Log.d(TAG, "State : idle and incoming_number : " + incoming_number);
if((previus_state.equals("OFFHOOK")) || (previus_state.equals("SECOND_CALL_RINGING")) || (previus_state.equals("IDLE"))){
current_state="IDLE";
}
if(previus_state.equals("FIRST_CALL_RINGING")){
current_state = "IDLE";
startDialog(context);
}
updateIncomingNumber("no_number",context);
Log.d(TAG,"stored incoming number flushed");
break;
}
if(!current_state.equals(previus_state)){
Log.d(TAG, "Updating state from "+previus_state +" to "+current_state);
updateCallState(current_state,context);
}
}
public void startDialog(Context context) {
Log.d(TAG,"Starting Dialog box");
Intent intent1 = new Intent(context, NotifyHangup.class);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent1);
}
public void updateCallState(String state,Context context){
sp = PreferenceManager.getDefaultSharedPreferences(context);
spEditor = sp.edit();
spEditor.putString("call_state", state);
spEditor.commit();
Log.d(TAG, "state updated");
}
public void updateIncomingNumber(String inc_num,Context context){
sp = PreferenceManager.getDefaultSharedPreferences(context);
spEditor = sp.edit();
spEditor.putString("inc_num", inc_num);
spEditor.commit();
Log.d(TAG, "incoming number updated");
}
public String getCallState(Context context){
sp1 = PreferenceManager.getDefaultSharedPreferences(context);
String st =sp1.getString("call_state", "IDLE");
Log.d(TAG,"get previous state as :"+st);
return st;
}
public String getIncomingNumber(Context context){
sp1 = PreferenceManager.getDefaultSharedPreferences(context);
String st =sp1.getString("inc_num", "no_num");
Log.d(TAG,"get incoming number as :"+st);
return st;
}
}

You need a PhoneStateListener. With that is is possible to check the state of a phone call. You have to implement onCallStateChanged. This method is called every time the state of a phone call changes. Then you can do something like this:
switch(state) {
case TelephonyManager.CALL_STATE_IDLE:
Log.d("Call","Outgoing Call finished");
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.d("Call","Outgoing Call Starting");
break;
}

What I understand is that we cannot differentiate among the various states inside the OFFHOOK state, unless we have the internal package access.
If we have the internal package access then only we can know whether the call connected is actually received or missed or disconnected by the other side.

you have to use broadcastreceiver with the following onReceive implementation to check for phonestate changes
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
final Bundle extras = intent.getExtras();
if (intent.getAction().equals(
TelephonyManager.ACTION_PHONE_STATE_CHANGED)){
final String state = extras.getString(TelephonyManager.EXTRA_STATE);
if ("IDLE".equals(state)){
//TODO
}
if ("OFFHOOK".equals(state)){
//TODO
}
if ("RINGING".equals(state)){
//TODO
}
}
}
And if you want to check for intermediate state, you can add e.g. state control in form of an integer ....

Related

All broadcasts registered for incoming calls [duplicate]

I'm trying to make an app like, when a call comes to the phone I want to detect the number. Below is what I tried, but it's not detecting incoming calls.
I want to run my MainActivity in background, how can I do that?
I had given the permission in manifest file.
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
Is there anything else should I provide in the manifest?
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_layout);
}
public class myPhoneStateChangeListener extends PhoneStateListener {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
if (state == TelephonyManager.CALL_STATE_RINGING) {
String phoneNumber = incomingNumber;
}
}
}
}
Here's what I use to do this:
Manifest:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<!--This part is inside the application-->
<receiver android:name=".CallReceiver" >
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
My base reusable call detector
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.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
private static int lastState = TelephonyManager.CALL_STATE_IDLE;
private static Date callStartTime;
private static boolean isIncoming;
private static String savedNumber; //because the passed incoming is only valid in ringing
#Override
public void onReceive(Context context, Intent intent) {
//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")) {
savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
}
else{
String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
int state = 0;
if(stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)){
state = TelephonyManager.CALL_STATE_IDLE;
}
else if(stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
state = TelephonyManager.CALL_STATE_OFFHOOK;
}
else if(stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)){
state = TelephonyManager.CALL_STATE_RINGING;
}
onCallStateChanged(context, state, number);
}
}
//Derived classes should override these to respond to specific events of interest
protected abstract void onIncomingCallReceived(Context ctx, String number, Date start);
protected abstract void onIncomingCallAnswered(Context ctx, String number, Date start);
protected abstract void onIncomingCallEnded(Context ctx, String number, Date start, Date end);
protected abstract void onOutgoingCallStarted(Context ctx, String number, Date start);
protected abstract void onOutgoingCallEnded(Context ctx, String number, Date start, Date end);
protected abstract void onMissedCall(Context ctx, String number, Date start);
//Deals with actual events
//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
public void onCallStateChanged(Context context, int state, String number) {
if(lastState == state){
//No change, debounce extras
return;
}
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
isIncoming = true;
callStartTime = new Date();
savedNumber = number;
onIncomingCallReceived(context, number, callStartTime);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
//Transition of ringing->offhook are pickups of incoming calls. Nothing done on them
if(lastState != TelephonyManager.CALL_STATE_RINGING){
isIncoming = false;
callStartTime = new Date();
onOutgoingCallStarted(context, savedNumber, callStartTime);
}
else
{
isIncoming = true;
callStartTime = new Date();
onIncomingCallAnswered(context, 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(context, savedNumber, callStartTime);
}
else if(isIncoming){
onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
}
else{
onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
}
break;
}
lastState = state;
}
}
Then to use it, simply derive a class from it and implement a few easy functions, whichever call types you care about:
public class CallReceiver extends PhonecallReceiver {
#Override
protected void onIncomingCallReceived(Context ctx, String number, Date start)
{
//
}
#Override
protected void onIncomingCallAnswered(Context ctx, String number, Date start)
{
//
}
#Override
protected void onIncomingCallEnded(Context ctx, String number, Date start, Date end)
{
//
}
#Override
protected void onOutgoingCallStarted(Context ctx, String number, Date start)
{
//
}
#Override
protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end)
{
//
}
#Override
protected void onMissedCall(Context ctx, String number, Date start)
{
//
}
}
In addition you can see a writeup I did on why the code is like it is on my blog. Gist link: https://gist.github.com/ftvs/e61ccb039f511eb288ee
EDIT: Updated to simpler code, as I've reworked the class for my own use
private MyPhoneStateListener phoneStateListener = new MyPhoneStateListener();
to register
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
and to unregister
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
With Android P - Api Level 28:
You need to get READ_CALL_LOG permission
Restricted access to call logs
Android P moves the CALL_LOG, READ_CALL_LOG, WRITE_CALL_LOG, and PROCESS_OUTGOING_CALLS permissions from the PHONE permission group to the new CALL_LOG permission group. This group gives users better control and visibility to apps that need access to sensitive information about phone calls, such as reading phone call records and identifying phone numbers.
To read numbers from the PHONE_STATE intent action, you need both the READ_CALL_LOG permission and the READ_PHONE_STATE permission.
To read numbers from onCallStateChanged(), you now need the READ_CALL_LOG permission only. You no longer need the READ_PHONE_STATE permission.
UPDATE: The really awesome code posted by Gabe Sechan no longer works unless you explicitly request the user to grant the necessary permissions. Here is some code that you can place in your main activity to request these permissions:
if (getApplicationContext().checkSelfPermission(Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
// Permission has not been granted, therefore prompt the user to grant permission
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_PHONE_STATE},
MY_PERMISSIONS_REQUEST_READ_PHONE_STATE);
}
if (getApplicationContext().checkSelfPermission(Manifest.permission.PROCESS_OUTGOING_CALLS)
!= PackageManager.PERMISSION_GRANTED) {
// Permission has not been granted, therefore prompt the user to grant permission
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.PROCESS_OUTGOING_CALLS},
MY_PERMISSIONS_REQUEST_PROCESS_OUTGOING_CALLS);
}
ALSO: As someone mentioned in a comment below Gabe's post, you have to add a little snippet of code, android:enabled="true, to the receiver in order to detect incoming calls when the app is not currently running in the foreground:
<!--This part is inside the application-->
<receiver android:name=".CallReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
Just to update Gabe Sechan's answer. If your manifest asks for permissions to READ_CALL_LOG and READ_PHONE_STATE, onReceive will called TWICE. One of which has EXTRA_INCOMING_NUMBER in it and the other doesn't. You have to test which has it and it can occur in any order.
https://developer.android.com/reference/android/telephony/TelephonyManager.html#ACTION_PHONE_STATE_CHANGED
this may helps you and also add require permision
public class PhoneListener extends PhoneStateListener
{
private Context context;
public static String getincomno;
public PhoneListener(Context c) {
Log.i("CallRecorder", "PhoneListener constructor");
context = c;
}
public void onCallStateChanged (int state, String incomingNumber)
{
if(!TextUtils.isEmpty(incomingNumber)){
// here for Outgoing number make null to get incoming number
CallBroadcastReceiver.numberToCall = null;
getincomno = incomingNumber;
}
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
break;
case TelephonyManager.CALL_STATE_RINGING:
Log.d("CallRecorder", "CALL_STATE_RINGING");
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
break;
}
}
}
Here is a simple method which can avoid the use of PhonestateListener and other complications.
So here we are receiving the 3 events from android such as RINGING,OFFHOOK and IDLE. And in order to get the all possible state of call,we need to define our own states like RINGING, OFFHOOK, IDLE, FIRST_CALL_RINGING, SECOND_CALL_RINGING.
It can handle every states in a phone call.
Please think in a way that we are receiving events from android and we will define our on call states. See the code.
public class CallListening extends BroadcastReceiver {
private static final String TAG ="broadcast_intent";
public static String incoming_number;
private String current_state,previus_state,event;
public static Boolean dialog= false;
private Context context;
private SharedPreferences sp,sp1;
private SharedPreferences.Editor spEditor,spEditor1;
public void onReceive(Context context, Intent intent) {
//Log.d("intent_log", "Intent" + intent);
dialog=true;
this.context = context;
event = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
incoming_number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
Log.d(TAG, "The received event : "+event+", incoming_number : " + incoming_number);
previus_state = getCallState(context);
current_state = "IDLE";
if(incoming_number!=null){
updateIncomingNumber(incoming_number,context);
}else {
incoming_number=getIncomingNumber(context);
}
switch (event) {
case "RINGING":
Log.d(TAG, "State : Ringing, incoming_number : " + incoming_number);
if((previus_state.equals("IDLE")) || (previus_state.equals("FIRST_CALL_RINGING"))){
current_state ="FIRST_CALL_RINGING";
}
if((previus_state.equals("OFFHOOK"))||(previus_state.equals("SECOND_CALL_RINGING"))){
current_state = "SECOND_CALL_RINGING";
}
break;
case "OFFHOOK":
Log.d(TAG, "State : offhook, incoming_number : " + incoming_number);
if((previus_state.equals("IDLE")) ||(previus_state.equals("FIRST_CALL_RINGING")) || previus_state.equals("OFFHOOK")){
current_state = "OFFHOOK";
}
if(previus_state.equals("SECOND_CALL_RINGING")){
current_state ="OFFHOOK";
startDialog(context);
}
break;
case "IDLE":
Log.d(TAG, "State : idle and incoming_number : " + incoming_number);
if((previus_state.equals("OFFHOOK")) || (previus_state.equals("SECOND_CALL_RINGING")) || (previus_state.equals("IDLE"))){
current_state="IDLE";
}
if(previus_state.equals("FIRST_CALL_RINGING")){
current_state = "IDLE";
startDialog(context);
}
updateIncomingNumber("no_number",context);
Log.d(TAG,"stored incoming number flushed");
break;
}
if(!current_state.equals(previus_state)){
Log.d(TAG, "Updating state from "+previus_state +" to "+current_state);
updateCallState(current_state,context);
}
}
public void startDialog(Context context) {
Log.d(TAG,"Starting Dialog box");
Intent intent1 = new Intent(context, NotifyHangup.class);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent1);
}
public void updateCallState(String state,Context context){
sp = PreferenceManager.getDefaultSharedPreferences(context);
spEditor = sp.edit();
spEditor.putString("call_state", state);
spEditor.commit();
Log.d(TAG, "state updated");
}
public void updateIncomingNumber(String inc_num,Context context){
sp = PreferenceManager.getDefaultSharedPreferences(context);
spEditor = sp.edit();
spEditor.putString("inc_num", inc_num);
spEditor.commit();
Log.d(TAG, "incoming number updated");
}
public String getCallState(Context context){
sp1 = PreferenceManager.getDefaultSharedPreferences(context);
String st =sp1.getString("call_state", "IDLE");
Log.d(TAG,"get previous state as :"+st);
return st;
}
public String getIncomingNumber(Context context){
sp1 = PreferenceManager.getDefaultSharedPreferences(context);
String st =sp1.getString("inc_num", "no_num");
Log.d(TAG,"get incoming number as :"+st);
return st;
}
}
I fixed Gabe Sechan answer, I used the following code and it worked properly.
I noticed when a receiver intent has the "incoming_number" key, I can get the phone number. So I filtered incoming intent and used EXTRA_INCOMING_NUMBER and EXTRA_PHONE_NUMBER to get the phone number.
public class ChangeCallStateListener extends BroadcastReceiver {
private static String lastState = TelephonyManager.EXTRA_STATE_IDLE;
private static Date callStartTime;
private static boolean isIncoming;
private static String savedNumber; //because the passed incoming is only valid in ringing
#Override
public void onReceive(Context context, Intent intent) {
Log.d("CallObserver", "CallReceiver is starting ....");
List<String> keyList = new ArrayList<>();
Bundle bundle = intent.getExtras();
if (bundle != null) {
keyList = new ArrayList<>(bundle.keySet());
Log.e("CallObserver", "keys : " + keyList);
}
if (keyList.contains("incoming_number")) {
String phoneState = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
String phoneIncomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
String phoneOutgoingNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
String phoneNumber = phoneOutgoingNumber != null ? phoneOutgoingNumber : (phoneIncomingNumber != null ? phoneIncomingNumber : "");
if (phoneState != null && phoneNumber != null) {
if (lastState.equals(phoneState)) {
//No change, debounce extras
return;
}
Log.e("CallObserver", "phoneState = " + phoneState);
if (TelephonyManager.EXTRA_STATE_RINGING.equals(phoneState)) {
isIncoming = true;
callStartTime = new Date();
//
lastState = TelephonyManager.EXTRA_STATE_RINGING;
if (phoneNumber != null) {
savedNumber = phoneNumber;
}
onIncomingCallStarted(context, savedNumber, callStartTime);
} else if (TelephonyManager.EXTRA_STATE_IDLE.equals(phoneState)) {
if (lastState.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
//
lastState = TelephonyManager.EXTRA_STATE_IDLE;
onMissedCall(context, savedNumber, callStartTime);
} else {
if (isIncoming) {
//
lastState = TelephonyManager.EXTRA_STATE_IDLE;
onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
} else {
//
lastState = TelephonyManager.EXTRA_STATE_IDLE;
Log.d("CallObserver", "onOutgoingCallEnded called !! : ");
onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
}
}
} else if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(phoneState)) {
if (lastState.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
isIncoming = true;
} else {
isIncoming = false;
}
callStartTime = new Date();
savedNumber = phoneNumber;
//
lastState = TelephonyManager.EXTRA_STATE_OFFHOOK;
onOutgoingCallStarted(context, savedNumber, callStartTime);
}
}
}
}
protected void onIncomingCallStarted(Context ctx, String number, Date start) {
Log.d("CallObserver", "onIncomingCallStarted : " + " number is : " + number);
}
protected void onOutgoingCallStarted(Context ctx, String number, Date start) {
Log.d("CallObserver", "onOutgoingCallStarted : " + " number is : " + number);
}
protected void onIncomingCallEnded(Context context, String number, Date start, Date end) {
}
protected void onOutgoingCallEnded(Context context , String number, Date start, Date end) {
}
protected void onMissedCall(Context context, String number, Date start) {
}
}
Don't forget to get run time permission.
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
#Gabe Sechan, thanks for your code. It works fine except the onOutgoingCallEnded(). It is never executed. Testing phones are Samsung S5 & Trendy. There are 2 bugs I think.
1: a pair of brackets is missing.
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(context, savedNumber, callStartTime);
} else {
// this one is missing
if(isIncoming){
onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
} else {
onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
}
}
// this one is missing
break;
2: lastState is not updated by the state if it is at the end of the function. It should be replaced to the first line of this function by
public void onCallStateChanged(Context context, int state, String number) {
int lastStateTemp = lastState;
lastState = state;
// todo replace all the "lastState" by lastStateTemp from here.
if (lastStateTemp == state) {
//No change, debounce extras
return;
}
//....
}
Additional I've put lastState and savedNumber into shared preference as you suggested.
Just tested it with above changes. Bug fixed at least on my phones.
Please use the below code. It will help you to get the incoming number with other call details.
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<TextView
android:id="#+id/call"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="#string/hello_world" />
</RelativeLayout>
MainActivity.java
public class MainActivity extends Activity {
private static final int MISSED_CALL_TYPE = 0;
private TextView txtcall;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtcall = (TextView) findViewById(R.id.call);
StringBuffer sb = new StringBuffer();
Cursor managedCursor = managedQuery(CallLog.Calls.CONTENT_URI, null,
null, null, null);
int number = managedCursor.getColumnIndex(CallLog.Calls.NUMBER);
int type = managedCursor.getColumnIndex(CallLog.Calls.TYPE);
int date = managedCursor.getColumnIndex(CallLog.Calls.DATE);
int duration = managedCursor.getColumnIndex(CallLog.Calls.DURATION);
sb.append("Call Details :");
while (managedCursor.moveToNext()) {
String phNumber = managedCursor.getString(number);
String callType = managedCursor.getString(type);
String callDate = managedCursor.getString(date);
Date callDayTime = new Date(Long.valueOf(callDate));
String callDuration = managedCursor.getString(duration);
String dir = null;
int dircode = Integer.parseInt(callType);
switch (dircode) {
case CallLog.Calls.OUTGOING_TYPE:
dir = "OUTGOING";
break;
case CallLog.Calls.INCOMING_TYPE:
dir = "INCOMING";
break;
case CallLog.Calls.MISSED_TYPE:
dir = "MISSED";
break;
}
sb.append("\nPhone Number:--- " + phNumber + " \nCall Type:--- "
+ dir + " \nCall Date:--- " + callDayTime
+ " \nCall duration in sec :--- " + callDuration);
sb.append("\n----------------------------------");
}
managedCursor.close();
txtcall.setText(sb);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
and in your manifest request for following permissions:
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.READ_LOGS"/>
You need a BroadcastReceiver for ACTION_PHONE_STATE_CHANGED This will call your received whenever the phone-state changes from idle, ringing, offhook so from the previous value and the new value you can detect if this is an incoming/outgoing call.
Required permission would be:
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
But if you also want to receive the EXTRA_INCOMING_NUMBER in that broadcast, you'll need another permission: "android.permission.READ_CALL_LOG"
And the code something like this:
val receiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d(TAG, "onReceive")
}
}
override fun onResume() {
val filter = IntentFilter()
filter.addAction("android.intent.action.PHONE_STATE")
registerReceiver(receiver, filter)
super.onResume()
}
override fun onPause() {
unregisterReceiver(receiver)
super.onPause()
}
and in receiver class, we can get current state by reading intent like this:
intent.extras["state"]
the result of extras could be:
RINGING -> If your phone is ringing
OFFHOOK -> If you are talking with someone (Incoming or Outcoming
call)
IDLE -> if call ended (Incoming or Outcoming call)
With PHONE_STATE broadcast we don't need to use PROCESS_OUTGOING_CALLS permission or deprecated NEW_OUTGOING_CALL action.
Refer to the answer by Gabe Sechan. As mentioned, in the case of an Outgoing call, we have the following state change: IDLE -> OFFHOOK -> IDLE. In Gabe's original answer, savedNumber is only set if the phone state becomes RINGING which won't be true for an Outgoing call. A small fix to to also set savedNumber when the phone state becomes OFFHOOK:
case TelephonyManager.CALL_STATE_OFFHOOK:
if(lastState != TelephonyManager.CALL_STATE_RINGING){
//IDLE to OFFHOOK for example.
isIncoming = false;
callStartTime = new Date();
savedNumber = number;
onOutgoingCallStarted(context, savedNumber, callStartTime);
}
...
This fix allows the dialed number to be passed to Outgoing call methods in the same way that the incoming number is passed to Incoming call or Missed call methods.
Hack Alert :)
If you are just interested in the incoming call event, consider using the AudioManager and listening to focus changes.
Advantage - no permission is required.
Disadvantage - won't work in silent mode... in that case, we will increase the volume of the incoming call to the minimum to still get the Audio focus event.
/**
* Register a callback to [AudioManager] to identify an incoming call.
*/
private fun registerAudioFocusChangeListener(){
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val incomingCallVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING)
if(incomingCallVolume == 0) {
// Hack Alert :)
// The user has muted the phone calls, if we still want to intercept the event,
// we set the volume of the incoming call to Minumum otherwise we will not get
// Audio focus event...
try {
audioManager.adjustVolume(
AudioManager.ADJUST_UNMUTE,
AudioManager.FLAG_ALLOW_RINGER_MODES
)
audioManager.setStreamVolume(
AudioManager.STREAM_RING,
audioManager.getStreamMinVolume(AudioManager.STREAM_RING),
0
);
} catch (e : SecurityException){
// DND (Don't Disturb Mode) is probably ON, we are not allowed to set the volume
// in this scenario, But in this case no incoming call is possible anyway.
}
}
val requestBuilder = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
requestBuilder.setOnAudioFocusChangeListener { _ ->
Handler(Looper.getMainLooper()).postDelayed({
val mode = audioManager.mode
if(mode == AudioManager.MODE_RINGTONE || mode == AudioManager.MODE_IN_CALL){
//Ring Ring, do your thing
}
}, 100)
}
audioManager.requestAudioFocus(requestBuilder.build())
}

Ending a phone call in Android

After a phone call ends, I must open another Activity.
However, it doesn't catch the "call end signal".
I've used mTelManager in BroadcastReceiver and Service, but it doesn't work.
When Activity is opened by a Service or BroadcastReceiver, it shows the MainActivity and not the targeted one instead.
Source code:
public class MainService extends Service{
TelephonyManager mTelManager;
PhoneStateRead pListener;
String TAG = "Call State catch";
boolean callOutState = false; //calling state
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
Log.i(TAG, "ServiceReceiver->onReceive();");
pListener = new PhoneStateRead();
mTelManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
mTelManager.listen(pListener, PhoneStateListener.LISTEN_CALL_STATE);
if(callOutState){ //if call end
Intent showRecord = new Intent(getBaseContext(), Call_Out_Record_Activity.class);
showRecord.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(showRecord);//open Call_Out_Record_Activity
}
}
public class PhoneStateRead extends PhoneStateListener {
private final String TAG = "Phone State Read";
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
Log.i(TAG,"MyPhoneStateListener->onCallStateChanged() -> CALL_STATE_IDLE "+incomingNumber);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.i(TAG,"MyPhoneStateListener->onCallStateChanged() -> CALL_STATE_OFFHOOK "+incomingNumber);
break;
case TelephonyManager.CALL_STATE_RINGING:
Log.i(TAG,"MyPhoneStateListener->onCallStateChanged() -> CALL_STATE_RINGING "+incomingNumber);
break;
default:
Log.i(TAG,"MyPhoneStateListener->onCallStateChanged() -> default -> "+Integer.toString(state));
break;
}
CatchCallState(state);
}
}
private void CatchCallState(int state){
if(state == TelephonyManager.CALL_STATE_OFFHOOK || state == TelephonyManager.CALL_STATE_RINGING)
callOutState = false;
else if(state == TelephonyManager.CALL_STATE_IDLE)
callOutState = true;
}
}
So it looks like you have a couple problems here. First, you are trying to start your activity when you start your Service - you should do this code:
if(callOutState){ //if call end
Intent showRecord = new Intent(getBaseContext(), Call_Out_Record_Activity.class);
showRecord.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(showRecord);//open Call_Out_Record_Activity
}
in the CallStateListener. Or just put it in your CatchCallState method.
Next, notice that there is not a "call end signal" - the phone will go from "OFF_HOOK" to "IDLE" so you need flags to track that. Also, notice that "RINGING" happens on incoming calls, but not outgoing calls. So you may want to display different messages depending on incoming or outgoing calls.

I am trying to delete a particular incoming number after hang up but these code not working properly

I am trying to delete a particular incoming number from call log when phone hang-up. I tried as i can do but that code not working the number still remain in call log.
it is working properly when broadcast invoke but this is not deleting number of incoming call.
Thanks in Advance.
public class ListenPhoneState extends BroadcastReceiver {
private boolean isPressOffHock = false;
static private String incomingNumberToDelete;
#Override
public void onReceive(Context context, Intent intent) {
try {
// TELEPHONY MANAGER class object to register one listner
TelephonyManager tmgr = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
//Create Listner
MyPhoneStateListener PhoneListener = new MyPhoneStateListener();
// Register listener for LISTEN_CALL_STATE
tmgr.listen(PhoneListener, PhoneStateListener.LISTEN_CALL_STATE);
} catch (Exception e) {
Log.e("Phone Receive Error", " " + e);
}
if(incomingNumberToDelete != null)
{
String queryString="NUMBER="+incomingNumberToDelete;
context.getContentResolver().delete(CallLog.Calls.CONTENT_URI,queryString,null);
Log.d("Delete", "Delete Number " + incomingNumberToDelete );
}
}
private class MyPhoneStateListener extends PhoneStateListener {
public void onCallStateChanged(int state, String incomingNumber) {
if(state == TelephonyManager.CALL_STATE_RINGING)
{
incomingNumberToDelete = incomingNumber;
Log.d("Check", "income " + incomingNumberToDelete );
}
switch(state) {
case TelephonyManager.CALL_STATE_IDLE:
//Log.d("IDLE", state + "incoming" + incomingNumber);
isPressOffHock = true;
break;
case TelephonyManager.CALL_STATE_RINGING:
//Log.d("RINGING", state + "incoming" + incomingNumber);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
String msg = "New Phone Call Event. Incomming Number : "+incomingNumber;
// Log.d("CALL_STATE_OFFHOOK",msg );
break;
}
}
}
}
Usually there is a delay between the broadcast of a phone state and the system data that is written or system activities (such as call logs or phone ringing). Right now, your receiver is probably trying to delete the call log before it is present.
You could delay the delete or take a look at this post for registering a ContentObserver:
Delete call from call log after call end

Android Call Waiting State

Is there any state which can tell us that the line is connected and other person's phone is ringing. Like I want to know the state where proper connection is made and other person knows you are calling and can answer it?
I have tried this but it never goes in phone ringing state:
public void onReceive(final Context context, Intent intent) {
TelephonyManagerTm=(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
Tm.listen(new PhoneStateListener(){
public void onCallStateChanged(int state,String number) {
super.onDataConnectionStateChanged(state);
switch(state)
{
case TelephonyManager.CALL_STATE_RINGING:
Toast.makeText(context, "Phone Ringing", 1).show();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
break;
case TelephonyManager.CALL_STATE_IDLE:
Toast.makeText(context, "call rejected", 1).show();
break;
}
}
},PhoneStateListener.LISTEN_CALL_STATE);
Even though there are only three states available in Android Telephony Manager, it is very easy to identify the waiting state of a new incoming call. And this algorithm will provide state for all situations in a phone call.
So here we are receiving the 3 events from android such as RINGING, OFFHOOK and IDLE. And in order to get the waiting state of a new incoming call, we need to define our own states like RINGING, OFFHOOK, IDLE, FIRST_CALL_RINGING, SECOND_CALL_RINGING.
Please think in a way that we are receiving events from android and we will define our on call states.
Here is the method to get call states at onReceive() of broadcast-receiver without registering PhoneStateListener and escaping from other complications. See the code.
public class CallListening extends BroadcastReceiver {
static CustomPhoneStateListener customPhoneListener;
private static final String TAG ="broadcast_intent";
public static String incoming_number;
private String current_state,previus_state,event;
public static Boolean dialog= false;
private Context context;
private SharedPreferences sp,sp1;
private SharedPreferences.Editor spEditor,spEditor1;
public void onReceive(Context context, Intent intent) {
//Log.d("intent_log", "Intent" + intent);
dialog=true;
this.context = context;
event = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
incoming_number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
Log.d(TAG, "The received event : "+event+", incoming_number : " + incoming_number);
previus_state = getCallState(context);
current_state = "IDLE";
if(incoming_number!=null){
updateIncomingNumber(incoming_number,context);
}else {
incoming_number=getIncomingNumber(context);
}
switch (event) {
case "RINGING":
Log.d(TAG, "State : Ringing, incoming_number : " + incoming_number);
if((previus_state.equals("IDLE")) || (previus_state.equals("FIRST_CALL_RINGING"))){
current_state ="FIRST_CALL_RINGING";
}
if((previus_state.equals("OFFHOOK"))||(previus_state.equals("SECOND_CALL_RINGING"))){
current_state = "SECOND_CALL_RINGING";
}
break;
case "OFFHOOK":
Log.d(TAG, "State : offhook, incoming_number : " + incoming_number);
if((previus_state.equals("IDLE")) ||(previus_state.equals("FIRST_CALL_RINGING")) || previus_state.equals("OFFHOOK")){
current_state = "OFFHOOK";
}
if(previus_state.equals("SECOND_CALL_RINGING")){
current_state ="OFFHOOK";
startDialog(context);
}
break;
case "IDLE":
Log.d(TAG, "State : idle and incoming_number : " + incoming_number);
if((previus_state.equals("OFFHOOK")) || (previus_state.equals("SECOND_CALL_RINGING")) || (previus_state.equals("IDLE"))){
current_state="IDLE";
}
if(previus_state.equals("FIRST_CALL_RINGING")){
current_state = "IDLE";
startDialog(context);
}
updateIncomingNumber("no_number",context);
Log.d(TAG,"stored incoming number flushed");
break;
}
if(!current_state.equals(previus_state)){
Log.d(TAG, "Updating state from "+previus_state +" to "+current_state);
updateCallState(current_state,context);
}
}
public void startDialog(Context context) {
Log.d(TAG,"Starting Dialog box");
Intent intent1 = new Intent(context, NotifyHangup.class);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent1);
}
public void updateCallState(String state,Context context){
sp = PreferenceManager.getDefaultSharedPreferences(context);
spEditor = sp.edit();
spEditor.putString("call_state", state);
spEditor.commit();
Log.d(TAG, "state updated");
}
public void updateIncomingNumber(String inc_num,Context context){
sp = PreferenceManager.getDefaultSharedPreferences(context);
spEditor = sp.edit();
spEditor.putString("inc_num", inc_num);
spEditor.commit();
Log.d(TAG, "incoming number updated");
}
public String getCallState(Context context){
sp1 = PreferenceManager.getDefaultSharedPreferences(context);
String st =sp1.getString("call_state", "IDLE");
Log.d(TAG,"get previous state as :"+st);
return st;
}
public String getIncomingNumber(Context context){
sp1 = PreferenceManager.getDefaultSharedPreferences(context);
String st =sp1.getString("inc_num", "no_num");
Log.d(TAG,"get incoming number as :"+st);
return st;
}
}
There are 3 states: IDLE, RINGING, OFFHOOK.
For incoming calls the normal flow is:
IDLE (start state),
RINGING (the phone is ringing),
OFFHOOK (the user picks up)
For outgoing calls the flow is:
IDLE (start state),
OFFHOOK (called only after the other person picks up)
There's no RINGING state for outgoing calls before they're picked up.
Yes we can get the state PhoneStateListener.
Whenever you extend a class from PhoneStateListener, you will get onCallStateChanged(), like below:
public class CustomPhoneStateListener extends PhoneStateListener {
ActivityManager activityManager;
Intent i1;
public CustomPhoneStateListener(Context context) {
super();
this.context = context;
i1 = new Intent(context, TelephoneyWithoutToastActivity.class);
i1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
#Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
//when Idle i.e no call
Toast.makeText(context, "Phone state Idle", Toast.LENGTH_LONG).show();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
//when Off hook i.e in call
//Make intent and start your service here
Toast.makeText(context, "Phone state Off hook", Toast.LENGTH_LONG).show();
break;
case TelephonyManager.CALL_STATE_RINGING:
ActivityManager localActivityManager = (ActivityManager) this.context.getSystemService("activity");
for (String str = ((ActivityManager.RunningTaskInfo) localActivityManager.getRunningTasks(1).get(0)).topActivity.flattenToString();; str = ((ActivityManager.RunningTaskInfo) localActivityManager.getRunningTasks(1).get(0)).topActivity.flattenToString()) {
if ((!str.contains("com.android.phone.InCallScreen")))
continue;
Log.d("IncomingCallPlus", "*****************************************************");
context.startActivity(i1);
return;
}
default:
break;
}
}
}
For reference check this.

Broadcast receiver and phonestatelistener

I am trying to use BroadcastReceiver to receive incoming calls. I want to register call when the call is ended. Below code gives each state (idle,ringing n offhook) more than once with the same number (its not an issue of past call). Is it because phonestatelistner is called within the service.
public class IncomingCallReceiver extends BroadcastReceiver {
int stateString =0;
int prevStat = 0;
#Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if(null == bundle)
return;
String state = bundle.getString(TelephonyManager.EXTRA_STATE);
if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_RINGING))
{
String phonenumber = bundle.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
String info = "Detect Calls sample application\nIncoming number: " + phonenumber;
Toast.makeText(context, info, Toast.LENGTH_LONG).show();
}
TelephonyManager telephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
//CustomPhoneStateListener customPhoneListener = new CustomPhoneStateListener();
PhoneStateListener listener = new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
String p = incomingNumber;
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
prevStat = stateString;
stateString = 1;
Log.i("ON CALL STATE CHANGED","IDLE");
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.i("ON CALL STATE CHANGED","OFFFHOOK");
prevStat = stateString;
stateString = 2;
break;
case TelephonyManager.CALL_STATE_RINGING:
prevStat = stateString;
stateString = 3;
Log.i("ON CALL STATE CHANGED","RINGING");
break;
}
if(prevStat == 2 && stateString == 1)
{
Log.i("REGISTER A CALLL","==" +p);
}
}
};
telephony.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
}
}
I also tried the same thing with phonestatelistener in a separate class, but it was not being called, i.e my code didn't reach there. In my manifest file I have registered receiver, but there is no declaration of phonestatelistener. Is that to be registered?
I am sorry if the solution is quite obvious but am new to android things.
Any help is appreciated :)
Yes, choose listener or received , not both of them

Categories

Resources