We're using the SMSManager on Android to send a text message to someone else from the device.
We've had reports of a user not receiving an SMS that we thought was delivered based on the fact that the sentIntent got called with Activity.RESULT_OK - but that seems to happen when the local SMS service simply queues the message for delivery and not necessarily when it is truly sent.
So I thought we should make use of the deliveryIntent.
The problem here is that I always get the sentIntent callback, but never the deliveryIntent one.
Any ideas?
Code is below, thanks.
// The intent action to be unique so that we can have multiple
// concurrent pending intents.
// http://developer.android.com/reference/android/app/PendingIntent.html
String intentAction = TAG + "-" + callbackId; // callbackId is unique
Intent intent = new Intent(intentAction);
intent.putExtra("phoneNumber", phoneNumber);
intent.putExtra("callbackId", callbackId);
intent.putExtra("message", message);
PendingIntent sentPI = PendingIntent.getBroadcast(
cordova.getActivity(), 0, intent, 0);
cordova.getActivity().registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context ctx, Intent intent) {
String sentToPhoneNumber = intent.getStringExtra("phoneNumber");
String callbackId = intent.getStringExtra("callbackId");
String message = intent.getStringExtra("message");
int resultCode = getResultCode();
int status = -1;
String details = "";
logger.log(Level.INFO, TAG + " SENT intent!! to: " +
sentToPhoneNumber + ", resultCode: " + resultCode);
switch (resultCode) {
case Activity.RESULT_OK:
status = 0;
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
details = "No service";
case SmsManager.RESULT_ERROR_NULL_PDU:
details = "Null PDU";
case SmsManager.RESULT_ERROR_RADIO_OFF:
details = "Radio off";
status = 1;
break;
}
JSONObject obj = new JSONObject();
try {
obj.put("status", status);
obj.put("details", details);
obj.put("phone_number", sentToPhoneNumber);
obj.put("message", message);
} catch (JSONException e) {
throw new RuntimeException(e);
}
sendAsyncResultStatus(callbackId, obj);
ctx.unregisterReceiver(this);
}
}, new IntentFilter(intentAction));
// The intent action to be unique so that we can have multiple
// concurrent pending intents.
// http://developer.android.com/reference/android/app/PendingIntent.html
String deliveryIntentAction = TAG + "-Delivery-" + callbackId;
Intent deliveryIntent = new Intent(deliveryIntentAction);
deliveryIntent.putExtra("phoneNumber", phoneNumber);
deliveryIntent.putExtra("callbackId", callbackId);
deliveryIntent.putExtra("message", message);
PendingIntent deliveryPI = PendingIntent.getBroadcast(
cordova.getActivity(), 0, deliveryIntent, 0);
cordova.getActivity().registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context ctx, Intent intent) {
logger.log(Level.INFO, TAG + " DELIVERY intent YOYO!!");
String sentToPhoneNumber = intent.getStringExtra("phoneNumber");
String callbackId = intent.getStringExtra("callbackId");
String message = intent.getStringExtra("message");
String pdu = intent.getStringExtra("pdu");
logger.log(Level.INFO, TAG + " DELIVERY intent!! to: " +
sentToPhoneNumber + ", pdu: " + pdu);
JSONObject obj = new JSONObject();
try {
obj.put("pdu", pdu);
obj.put("phone_number", sentToPhoneNumber);
obj.put("message", message);
} catch (JSONException e) {
throw new RuntimeException(e);
}
sendAsyncResultStatus(callbackId, obj);
ctx.unregisterReceiver(this);
}
}, new IntentFilter(deliveryIntentAction));
smsManager.sendTextMessage(phoneNumber, null, message, sentPI, deliveryPI);
DeliveryIntent is dependent on DeliveryReports provided by some carriers.
Some Carriers do provide DeliveryReports and some don't. There are three categories in terms of carriers support of DliveryReports...
Provider does not provide DeliveryReport at all.
Provider always give a fake DeliveryReport (you will get an OK report even for invalid numbers)
Provider does provide the delivery report.
The Complication doesn't stop here... If your provider (sending provider) does support delivery reports, you will get the delivery reports mostly when you are sending messages to subscribers of the same carrier. But when you send the message outside this carrier you may or may not get the delivery report. In most cases you won't.
This can be caused by a verity of reasons e.g.
Destination provider doesn't support delivery reports
Some of the intermediate SMS gateway(s) do not support delivery reports
DeliveryReports of Destination carrier or some intermediate gateways are not compatible with those of Originating Carriers.
Delivery Reports in such case are not a guaranteed information in my knowledge so far.
I could imagine that delivery intents depend on the reception of Return Receipts. The network provider needs to send these in order to make your program work.
You can do this by opening the "settings menu", browse to "SMS settings" and tick the box "Return receipt".
Related
My app sends SMS via SmsManager.sendMultipartTextMessage and then notifies the server about the status of SMS sending.
Everything works fine, but there is a problem with Yota operator. SMS is not delivered to phones with MTS operator. Employees of the Yota operator claim that MTS operator blocks receiving messages from Yota.
This is not our problem, but the Android system says that such SMS successfully delivered. We cannot redeliver such SMS from another phone because our system thinks that they have already been successfully delivered.
If app tries to send such SMS, the system first signals about successful SMS sending and then about successful SMS delivery. Sounds good, but in fact the SMS is not delivered. This is not an isolated case. Tested on different devices. Also tried to send SMS to different phones with MTS operator.
I tried to send the same SMS through a standard "Messages" application and noticed the following:
SMS, sent from my application, are displayed differently in a standard "Messages" application on different devices. For example, ZTE BLADE L110 (API 22) shows an error sending a message and Xiaomi Redmi 3S (API 23) does not. But on both smartphones when I try to send such SMS via the standard application is shown error sending message (ZTE before this shows Toast "SMS successfully sent").
Who can help? Can there be another way to check if the SMS is delivered or not? How does the standard "Messages" application register the fact of SMS sending error while the system generates intents with signals that the message was successfully sent and delivered?
Here is my code:
Sending SMS job:
public class SendSMSJob extends SimpleJobService {
public static final String EXTRA_SMS_LIST_JSON = "sms_list";
public static final String TAG = "send_sms_job";
private static final String ACTION_SENT = "ru.sp2all.smsgate.SMS_SENT";
private static final String ACTION_DELIVERED = "ru.sp2all.smsgate.SMS_DELIVERED";
private int startId;
#Override
public void onCreate() {
super.onCreate();
MyLog.i(getLogTag(), "onCreate()");
}
#Override
public int onRunJob(JobParameters parameters) {
try {
this.startId = startId;
String json = parameters.getExtras().getString(EXTRA_SMS_LIST_JSON);
Gson gson = new Gson();
SMSDataList smsDataList = gson.fromJson(json, SMSDataList.class);
MyLog.d(getLogTag(), "Sending " + String.valueOf(smsDataList.items.length) + " messages");
boolean complete = true;
boolean needRetry = true;
for (SMSData sms: smsDataList.items) {
Integer subscriptionId = SimsHelper.getSimSubscriptionIdForIndex(getApplicationContext(), sms.simIndex);
SmsManager smsManager = null;
if (subscriptionId != null) {
smsManager = SmsManager.getSmsManagerForSubscriptionId(subscriptionId);
} else {
smsManager = SmsManager.getDefault();
}
ArrayList<String> parts = smsManager.divideMessage(sms.message);
MyLog.i(getLogTag(), "parts.size=" + parts.size());
insertSmsToDB(sms, parts);
ArrayList<PendingIntent> deliveryIntents = new ArrayList<>(parts.size());
ArrayList<PendingIntent> sentIntents = new ArrayList<>(parts.size());
try {
for (int part = 0; part < parts.size(); part++) {
Intent sentIntent = new Intent(ACTION_SENT);
sentIntent.putExtra("id", sms.smsId + "_" + Integer.toString(part));
sentIntent.putExtra("sms_id", sms.smsId);
sentIntent.putExtra("parts", Integer.toString(parts.size()));
sentIntent.putExtra("part", Integer.toString(part));
sentIntent.putExtra("phone", sms.phone);
sentIntent.putExtra("msg", sms.message);
PendingIntent sentPI = PendingIntent.getBroadcast(this,
Integer.parseInt(sms.smsId) * 100 + part,
sentIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
sentIntents.add(sentPI);
Intent deliveredIntent = new Intent(ACTION_DELIVERED);
deliveredIntent.putExtra("id", sms.smsId + "_" + Integer.toString(part));
deliveredIntent.putExtra("sms_id", sms.smsId);
deliveredIntent.putExtra("parts", Integer.toString(parts.size()));
deliveredIntent.putExtra("part", Integer.toString(part));
deliveredIntent.putExtra("phone", sms.phone);
deliveredIntent.putExtra("msg", sms.message);
PendingIntent deliveredPI = PendingIntent.getBroadcast(this,
Integer.parseInt(sms.smsId) * 100 + part,
deliveredIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
deliveryIntents.add(deliveredPI);
}
MyLog.i(getLogTag(), "Sending to " + sms.phone);
smsManager.sendMultipartTextMessage(sms.phone, null, parts,
sentIntents, deliveryIntents);
} catch (Exception e) {
ErrorReporter.exception(this, getLogTag(), e, new ErrorReporter.ReporterHandler(e));
complete = true;
}
}
if (complete) {
return RESULT_SUCCESS;
} else if (needRetry) {
return RESULT_FAIL_RETRY;
} else {
return RESULT_FAIL_NORETRY;
}
} catch (Exception e) {
ErrorReporter.exception(this, getLogTag(), e, new ErrorReporter.ReporterHandler(e));
return RESULT_FAIL_RETRY;
}
}
private String getLogTag() {
return getClass().getSimpleName();
}
}
Registered in AndroidManifest Broadcast receivers for SMS status handling:
<receiver android:name=".SMSSentReceiver">
<intent-filter>
<action android:name="ru.sp2all.smsgate.SMS_SENT" />
</intent-filter>
</receiver>
<receiver android:name=".SMSDeliveredReceiver">
<intent-filter>
<action android:name="ru.sp2all.smsgate.SMS_DELIVERED" />
</intent-filter>
</receiver>
SMS sent BroadcastReceiver:
public class SMSSentReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
try {
String sms_id = intent.getStringExtra("sms_id");
int part = Integer.parseInt(intent.getStringExtra("part"));
MyLog.d(getLogTag(), "br_sent " + sms_id);
int status = 0;
int code = getResultCode();
switch (code) {
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
status = 400;
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
status = 401;
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
status = 402;
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
status = 403;
break;
case Activity.RESULT_OK:
updateDBWithNewSentPartsCount();
status = 202; // 202 is SMS sent Server status
break;
default:
status = 404; break;
}
if (status == 202) {
int sent = getSentPartsFromDB();
int parts = getAllPartsFromDB();
MyLog.d(getLogTag(), "DB sent:" + sms_id + " - : sent:" + sent + " parts: " + parts);
if (sent >= parts) {
MyLog.d(getLogTag(), "DB sent all parts");
Network.sendSMSStatus(context, sms_id, status);
}
} else {
Network.sendSMSStatus(context, sms_id, status);
}
} catch (Exception e) {
ErrorReporter.exception(context, getLogTag(), e, new ErrorReporter.ReporterHandler(e));
}
}
private String getLogTag() {
return getClass().getSimpleName();
}
}
SMS delivered Broadcast Receiver:
public class SMSDeliveredReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
try {
String sms_id = intent.getStringExtra("sms_id");
int part = Integer.parseInt(intent.getStringExtra("part"));
MyLog.d(getLogTag(), "br_delivered " + sms_id);
int resultCode = getResultCode();
switch (resultCode) {
case Activity.RESULT_OK:
updateDBWithNewDeliveredPartsCount();
int delivered = getDeliveredPartsFromDB();
int parts = getAllPartsFromDB();
MyLog.i(getLogTag(), "DB delivered: " + sms_id + ", " + delivered + " parts of " + parts);
if (delivered >= parts) {
MyLog.i(getLogTag(), "DB delivered all parts");
Network.sendSMSStatus(context, sms_id, 200); // 200 is SMS delivered Server status
}
break;
case Activity.RESULT_CANCELED:
MyLog.w(getLogTag(), "DB delivered: CANCELLED " + sms_id);
Network.sendSMSStatus(context, sms_id, 405);
break;
default:
MyLog.w(getLogTag(), "DB delivered: unknown code " + resultCode);
Network.sendSMSStatus(context, sms_id, resultCode);
}
} catch (Exception e) {
ErrorReporter.exception(context, getLogTag(), e, new ErrorReporter.ReporterHandler(e));
}
}
private String getLogTag() {
return getClass().getSimpleName();
}
}
Log from device when I try to send SMS and SMS not delivered actually:
SendSMSJob: onCreate()
SendSMSJob: Sending 1 messages
SendSMSJob: parts.size=1
SendSMSJob: Sending to +7988*******
SMSSentReceiver: br_sent 704402
SMSSentReceiver: DB sent:704402 - : sent:1 parts: 1
SMSSentReceiver: DB sent all parts
Network: sendSMSStatus status: 202
Network: get: https://{SERVER_NAME}/result.php?status=202&smsId=704402
Network: got: {"status":200}
SMSDeliveredReceiver: br_delivered 704402
SMSDeliveredReceiver: DB delivered: 704402, 1 parts of 1
SMSDeliveredReceiver: DB delivered all parts
Network: sendSMSStatus status: 200
Network: get: https://{SERVER_NAME}/result.php?status=200&smsId=704402
Network: got: {"status":200}
Text of SMS for test: gate4
SMS is not an acknowledged protocol. You can't know if it has been delivered from the sending end. The sender can only know when the message is sent. The send is reporting correctly, as the data is being sent, but it is being blocked at the other end.
If you need acknowledged SMS you can reply from the receiver, but this will involve developing you own acknowledging protocol.
I'd just poll a web service from the device. Data is a LOT cheaper than SMS messages anyway.
Good luck.
I want to send to multiple number of recepients.
I also want to use the built in SMS mechanism without being prompt for a required App (Whatsapp, etc.)
In order to acomplish this, I am using Android's SmsManager.
The for loop iterates through the mobileList array of mobile numbers and send SMS to each one of them, one by one.
The indication for delivered SMS is retrieved by the BroadcastReceiver for the deliveredActionIntent intent.
I am popping a toast with the word "Delivered" and the index number of the message being delivered.
My questions are:
The actual index (idx) is not shown. I get for all toasts the same index number which is the number of mobileList items.
Why is this happening? I expected the index for each mobile by itself.
Is the number of mobileList items limited? Can I have 200 people for instance?
I tested this on a list of 4 mobile numbers but then I got 8-10 toasts. I expected one toast for one mobile delivery.
What is wrong here?
How can I get a notification when all SMSs are delivered? I guess this should be a background action like AsyncTask.
Can someone please show me how to do this?
The code of the SmsManager is shown below.
SmsManager smsManager = SmsManager.getDefault();
for(idx = 0; idx < mobileList.length; idx++) {
String toNumber = mobileList[idx];
String sms = message;
// SMS sent pending intent
Intent sentActionIntent = new Intent(SENT_ACTION);
sentActionIntent.putExtra(EXTRA_IDX, idx);
sentActionIntent.putExtra(EXTRA_TONUMBER, toNumber);
sentActionIntent.putExtra(EXTRA_SMS, sms);
PendingIntent sentPendingIntent = PendingIntent.getBroadcast(this, 0, sentActionIntent, PendingIntent.FLAG_UPDATE_CURRENT);
/* Register for SMS send action */
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String result = "";
switch (getResultCode()) {
case Activity.RESULT_OK:
result = "Transmission successful";
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
result = "Transmission failed";
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
result = "Radio off";
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
result = "No PDU defined";
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
result = "No service";
break;
}
// Toast.makeText(getApplicationContext(), result, Toast.LENGTH_SHORT).show();
}
}, new IntentFilter(SENT_ACTION));
// SMS delivered pending intent
Intent deliveredActionIntent = new Intent(DELIVERED_ACTION);
deliveredActionIntent.putExtra(EXTRA_IDX, idx);
deliveredActionIntent.putExtra(EXTRA_TONUMBER, toNumber);
deliveredActionIntent.putExtra(EXTRA_SMS, sms);
PendingIntent deliveredPendingIntent = PendingIntent.getBroadcast(this, 0, deliveredActionIntent, PendingIntent.FLAG_UPDATE_CURRENT);
/* Register for Delivery event */
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(getApplicationContext(), "Deliverd " + Integer.toString(idx), Toast.LENGTH_SHORT).show();
}
}, new IntentFilter(DELIVERED_ACTION));
//send
smsManager.sendTextMessage(toNumber, null, sms, sentPendingIntent, deliveredPendingIntent);
}
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();
}
}
I am sending SMS through my application using a very common way that is explained in pretty much all tutorials. I use sendMultipartTextMessage with "sent Intents" and "delivery Intents", then a Broadcast receiver listen for the results and print things.
But, everytime I try to send a SMS, even with something like 10 characters, I always get a "Generic failure".
My default SMS app is working perfectly and I can send/receive SMS/MMS without any troubles so it can't be a network issue. I don't want my app to become my new default SMS app, I just want it to be able to send a short SMS sometimes.
I tried a lot of things but everything has failed.
What is that issue and what can I do to get rid of it ?
Utils :
public static void sendSMS(Context context, String destination) {
final String srcPhoneNumber = PreferenceManager.getDefaultSharedPreferences(context).getString(OptionsFragment.SMS_SRC_PHONE_NUMBER, null);
final String message = PreferenceManager.getDefaultSharedPreferences(context).getString(OptionsFragment.SMS_MESSAGE, null);
if (message == null || message.isEmpty() || destination == null || destination.isEmpty()) {
Console.log('e', "tag", "sms sending failure");
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("sms");
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra("sms", context.getString(R.string.empty_message));
MyApplication.getInstance().sendBroadcast(broadcastIntent);
return ;
}
final List<String> phoneNumbers = getPhoneNumbers();
removeEmptyElement(phoneNumbers);
SmsManager smsManager = SmsManager.getDefault();
ArrayList<String> parts = smsManager.divideMessage(message);
ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>();
ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();
PendingIntent sentPI = PendingIntent.getBroadcast(MyApplication.getInstance(), 0, new Intent("sms"), 0);
PendingIntent deliveredPI = PendingIntent.getBroadcast(MyApplication.getInstance(), 0, new Intent("sms"), 0);
for (String part : parts) {
sentIntents.add(sentPI);
deliveryIntents.add(deliveredPI);
}
SmsManager.getDefault().sendMultipartTextMessage(destination, srcPhoneNumber, parts, sentIntents, deliveryIntents);
}
Receiver :
#Override
public void onReceive(Context context, Intent intent) {
String s = intent.getAction();
if (s.equals("sms")) {
String message = intent.getStringExtra("sms");
if (message != null) {
if (message.equals("OK")) {
AlertDialog.Builder builder = new AlertDialog.Builder(HomeFragment.this.getActivity());
builder.setMessage(context.getString(R.string.sms_send_success))
.setCancelable(true)
.setTitle("Success");
builder.create().show();
} else {
Utils.makeErrorDialog(HomeFragment.this.getActivity(), message);
}
}
else {
switch (getResultCode())
{
case Activity.RESULT_OK:
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
Utils.makeErrorDialog(HomeFragment.this.getActivity(),context.getString(R.string.send_error) + "Generic failure");
progressDialog.dismiss();
return;
case SmsManager.RESULT_ERROR_NO_SERVICE:
Utils.makeErrorDialog(HomeFragment.this.getActivity(),context.getString(R.string.send_error) + "No service");
progressDialog.dismiss();
return;
case SmsManager.RESULT_ERROR_NULL_PDU:
Utils.makeErrorDialog(HomeFragment.this.getActivity(),context.getString(R.string.send_error) + "Null PDU");
progressDialog.dismiss();
return;
case SmsManager.RESULT_ERROR_RADIO_OFF:
Utils.makeErrorDialog(HomeFragment.this.getActivity(),context.getString(R.string.send_error) + "Radio off");
progressDialog.dismiss();
return;
}
...
The second parameter in the SmsManager#sendMultipartTextMessage() method (as well as the sendTextMessage() and sendDataMessage() methods) is for the number of your service center, not the sender's number. A service center is the part of your network that handles the storage, routing, and delivery of SMS messages, so passing an invalid number would result in the generic failure status you're getting. You simply need to pass null for this.
I'm rather new to Android.
Im trying to send SMS from Android application.
When using the SMS Intent the SMS window opens and the user needs to approve the SMS and send it.
Is there a way to automatically send the SMS without the user confirming it?
Thanks,
Lior
You can use this method to send an sms. If the sms is greater than 160 character then sendMultipartTextMessage is used.
private void sendSms(String phonenumber,String message, boolean isBinary)
{
SmsManager manager = SmsManager.getDefault();
PendingIntent piSend = PendingIntent.getBroadcast(this, 0, new Intent(SMS_SENT), 0);
PendingIntent piDelivered = PendingIntent.getBroadcast(this, 0, new Intent(SMS_DELIVERED), 0);
if(isBinary)
{
byte[] data = new byte[message.length()];
for(int index=0; index<message.length() && index < MAX_SMS_MESSAGE_LENGTH; ++index)
{
data[index] = (byte)message.charAt(index);
}
manager.sendDataMessage(phonenumber, null, (short) SMS_PORT, data,piSend, piDelivered);
}
else
{
int length = message.length();
if(length > MAX_SMS_MESSAGE_LENGTH)
{
ArrayList<String> messagelist = manager.divideMessage(message);
manager.sendMultipartTextMessage(phonenumber, null, messagelist, null, null);
}
else
{
manager.sendTextMessage(phonenumber, null, message, piSend, piDelivered);
}
}
}
Update
piSend and piDelivered are Pending Intent They can trigger a broadcast when the method finish sending an SMS
Here is sample code for broadcast receiver
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String message = null;
switch (getResultCode()) {
case Activity.RESULT_OK:
message = "Message sent!";
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
message = "Error. Message not sent.";
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
message = "Error: No service.";
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
message = "Error: Null PDU.";
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
message = "Error: Radio off.";
break;
}
AppMsg.makeText(SendMessagesWindow.this, message,
AppMsg.STYLE_CONFIRM).setLayoutGravity(Gravity.BOTTOM)
.show();
}
};
and you can register it using below line in your Activity
registerReceiver(receiver, new IntentFilter(SMS_SENT)); // SMS_SENT is a constant
Also don't forget to unregister broadcast in onDestroy
#Override
protected void onDestroy() {
unregisterReceiver(receiver);
super.onDestroy();
}
If your application has in the AndroidManifest.xml the following permission
<uses-permission android:name="android.permission.SEND_SMS"/>
you can send as many SMS as you want with
SmsManager manager = SmsManager.getDefault();
manager.sendTextMessage(...);
and that is all.
Yes, you can send SMS using the SmsManager. Please keep in mind that your application will need the SEND_SMS permission for this to work.
Yes, you can send sms without making user interaction...But it works, when user wants to send sms only to a single number.
try {
SmsManager.getDefault().sendTextMessage(RecipientNumber, null,
"Hello SMS!", null, null);
} catch (Exception e) {
AlertDialog.Builder alertDialogBuilder = new
AlertDialog.Builder(this);
AlertDialog dialog = alertDialogBuilder.create();
dialog.setMessage(e.getMessage());
dialog.show();
}
Also, add manifest permission....
<uses-permission android:name="android.permission.SEND_SMS"/>
So i have tried for a long time to find a way to make a app that can send and receive sms in android. That works fine. Here is the code:
For sending:
#SuppressWarnings("deprecation")
public void sendSMS(String phoneNumber, String message) {
String SENT = "SMS_SENT";
String DELIVERED = "SMS_DELIVERED";
int unq = 0;
Intent sent = new Intent(SENT);
sent.putExtra("unq", unq);
Intent delivered = new Intent(DELIVERED);
delivered.putExtra("unq", unq);
PendingIntent sentPI = PendingIntent.getBroadcast(this, 0, sent, 0);
PendingIntent deliveredPI = PendingIntent.getBroadcast(this, 0,delivered, 0);
// ---when the SMS has been sent---
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context arg0, Intent arg1) {
switch (getResultCode()) {
case Activity.RESULT_OK:
Toast.makeText(getBaseContext(), "SMS sent",
Toast.LENGTH_SHORT).show();
smsstatus = "0";
smserror = "noError";
//sendSmsStatus();
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
Toast.makeText(getBaseContext(), "Generic failure",
Toast.LENGTH_SHORT).show();
setSmsstatus("1");
setSmserror("Generic failure");
sendSmsStatus("Generic failure");
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
Toast.makeText(getBaseContext(), "No service",
Toast.LENGTH_SHORT).show();
setSmsstatus("2");
setSmserror("No service");
sendSmsStatus("No service");
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
Toast.makeText(getBaseContext(), "Null PDU",
Toast.LENGTH_SHORT).show();
setSmsstatus("3");
setSmserror("Null PDU");
sendSmsStatus("Null PDU");
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
Toast.makeText(getBaseContext(), "Radio off",
Toast.LENGTH_SHORT).show();
setSmsstatus("4");
setSmserror("Radio off");
sendSmsStatus("Radio off");
break;
}
}
}, new IntentFilter(SENT));
// ---when the SMS has been delivered---
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context arg0, Intent arg1) {
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));
SmsManager sms = SmsManager.getDefault();
sms.sendTextMessage(phoneNumber, null, message, sentPI, deliveredPI);
}
For receiving:
public class SmsReceiver extends BroadcastReceiver {
#SuppressWarnings("deprecation")
#Override
public void onReceive(Context context, Intent intent) {
// ---get the SMS message passed in---
Bundle bundle = intent.getExtras();
SmsMessage[] msgs = null;
String str = "";
Object sms = "";
ArrayList<String> s = new ArrayList<String>();
Manager m = Factory.getInstance().getManager();
if (bundle != null) {
// ---retrieve the SMS message received---
Object[] pdus = (Object[]) bundle.get("pdus");
msgs = new SmsMessage[pdus.length];
for (int i = 0; i < msgs.length; i++) {
msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
str += " SMS fra " + msgs[i].getOriginatingAddress() + "\n";
str += " Besked: ";
str += msgs[i].getMessageBody().toString();
str += "\n";
sms = "SMS = "+msgs[i].getOriginatingAddress()+","+msgs[i].getMessageBody().toString();
s.add(msgs[i].getOriginatingAddress());
s.add(msgs[i].getMessageBody().toString());
}
// ---display the new SMS message---
Toast.makeText(context, str, Toast.LENGTH_LONG).show();
Manager.toastIt(context, str);
// Send the sms to the server
//Connection.send(new Transmit("SmsReceived",s));
}
}
}
This works great!.
And here comes the question. Is it posible to refactore my code to achive the following:
Have a unique indentifier/flag on my send sms so i can make sure which sms i receive a status on. As you can see i have already tried to put extra on my 2 intents, and maybe that is the right way, but not only do i not now how to check/receive/extract the flag for the status, but also the flag really "unique" right now.
Its nice that I can revieve a sms, but when its more than 160 chars it only shows me the first 160 chars. I had looked at how GTalkSMS does it, but was hoping my code could just be refactored a bit :)
The last problem is, a mix of the 2 above. I cant send a sms thats more than 160 char. I know i have to use sendMultipartTextMessage, but i dont know how. My thought is that i could devide the "String message" by 100 to a Array but i dont know.
So feel free to refactore the code. I'm looking forward to see your replies! :D
Please ask if you need anything explained better or more code! :)
Not Android specific, but read about the "Concatenated SMS" standard. Basically it's multiple messages that each specify they go with the previous one, but it goes over the air as entirely independent SMS.
Most phones hide this fact from the user, of course, but if you're directly receiving SMS it's likely you'll need to deal with it yourself - sending and receiving. Assuming Android uses this standard, which seems like a safe bet.
Since it's so common, I'm sure you can find a library that somebody's already written.
http://en.wikipedia.org/wiki/Concatenated_SMS
Everything you asked for is in the GTalkSMS code (seems you have missed it). :-) But I will point you to the right snippets.
In order to use sendMultipartTextMessage() and distinguish the different sent/delivered notification intents you need first to split the message string via SmsManager.divideMessage(message) and create two, one for sent notifications and one for the delivered notifications, PendingIntents ArrayLists, where, and this is the important part, every PendingIntent has to be created with a a unique request int:
PendingIntent.getBroadcast(context, UNIQUE_ID, deliveredIntent, PendingIntent.FLAG_ONE_SHOT)
This assures that andoird will broadcast an unique intent for every sent delivered notification. You can also add some extras to the intent, which could later tell you for which SMS the notification was.
Code snippets can be found here: sendSMSByPhoneNumber()
Make sure that your SmsReceiver is aware that an incoming intent can contain more that one SMS for multiple senders, but a maximum of nbfOfpdus different senders. The GTalkSMS receiver will sure help you understand how to get this going. :)