How to Get Sms Sent Confirmation for Each Contact/Person in Android

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

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";
}
}
}
}

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.

Android: send SMS to multiple recepients and get confirmation

1) idx changes as you run through the for-loop. Thus, each time you toast, you're showing the current value for idx, which is the number of messages being shown. Since you've packed it in your intent, you can simply show the text "Delivered" + intent.getIntExtra(EXTRA_IDX, -1) in your onReceive method.

2) I'm not sure what you're asking.

3) I'm not sure off-hand, and can't currently debug.

4) You're going to have to keep track of which indices you've received. A HashSet<Integer> should do the trick.

Above your for loop, add this:

final HashSet<Integer> undelivered = new HashSet<Integer>();

In your for loop, add this:

undelivered.add(idx);

To answer your questions for 1, 3, and 4 at once, change your onReceived body to this:

// Get the index from the intent
int idx = intent.getIntExtra(EXTRA_IDX, -1);

if (undelivered.contains(idx)) {
// This index is now delivered. We remove it from the undelivered set, and Toast that it was delivered.
undelivered.remove(idx);
Toast.makeText(getApplicationContext(), "Delivered " + idx, Toast.LENGTH_SHORT).show();

if (undelivered.isEmpty() {
// We've delivered all of the messages ...
Toast.makeText(getApplicationContext(), "All messages were delivered.", Toast.LENGTH_SHORT).show();
}
}

Practical way to find out if SMS has been sent

Yes, It is possible to listen SMS ContentProvider by using ContentObserver

Here is my example for Outgoing SMS:

First register a ContetObserver with content://sms/

   public class Smssendservice extends Service{

@Override
public void onCreate() {
SmsContent content = new SmsContent(new Handler());
// REGISTER ContetObserver
this.getContentResolver().
registerContentObserver(Uri.parse("content://sms/"), true, SMSObserver);
}

@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub

return null;
}

SMSObserver.class

       public class SMSObserver extends ContentObserver {
private Handler m_handler = null;

public SMSObserver(SMSLogger handler){
super(handler);
m_handler = handler;
}

@Override
public void onChange(boolean selfChange) {
super.onChange(bSelfChange);
Uri uriSMSURI = Uri.parse("content://sms");

Cursor cur = this.getContentResolver().query(uriSMSURI, null, null,
null, null);
cur.moveToNext();

String protocol = cur.getString(cur.getColumnIndex("protocol"));

if(protocol == null) {
//the message is sent out just now
}
else {
//the message is received just now
}
}
}

}

How to send sms to multiple contacts and get the result code for each of them in android

This may help you

private void sendSMS(final String phoneNumber, String message,String messageID)
{
PendingIntent pi = PendingIntent.getActivity(this, 0,
new Intent(this, SM.class), 0);
pi.putExtras("messageID",messageID);
SmsManager sms = SmsManager.getDefault();
//sms.sendTextMessage(phoneNumber, null, message, pi, null);

String SENT = "SMS_SENT";
String DELIVERED = "SMS_DELIVERED";

PendingIntent sentPI = PendingIntent.getBroadcast(this, 0,
new Intent(SENT), 0);

PendingIntent deliveredPI = PendingIntent.getBroadcast(this, 0,
new Intent(DELIVERED), 0);
sms.sendTextMessage(phoneNumber, null, message, sentPI, deliveredPI);

//---when the SMS has been sent---
registerReceiver(new BroadcastReceiver(){
@Override
public void onReceive(Context arg0, Intent arg1) {
//showMessage("sms has been sent with reusltcode "+getResultCode());
String id=arg1.getBundle().getExtras().getString("messageID");
//This will recognize your message

switch (getResultCode())
{
case Activity.RESULT_OK:
Toast.makeText(getBaseContext(), "SMS sent to "+phoneNumber,
Toast.LENGTH_LONG).show();
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
Toast.makeText(getBaseContext(), "Generic failure",
Toast.LENGTH_SHORT).show();

break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
Toast.makeText(getBaseContext(), "No service",
Toast.LENGTH_SHORT).show();

break;
case SmsManager.RESULT_ERROR_NULL_PDU:
Toast.makeText(getBaseContext(), "Null PDU",
Toast.LENGTH_SHORT).show();

break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
Toast.makeText(getBaseContext(), "Radio off",
Toast.LENGTH_SHORT).show();
break;
}
}
}, new IntentFilter(SENT));

//---when the SMS has been delivered---
registerReceiver(new BroadcastReceiver(){
public void onReceive(Context arg0, Intent arg1) {
showMessage("sms has been delivered with reusltcode "+getResultCode());

switch (getResultCode())
{
case Activity.RESULT_OK:
Toast.makeText(getBaseContext(), "SMS delivered",
Toast.LENGTH_SHORT).show();
break;
case Activity.RESULT_CANCELED:
Toast.makeText(getBaseContext(), "SMS not delivered",
Toast.LENGTH_SHORT).show();
break;
}
}
}, new IntentFilter(DELIVERED));

}

How to send sms from selected contact in android?

You can use SmsManager class.

SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage("phoneNo", null, "sms message", null, null);

or built-in android sms application.

Intent sendIntent = new Intent(Intent.ACTION_VIEW);
sendIntent.putExtra("sms_body", "default content");
sendIntent.setType("vnd.android-dir/mms-sms");
startActivity(sendIntent);

Note: both need SEND_SMS permission

<uses-permission android:name="android.permission.SEND_SMS" />

How to get sms data in a Broadcast receiver

Here's your problem:

registerReceiver(..., new IntentFilter(SENT));

You're registering that Receiver for the action you're using for the sent confirmation, not for the SMS_RECEIVED action, which is what is broadcast when a message arrives.

The sent PendingIntent is used to confirm that the message has successfully left the device. The resulting Intent from that will not have any message PDUs on it, which is why you're crashing.

The action you need to register an incoming SMS Receiver for is "android.provider.Telephony.SMS_RECEIVED", or the constant Telephony.Sms.Intents.SMS_RECEIVED_ACTION, if you're compiling with API 19 or above.


To address the specific questions:

  1. Do I need the Receiver/Intent-filter block in my manifest, and if so, how do I name the listener in the first line?

Registering a Receiver in the manifest will allow your app to receive messages even when it's not already running, or in the foreground. If you don't need to do that, you don't necessarily need to register in the manifest. However, if the user navigates away from your Activity before receiving the message, the paused Activity won't get the broadcast.

The "safest" way to handle it would be to register in the manifest, but there you're registering a class, not a dynamic instance. You would need to create a MyBroadcastListener class, and somehow notify the Activity from that upon receipt; e.g., using startActivity(), or LocalBroadcastManager, or some other event bus implementation, etc.


  1. What is the likely reason I can not receive a copy of the incoming message in onReceive(), and what should I do to correct it?

As mentioned above, the PendingIntent passed in the sendTextMessage() call is for sent confirmation. Its resulting Intent won't have the incoming message on it. You just need to listen for the correct broadcast.


  1. Do I need to send a message and "catch it" in onReceive() in order to reliably get the devices phone number, or can I request permission to read SMS from the user, and just read the first message to get the incoming phone number?

You need to actually send a message. Incoming messages won't have the recipient number attached.

How to send sms to multiple contacts and get the result code for each of them in android

This may help you

private void sendSMS(final String phoneNumber, String message,String messageID)
{
PendingIntent pi = PendingIntent.getActivity(this, 0,
new Intent(this, SM.class), 0);
pi.putExtras("messageID",messageID);
SmsManager sms = SmsManager.getDefault();
//sms.sendTextMessage(phoneNumber, null, message, pi, null);

String SENT = "SMS_SENT";
String DELIVERED = "SMS_DELIVERED";

PendingIntent sentPI = PendingIntent.getBroadcast(this, 0,
new Intent(SENT), 0);

PendingIntent deliveredPI = PendingIntent.getBroadcast(this, 0,
new Intent(DELIVERED), 0);
sms.sendTextMessage(phoneNumber, null, message, sentPI, deliveredPI);

//---when the SMS has been sent---
registerReceiver(new BroadcastReceiver(){
@Override
public void onReceive(Context arg0, Intent arg1) {
//showMessage("sms has been sent with reusltcode "+getResultCode());
String id=arg1.getBundle().getExtras().getString("messageID");
//This will recognize your message

switch (getResultCode())
{
case Activity.RESULT_OK:
Toast.makeText(getBaseContext(), "SMS sent to "+phoneNumber,
Toast.LENGTH_LONG).show();
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
Toast.makeText(getBaseContext(), "Generic failure",
Toast.LENGTH_SHORT).show();

break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
Toast.makeText(getBaseContext(), "No service",
Toast.LENGTH_SHORT).show();

break;
case SmsManager.RESULT_ERROR_NULL_PDU:
Toast.makeText(getBaseContext(), "Null PDU",
Toast.LENGTH_SHORT).show();

break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
Toast.makeText(getBaseContext(), "Radio off",
Toast.LENGTH_SHORT).show();
break;
}
}
}, new IntentFilter(SENT));

//---when the SMS has been delivered---
registerReceiver(new BroadcastReceiver(){
public void onReceive(Context arg0, Intent arg1) {
showMessage("sms has been delivered with reusltcode "+getResultCode());

switch (getResultCode())
{
case Activity.RESULT_OK:
Toast.makeText(getBaseContext(), "SMS delivered",
Toast.LENGTH_SHORT).show();
break;
case Activity.RESULT_CANCELED:
Toast.makeText(getBaseContext(), "SMS not delivered",
Toast.LENGTH_SHORT).show();
break;
}
}
}, new IntentFilter(DELIVERED));

}


Related Topics



Leave a reply



Submit