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())
}
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.