How to determine the phone number of a current caller in a stand-alone application - android

I'd like to build an Android application that can contact the current caller via a pre-determined text message. Sending a text message is simple enough but determining the phone number of the current caller in a stand-alone application is the challenge. Is the there an easy way to divine the phone number so I can send them a message while still on the call?
Of course there are manual ways to do this: write down the number, key it into a new text message, enter the message. But I want to define the message up front and be able to "send it to current caller".

#Override
public void onReceive(Context context, Intent intent) {
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
PhoneCallStateListener customPhoneListener = new PhoneCallStateListener(context);
telephony.listen(customPhoneListener, PhoneStateListener.LISTEN_CALL_STATE);
helper = new ContactDatabaseHelper(context);
list = helper.getAllContacts();
try{
incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
if (list.size() != 0){
for ( int i = 0, size = list.size(); i < size; i++ ){
if (PhoneNumberUtils.compare(incomingNumber, list.get(i).getContactNumber())){
ToastMsg.showToast(context,list.get(i).getContactName()+" Calling");
}
}
}
}catch (Exception e) {
// TODO: handle exception
}
}
public class PhoneCallStateListener extends PhoneStateListener{
private Context context;
public PhoneCallStateListener(Context context){
this.context = context;
}
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
break;
case PhoneStateListener.LISTEN_CALL_STATE:
}
super.onCallStateChanged(state, incomingNumber);
}
}

For your sistuation the best I can think of is to use PhoneStateListener. It contains onCallStateChanged handler. One of the arguments is a String containing the incoming phone number.
Source:
http://developer.android.com/reference/android/telephony/PhoneStateListener.html
Ctrl + F and type in "Incoming" and you will find everything you need to know.
EDIT: To make sure you're app starts on the startup of your phone, just add a BroadcastReciever. How to start an Application on startup?

Register a BroadcastReceiver in your manifest that listens to ACTION_PHONE_STATE_CHANGED.
Broadcast intent action indicating that the call state (cellular) on
the device has changed.
The EXTRA_STATE extra indicates the new call state. If the new state
is RINGING, a second extra EXTRA_INCOMING_NUMBER provides the incoming
phone number as a String.
Requires the READ_PHONE_STATE permission.
This was a sticky broadcast in version 1.0, but it is no longer
sticky. Instead, use getCallState() to synchronously query the current
call state.
This way you don't need the user to launch your app before receiving a call.

Related

Get notification when someone is calling

I'm very new to mobile development and to Xamarin.
I created a new Xamarin.Forms solution "AndroidApp1" which by default consists of two projects, "AndroidApp1.Android" and "AndroidApp1".
If I understood correctly what I read, the first one contains the code specific to Android and the second one contains the code that can be used in Android, iOS or Windows Phone.
My solution runs in an Android emulation in debug mode, now I'd like to be notified when there's an incoming call, I'd like to get the telephone number that is calling me.
Google told me, that I need to create a class that inherits from BroadcastReceiver and override the OnReceive function.
I assume that this class needs to reside in the Android specific project (AndroidApp1.Android), so I created the class here, but now what?
I can't find any information about what to do with this class? Where do I instantiate it? How do I get the notification and the phone number to react on in my "AndroidApp1" project?
Here's the source code of my BroadcastReceiver (copied from the internet):
[BroadcastReceiver(Enabled = true, Exported = false)]
[IntentFilter(new[] { "android.intent.action.PHONE_STATE" })]
public class IncomingCallReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
// ensure there is information
if (intent.Extras != null)
{
// get the incoming call state
string state = intent.GetStringExtra(TelephonyManager.ExtraState);
// check the current state
if (state == TelephonyManager.ExtraStateRinging)
{
// read the incoming call telephone number...
string telephone = intent.GetStringExtra(TelephonyManager.ExtraIncomingNumber);
// check the reade telephone
if (string.IsNullOrEmpty(telephone))
telephone = string.Empty;
}
else if (state == TelephonyManager.ExtraStateOffhook)
{
// incoming call answer
}
else if (state == TelephonyManager.ExtraStateIdle)
{
// incoming call end
}
}
}
}
You need to register brodcast receiver for the call state.
<action android:name="android.intent.action.PHONE_STATE" />
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
and create a notification once your get broadcast events.
Make sure you have defined permission.
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
You need to have a Broadcast Receiver something like this.
/**
* Listener to detect incoming calls.
*/
private class CallStateListener extends PhoneStateListener {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
// called when someone is ringing to this phone
Toast.makeText(ctx, "Incoming: "+incomingNumber,
Toast.LENGTH_LONG).show();
break;
}
}
You can register the lister for incoming call events.
tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(callStateListener, PhoneStateListener.LISTEN_CALL_STATE);
Here is the more info
https://stackoverflow.com/questions/9684866/how-to-detect-when-phone-is-answered-or-rejected
Finally I am one step forward. I created a class StateListener that inherits from PhoneStateListener in the Android specific project like this:
public class StateListener : PhoneStateListener
{
public override void OnCallStateChanged(CallState state, string incomingNumber)
{
base.OnCallStateChanged(state, incomingNumber);
switch (state)
{
case CallState.Ringing:
break;
case CallState.Offhook:
break;
case CallState.Idle:
break;
}
}
}
Then I instantiated this class in the OnCreate function of the MainActivity class of the Android specific project with these three lines of code:
StateListener phoneStateListener = new StateListener();
TelephonyManager telephonyManager = (TelephonyManager)GetSystemService(Context.TelephonyService);
telephonyManager.Listen(phoneStateListener, PhoneStateListenerFlags.CallState);
Now when I set break points in the case parts of the switch (state) in OnCallStateChanged they break, but the incomingNumber is always empty, although I've set the corresponding rights in the manifest.
So, that will be my next step, to get the calling number.

ACTION_PHONE_STATE_CHANGED not called on network cell changes

I am trying to make my broadcast receiver fire when the phone goes in and out of reception areas. The issue is the receiver never gets called when the cell reception changes. The BroadcastReceiver works fine for getting phone call states (call idle, started ect...), and also for getting the airplane mode switched on and off because the broadcast receiver handles both.
I added the permissions and intent filter to the receiver in the manifest and they are working fine.
Here is what I have for my BroadcastReceiver and PhoneStateListener.
public class PhoneStateReceiver extends BroadcastReceiver {
private PhoneStateListener mListener = new ServiceStateListener();
private TelephonyManager mTelephonyManager;
private Context mContext;
/**
* TODO add some sort of call back interface to allow for different uses of this phone state receiver
*/
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
mContext = context;
if (action.intern().equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
boolean isAirplaneModeOn = intent.getBooleanExtra("state", false);
if (!isAirplaneModeOn) {
SmsRetryManager.getInstance().retryAllSms(context);
}
} else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
mTelephonyManager = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
Toast.makeText(mContext, "Receiver registered!", Toast.LENGTH_LONG).show();
mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_SERVICE_STATE);
mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
}
public void onDestroy() {
mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
}
private class ServiceStateListener extends PhoneStateListener {
#Override
public void onServiceStateChanged (ServiceState serviceState) {
super.onServiceStateChanged(serviceState);
boolean connected = (serviceState.getState() == ServiceState.STATE_IN_SERVICE);
if (connected) {
Toast.makeText(mContext, "Connection Gained!", Toast.LENGTH_LONG).show();
//todo retry sms here
SmsRetryManager.getInstance().retryAllSms(mContext);
} else {
Toast.makeText(mContext, "Connection Lost!", Toast.LENGTH_LONG).show();
}
}
#Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
super.onSignalStrengthsChanged(signalStrength);
Toast.makeText(mContext, "Signal changed - cdma : " + signalStrength.getCdmaDbm() + " gsm : " + signalStrength.getGsmSignalStrength(), Toast.LENGTH_LONG).show();
}
}
Any insight would be awesome. I have been banging my head on this one for a while.
Thanks!
I assume you are listening the broadcast android.intent.action.SERVICE_STATE
If so, try using:
if (TelephonyManager.getnetworkOperator.length()==0) connected=false;
on your OnReceive method, to know if the phone is not connected. It works fine for me.
If this solution doesn't work, please show how you register the receiver: is it statically registered at manifest.xml? or dynamically with PackageManager.setComponentEnabledSetting? Note: With static registration you'll find the receiver is not triggered after reinstalling the app, needing to add to the receiver tag
<intent-filter>
<action android:name= "android.intent.action.MY_PACKAGE_REPLACED"/></intent-filter>
You can also look at the values that return the ServiceState.
See here http://developer.android.com/reference/android/telephony/ServiceState.html
And check which of these values is returned:
int STATE_EMERGENCY_ONLY = The phone is registered and locked. Only
emergency numbers are allowed.
int STATE_IN_SERVICE = Normal operation condition, the phone is registered with an operator either in home network or in roaming.
int STATE_OUT_OF_SERVICE = Phone is not registered with any operator,
the phone can be currently searching a new operator to register to, or
not searching to registration at all, or registration is denied, or
radio signal is not available.
int STATE_POWER_OFF = Radio of telephony is explicitly powered off.

How to get sms sent confirmation for each contact/person in android?

I want to send sms to multiple people and verify whether sms sent or not. I checked multiple links (mentioned here) and got the idea of using PendingIntent and broadCast Receiver for confirmation.
Practical way to find out if SMS has been sent
Sending text messages programmatically in android
http://mobiforge.com/design-development/sms-messaging-android
But the key problem is that, I have different 50 contacts number in an arrayList and their different msgs in another arrayList.
I use this code :
for (Condition) {
sms = SmsManager.getDefault();
try {
. . . sms.sendTextMessage(phoneNumbers[i], null, messages[i], sentPI, deliveredPI);
}
catch(IllegalArgumentException e) { }
}
Now, I can't identify how many people do get their msg and how many don't. Because as shown in post(mentioned link above), every time we just get one msg, "SMS delivered".
So please let me know, how can I put "extras" in Intent, when I send msg and get the extras from broadcast Receiver to get the detail of specific contact/person.
One More thing : There are four different option for flag value in PendingIntent
(FLAG_ONE_SHOT, FLAG_NO_CREATE, FLAG_CANCEL_CURRENT,FLAG_UPDATE_CURRENT).
Which one should I use when I send messages in for loop for correct result?
This is a very simple example to demonstrate the use of the send and delivery PendingIntents available for all of the SmsManager#send*() methods, and attaching data to those to easily differentiate the results in the Receiver.
Attaching that data is as simple as putting extras on the Intents backing the PendingIntents we pass to the send*() methods. The catch is that PendingIntents might not behave as one expects. To conserve resources, the system will only create new ones when it must. The get*() methods will only return a distinct PendingIntent if the Intent is different per the Intent#filterEquals() method, the request code is not currently in use for an equal Intent, or an appropriate flag is passed.
Different extras on an otherwise-same Intent with the same request code will not cause a new PendingIntent to be created. Depending on the flag passed in that case, those extras might be ignored, or overwrite those in a currently active PendingIntent, which can lead to incorrect results.
In our example, we're basically using the same Intent for each send, so we'll ensure a distinct PendingIntent for each by passing unique request codes. This simple example uses the size of a shrinking list for those codes, which will be unique in the context of a single run. The request code can ultimately be any arbitrary int, as long as you know it's unused at the time of request.
The system will want to cache these PendingIntents, should we need them again in the near future, so we'll also pass FLAG_ONE_SHOT to "clear them out" after use, and make sure we get the correct, current extras in subsequent runs.
public class SmsActivity extends Activity implements View.OnClickListener {
private static final String SMS_SENT_ACTION = "com.mycompany.myapp.SMS_SENT";
private static final String SMS_DELIVERED_ACTION = "com.mycompany.myapp.SMS_DELIVERED";
private static final String EXTRA_NUMBER = "number";
private static final String EXTRA_MESSAGE = "message";
// Initialize our sample numbers list.
private final List<String> numberList = new ArrayList<String>() {{
add("111-111-1111");
add("222-222-2222");
add("333-333-3333");
}};
// Initialize our sample message list.
private final List<String> messageList = new ArrayList<String>() {{
add("Hello.");
add("Howdy.");
add("Hi.");
}};
private SmsManager smsManager;
private IntentFilter intentFilter;
private BroadcastReceiver resultsReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sms);
findViewById(R.id.button_send).setOnClickListener(this);
smsManager = SmsManager.getDefault();
resultsReceiver = new SmsResultReceiver();
intentFilter = new IntentFilter(SMS_SENT_ACTION);
intentFilter.addAction(SMS_DELIVERED_ACTION);
}
#Override
protected void onResume() {
super.onResume();
registerReceiver(resultsReceiver, intentFilter);
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(resultsReceiver);
}
public void onClick(View v) {
v.setEnabled(false);
sendNextMessage();
}
private void sendNextMessage() {
// We're going to remove numbers and messages from
// the lists as we send, so if the lists are empty, we're done.
if (numberList.size() == 0) {
return;
}
// The list size is a sufficiently unique request code,
// for the PendingIntent since it decrements for each send.
int requestCode = numberList.size();
String number = numberList.get(0);
String message = messageList.get(0);
// The Intents must be implicit for this example,
// as we're registering our Receiver dynamically.
Intent sentIntent = new Intent(SMS_SENT_ACTION);
Intent deliveredIntent = new Intent(SMS_DELIVERED_ACTION);
// We attach the recipient's number and message to
// the Intents for easy retrieval in the Receiver.
sentIntent.putExtra(EXTRA_NUMBER, number);
sentIntent.putExtra(EXTRA_MESSAGE, message);
deliveredIntent.putExtra(EXTRA_NUMBER, number);
deliveredIntent.putExtra(EXTRA_MESSAGE, message);
// Construct the PendingIntents for the results.
// FLAG_ONE_SHOT cancels the PendingIntent after use so we
// can safely reuse the request codes in subsequent runs.
PendingIntent sentPI = PendingIntent.getBroadcast(this,
requestCode,
sentIntent,
PendingIntent.FLAG_ONE_SHOT);
PendingIntent deliveredPI = PendingIntent.getBroadcast(this,
requestCode,
deliveredIntent,
PendingIntent.FLAG_ONE_SHOT);
// Send our message.
smsManager.sendTextMessage(number, null, message, sentPI, deliveredPI);
// Remove the number and message we just sent to from the lists.
numberList.remove(0);
messageList.remove(0);
}
private class SmsResultReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// A simple result Toast text.
String result = null;
// Get the result action.
String action = intent.getAction();
// Retrieve the recipient's number and message.
String number = intent.getStringExtra(EXTRA_NUMBER);
String message = intent.getStringExtra(EXTRA_MESSAGE);
// This is the result for a send.
if (SMS_SENT_ACTION.equals(action)) {
int resultCode = getResultCode();
result = "Send result : " + translateSentResult(resultCode);
// The current send is complete. Send the next one.
sendNextMessage();
}
// This is the result for a delivery.
else if (SMS_DELIVERED_ACTION.equals(action)) {
SmsMessage sms = null;
// A delivery result comes from the service
// center as a simple SMS in a single PDU.
byte[] pdu = intent.getByteArrayExtra("pdu");
String format = intent.getStringExtra("format");
// Construct the SmsMessage from the PDU.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && format != null) {
sms = SmsMessage.createFromPdu(pdu, format);
}
else {
sms = SmsMessage.createFromPdu(pdu);
}
// getResultCode() is not reliable for delivery results.
// We need to get the status from the SmsMessage.
result = "Delivery result : " + translateDeliveryStatus(sms.getStatus());
}
result = number + ", " + message + "\n" + result;
Toast.makeText(context, result, Toast.LENGTH_SHORT).show();
}
String translateSentResult(int resultCode) {
switch (resultCode) {
case Activity.RESULT_OK:
return "Activity.RESULT_OK";
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
return "SmsManager.RESULT_ERROR_GENERIC_FAILURE";
case SmsManager.RESULT_ERROR_RADIO_OFF:
return "SmsManager.RESULT_ERROR_RADIO_OFF";
case SmsManager.RESULT_ERROR_NULL_PDU:
return "SmsManager.RESULT_ERROR_NULL_PDU";
case SmsManager.RESULT_ERROR_NO_SERVICE:
return "SmsManager.RESULT_ERROR_NO_SERVICE";
default:
return "Unknown error code";
}
}
String translateDeliveryStatus(int status) {
switch (status) {
case Telephony.Sms.STATUS_COMPLETE:
return "Sms.STATUS_COMPLETE";
case Telephony.Sms.STATUS_FAILED:
return "Sms.STATUS_FAILED";
case Telephony.Sms.STATUS_PENDING:
return "Sms.STATUS_PENDING";
case Telephony.Sms.STATUS_NONE:
return "Sms.STATUS_NONE";
default:
return "Unknown status code";
}
}
}
}
The translateSentResult() method is modified from a similar one in AOSP's old default SMS app. Licensed under the Apache License, Version 2.0.
Notes:
Do make note of the method we're using to get the delivery status. The result code in the Receiver is not a reliable indicator. We must check the getStatus() return of the SmsMessage obtained from the PDU extra on the Intent to get the actual result.
Also be aware that not all carriers provide delivery results, in which case the delivery PendingIntents will never fire. Do not rely on a delivery result.
This example uses a "correct", albeit simple, method to sequentially send multiple messages, in that it waits until the current send is compete before proceeding to the next. For short lists, you might be able to get away with a loop firing all of the sends as quickly as it executes, but this can result in a generic failure if the system can't keep up.
As noted, this is a very simple example. It is not really suitable for production, as the dynamically registered Receiver is tied to the Activity's lifecycle. Ideally, you'd want to implement a static Receiver class, registered in the manifest, and use explicit Intents to target it. Using a Service to process the results is also recommended, and those results could be delivered to the UI through any number of mechanisms; e.g., LocalBroadcastManager, another event bus implementation, Intents, Notifications, etc.

Getting the outbound phonenumber in Android

Bellow a simplified piece of my code is shown. The important part is that I now have phone number when the call state is CALL_STATE_RINGING and the call is inbound. Now I also want the phone number when the call is outbound (so the phone number on the receiving end). What am I missing here?
#Override
public void onCallStateChanged(int state, String number) {
Log.d("BackgroundService", "State: "+ state +" Number: " + number);
}
telephonyManager.listen(phoneStateListener,
PhoneStateListener.LISTEN_CALL_STATE);
Register a broadcast receiver using ACTION_NEW_OUTGOING_CALL. In the onReceive callback function you will know the number of outgoing call
public void onReceive(Context context, Intent intent) {
String phone = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
}
And offcourse use the permission android.permission.PROCESS_OUTGOING_CALLS

how to get the state for outgoing calls

In the OnReceive method I have something like this:
Bundle bundle=intent.getExtras();
String phonenumber=intent.getStrngExtra(Intent.EXTRA_PHONE_NUMBER);
How to chech if the dialing call is still on or the client hanged up the call?
How to check if the call was answered?
I need to print up a toat when the client hanged up the call or when the called client answered to the call.
You will need a broadcast receiver registered for action android.intent.action.PHONE_STATEiF THE phone state has not changed to idle once it is offhook, it means the call is still going on.
the call was answered if the state in read phone state broadcast receiver changes to offhook. Put a toast as need in these states.
public class CallDurationReceiver extends BroadcastReceiver {
static boolean flag =false;
static long start_time,end_time;
#Override
public void onReceive(Context arg0, Intent intent) {
String action = intent.getAction();
if(action.equalsIgnoreCase("android.intent.action.PHONE_STATE")){
if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
TelephonyManager.EXTRA_STATE_RINGING)) {
//tOAST FOR INCOMING CALL, NOT YET PICKED UP
}
if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
TelephonyManager.EXTRA_STATE_IDLE)) {
end_time=System.currentTimeMillis();
//Total time talked =
long total_time = end_time-start_time;
//Store total_time somewhere or pass it to an Activity using intent
} if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
TelephonyManager.EXTRA_STATE_OFFHOOK)) {
start_time=System.currentTimeMillis();
}
}
}
Register your receiver in your manifest file like this:
<receiver android:name=".CallDurationReceiver">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
}
Also add the uses permission:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Well all I find out a solution for this and I successfully implemented it.Its not possible to fetch the exact time when the callee has accepted an outgoing call.
Before picking up a call in the other end it has already passed through 2 stages namely on_State_idle and on_state_offhook. On_state_ringing is not working for the outgoing calls.
Let's assume a phone is ringing for 40sec (this am not sure) continuously if the person at the other side didn't pick the call.
Start a timer along with the starting stage of on_State_idle and on_state_offhook.
Two cases if the timer cross above 40sec means the person at the other hand pick my call.
If on_State_idle->on_state_offhook->on_State_idle worked within 40sec means the other hand didn't pick my call.
If the second case is true, fetch the call talk duration from the call log.
Totaltimer running time - time in call log gives you the exact time of picking of the outgoing Call!
you can use the below code for handling call state:::
private Runnable callMonitor = new Runnable() {
public void run() {
try {
EndCallListener callListener = new EndCallListener();
TelephonyManager mTM = (TelephonyManager)m_activity.getSystemService(Context.TELEPHONY_SERVICE);
mTM.listen(callListener, PhoneStateListener.LISTEN_CALL_STATE);
} catch(Exception e) {
Log.e("callMonitor", "Exception: "+e.toString());
}
}
};
private class EndCallListener extends PhoneStateListener {
private boolean active = false;
#Override
public void onCallStateChanged(int state, String incomingNumber) {
if(TelephonyManager.CALL_STATE_RINGING == state) {
Log.i("EndCallListener", "RINGING, number: " + incomingNumber);
}
if(TelephonyManager.CALL_STATE_OFFHOOK == state) {
//wait for phone to go offhook (probably set a boolean flag) so you know your app initiated the call.
active = true;
Log.i("EndCallListener", "OFFHOOK");
}
if(TelephonyManager.CALL_STATE_IDLE == state) {
//when this state occurs, and your flag is set, restart your app
Log.i("EndCallListener", "IDLE");
if (active) {
active = false;
// stop listening
TelephonyManager mTM = (TelephonyManager)m_activity.getSystemService(Context.TELEPHONY_SERVICE);
mTM.listen(this, PhoneStateListener.LISTEN_NONE);
// restart the inbox activity
// Intent intent = new Intent(m_activity, MDInboxActivity.class);
// m_activity.startActivity(intent);
}
}
}
}

Categories

Resources