I know this has been asked multiple times, but I am having problems making it work or figuring out what will work best. I need to have the sms wait 2-3 seconds between each message before sending another message. I have looked at and tried handlers, timers and thread sleep and I am not sure which one would be the best use in my situation, or how to make it work right. I am still new to programming, so please take it easy on me.
// ---sends an SMS message---
private void sendSMS(String phoneNumber, String message) {
int i;
SmsManager sms = SmsManager.getDefault();
int amount = 10; // just making 10 the default if the EditText has an
// invalid value
try {
amount = Integer.parseInt(smsamount.getText().toString());
} catch (NumberFormatException smsamount) {
}
if (amount < 501) {
for (i = 0; i < amount; i++) {
sms.sendTextMessage(phoneNumber, null, message, sentPI, null);
}
if you want to use 2 seconds delay between each sms, use a ScheduledExecutorService thread pool (1 thread is probably enough cause you don't send parallel) and call schedule method with the code to send the sms.
For each call, raise the delay parameter by 2 seconds (0,2,4,6,...)
Hope it helps.
Maybe something like this. I've not tested it, but the idea of using a ScheduledExecutorService should be what you're after.
public class SMS extends Activity {
private final static OnClickListener EMPTY_ON_CLICK_LISTENER = new EmptyOnClickListener();
TextView smsamount;
// ---sends an SMS message---
private void sendSMS(String phoneNumber, String message) {
// just making 10 the default if the EditText has an invalid value
int amount = 10;
try {
amount = Integer.parseInt(smsamount.getText().toString());
} catch (NumberFormatException smsamount) {
// Ignore
}
sendSMS(phoneNumber, message, amount);
}
// ---sends an SMS message---
private void sendSMS(String phoneNumber, String message, int count) {
if (count >= 501) {
new AlertDialog.Builder(SMS.this).setTitle("Maximum amount of messages exceeded!")
.setMessage("Please enter 500 or less for the amount of messages")
.setNeutralButton("Ok", EMPTY_ON_CLICK_LISTENER).show();
// Quit early when we know we can't go any further.
return;
}
String SENT = "SMS_SENT";
PendingIntent sentPI = PendingIntent.getBroadcast(this, 0, new Intent(SENT), 0);
// ---when the SMS has been sent---
registerReceiver(new SmsSentBroadcastReceiver(getBaseContext()), new IntentFilter(SENT));
int delaySeconds = 3;
ScheduledExecutorService scheduler = new SmsScheduler().sendSmsMessages(phoneNumber, message, sentPI, delaySeconds,
count);
// You may cancel the scheduled messages with the scheduler.
// scheduler.shutdownNow();
new AlertDialog.Builder(SMS.this).setTitle("Attention!")
.setMessage("Your messages will start sending shortly, please do not press the send sms button again")
.setNeutralButton("Ok", EMPTY_ON_CLICK_LISTENER).show();
}
private static class SmsSentBroadcastReceiver extends BroadcastReceiver {
Context context;
public SmsSentBroadcastReceiver(Context context) {
this.context = context;
}
#Override
public void onReceive(Context arg0, Intent arg1) {
switch (getResultCode()) {
case Activity.RESULT_OK:
Toast.makeText(context, "SMS sent", Toast.LENGTH_SHORT).show();
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
Toast.makeText(context, "Generic failure", Toast.LENGTH_SHORT).show();
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
Toast.makeText(context, "No service", Toast.LENGTH_SHORT).show();
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
Toast.makeText(context, "Null PDU", Toast.LENGTH_SHORT).show();
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
Toast.makeText(context, "Radio off", Toast.LENGTH_SHORT).show();
break;
}
}
}
private static class EmptyOnClickListener implements OnClickListener {
public void onClick(DialogInterface dialog, int which) {
// Does nothing
}
}
private static class SmsScheduler {
public ScheduledExecutorService sendSmsMessages(final String phoneNumber, final String message,
final PendingIntent sentIntent, int count, int delaySeconds) {
final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
final SmsManager sms = SmsManager.getDefault();
// Create the task that will send a SMS message
final Runnable sender = new Runnable() {
public void run() {
sms.sendTextMessage(phoneNumber, null, message, sentIntent, null);
}
};
// Schedule the messages to be sent at intervals of delaySeconds.
for (int i = 0; i < count; i++) {
scheduler.schedule(sender, delaySeconds * i, TimeUnit.SECONDS);
}
return scheduler;
}
}
}
Related
I have a problem with sending SMS.
it keeps sending SMS. How to only send one SMS?
the text toast will keep on displaying, you have to close the app to stop sending sms.
I try the return, but it is not work.
private void sendSMS(String address, String time){
SmsManager smsManager = SmsManager.getDefault();
String name = sharedPreferences.getString("pre_key_name", null);
String phoneNum = sharedPreferences.getString("pre_key_phone", null);
String smsContent = time + name + "in" + address + "falls!";
smsManager.sendTextMessage(phoneNum, null, smsContent ,null, null);
Toast.makeText(context, "message send success", Toast.LENGTH_SHORT).show();
}
public Handler handler = new Handler(Looper.getMainLooper()){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.arg1 > 0){
countingView.setText(" " + msg.arg1 + context.getApplicationContext().getResources().getString(R.string.fall_contacts));
}else{
if(dialog != null){
dialog.dismiss();
if(isVibrate){
stopVibrate();
}
stopAlarm();
sendSMS(locationAddress, locationTime);
return;
}
timer.cancel();
}
}
};
To check whether an SMS has been sent or not, just use a boolean value.
private boolean hasSentSMS = false; //declaring outside of the handler
if (!hasSentSMS) {
sendSMS(locationAddress, locationTime);
hasSentSMS = true;
}
I am trying to convert String into integer. Integer.parseInt() worked before, but now it is failing. It is failing here int INTERVAL= (60000 * Integer.parseInt(preferenceTime)); I am trying dynamically specify time when I schedule a timer.
thank you
public class Service extends Service {
public SharedPreferences settings;
private Handler HandleIt = new Handler();
private Timer timer = new Timer();
boolean timeout = false;
//private PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
//////////////////////////////////////////////
class TimeDisplayTimerTask extends TimerTask {
#Override
public void run() {
HandleIt.post(new Runnable(){
public void run(){
//SharedPreferences
settings = getSharedPreferences("timer_preference", MODE_PRIVATE);
String preferenceTime = settings.getString("timer_preference", "");
// int INTERVAL= (60000 * Integer.parseInt(preferenceTime));
Toast.makeText(getApplicationContext(), TextonScreen(), Toast.LENGTH_SHORT).show();
//get screen light up
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
boolean isScreenOn = pm.isScreenOn();
if(isScreenOn==false) {
pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "My Tag");
}
// make a new intent and start it with flag and send an sms
Intent launch = new Intent(getBaseContext(), SMS.class);
launch.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(launch);
}
});
}
}
private String TextonScreen()
{
timeout = true;
return "it is running";
}
boolean isTimeout()
{
return timeout;
}
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
Toast.makeText(this, "Service is created", Toast.LENGTH_SHORT).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
SharedPreferences settings = getSharedPreferences( getPackageName() + "timer_preference", MODE_PRIVATE);
String preferenceTime = settings.getString("timer_preference", "");
int INTERVAL= (60000 * Integer.parseInt(preferenceTime));
// TODO Auto-generated method stub
// Display the Toast Message
Toast.makeText(this, "Start Service", Toast.LENGTH_SHORT).show();
// Execute an action after period time
//comes from the TimeDisplayTimerTask class
timer.scheduleAtFixedRate(new TimeDisplayTimerTask(), 0, INTERVAL);
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
// Display the Toast Message
Toast.makeText(this, "Stop Service", Toast.LENGTH_SHORT).show();
if (timer != null) {
timer.cancel();
}
super.onDestroy();
}
}
Ofcourse it would fail because you want to parser "" as an int !
Try this :
String preferenceTime = settings.getString("timer_preference", "0");
int INTERVAL= (60000 * Integer.parseInt(preferenceTime));
0 means if i never set anything in it, return 0. Its the default value.
why aren't you storing it as an int ?! i mean this :
int preferenceTime = settings.getInteger("timer_preference", 0);
int INTERVAL= (60000 * preferenceTime);
If it fails to parse a number, it will throw a NumberFormatException. I usually like to wrap Integer#parseInt calls in an Optional so I can supply a default value:
public static Optional<Integer> parseInt(String s) {
try {
return Optional.of(Integer.parseInt(s));
} catch (NumberFormatException ex) {
return Optional.empty();
}
}
Then, calling can supply a default (or let you know it failed via #isPresent returning false):
int value = parseInt(preferenceTime).orElse(/* some default integer */);
I am trying to do mobile number verification without using third party. For this my logic is this:-
User enter their mobile number with country code
When they click on verify button a intent will send a sms to user-defined mobile number with random unique id
after that app broadcast will wait for 2 minute and when it receive sms then user can login or sign-up
Is this logic right or it need some modification?
To send sms i am using this code, but dont know how i can recieve and validate number.
String phoneNumber = "9999999999";
String smsBody = "Message from the API";
// Get the default instance of SmsManager
SmsManager smsManager = SmsManager.getDefault();
// Send a text based SMS
smsManager.sendTextMessage(phoneNumber, null, smsBody, null, null);
UPDATE:-
Not receiving message on samsung device.
this requires android.permission.INTERACT_ACROSS_USERS_FULL
First I generate a random number within range 10000 to 99999.
Random rNo = new Random();
final int code = rNo.nextInt((99999 - 10000) + 1) + 10000;
Next I display a popup mentioning the user that a message will be sent from the mobile to verify the number. This is done using AlertDialog.
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Verify Phone Number");
builder.setMessage("An sms will be sent to the number " + phNo + " for verification. Charges will apply as per your plan");
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(final DialogInterface dialog, int which) {
//code to send sms here with the code value
final ProgressDialog progressdialog = ProgressDialog.show(getActivity(), "Waiting for SMS", "Please hold on");
final CountDownTimer timer = new CountDownTimer(120000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
Log.v("ranjapp", "Ticking " + millisUntilFinished / 1000);
progressdialog.setMessage("Waiting for the message " + millisUntilFinished / 1000);
}
#Override
public void onFinish() {
getActivity().unregisterReceiver(receiver);
progressdialog.dismiss();
}
}.start();
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
if (readSMS(intent, code)) {
Log.v("ranjapp", "SMS read");
timer.cancel();
try {
getActivity().unregisterReceiver(receiver);
} catch (Exception e) {
}
}
}
}
};
getActivity().registerReceiver(receiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));
}
}
);
builder.setNegativeButton("CANCEL", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}
);
builder.show();
Explanation of above code:
After user acknowledges to send sms an sms is sent with the code to the entered number and this number should be the one that user is using himself. So we will wait for an sms with the code, to receive the sms and read its content we will create a BroadCastReceiver to listen to incoming sms.
Now also we need to start a timer so that we wait for only 2 minutes for the sms, so we start a CountDownTimer for 2 minutes at the end of 2 minutes the countdowntimer will unregister the receiver. The receiver is unregistered even if the code is received in the incoming sms so that we can free the resources.
Code to readSMS, if the code is found true is returned else false is returned,
boolean readSMS(Intent intent, int code) {
try {
Bundle bundle = intent.getExtras();
if (bundle != null) {
Object[] pdusObj = (Object[]) bundle.get("pdus");
for (int i = 0; i < pdusObj.length; i++) {
SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
String phoneNumber = currentMessage.getDisplayOriginatingAddress();
String senderNum = phoneNumber;
String message = currentMessage.getDisplayMessageBody();
if (message.contains(String.valueOf(code)))
return true;
}
}
} catch (Exception e) {
Log.v("ranjapp", "Exception here " + e.toString());
return false;
}
return false;
}
To send SMS use below method:
public static void sendSMS(Context context, String incomingNumber, String sms) {
DateTimeFormatter dtfOut = DateTimeFormat.forPattern("YYYY-MM-dd HH:MM:SS");
SmsManager smsManager = SmsManager.getDefault(); //send sms
try {
ArrayList<String> parts = smsManager.divideMessage(sms);
smsManager.sendMultipartTextMessage(incomingNumber, null, parts, null, null);
RecContDBHelper recContDBHelper = new RecContDBHelper(context);
recContDBHelper.insertRecord(new ContactData("", incomingNumber, dtfOut.print(MutableDateTime.now())));
Log.v("ranjith", "Sms to be sent is " + sms);
} catch (Exception e) {
Log.v("ranjith", e + "");
}
}
In AndroidManifest.xml you need to have these permissions:
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
You can use a Content Provider to access the sms inbox!
ContentResolver contentResolver =getContentResolver();
Uri uri = Uri.parse("content://sms/inbox");
//query here
I have a single Activity (called as NMessageActivity) that has a simple method of sending SMS. Just using this code:
public void sendSMS(MBean dBean) {
if (sentSMS != null && deliveredSMS != null) {
SmsManager sms = SmsManager.getDefault();
sms.sendTextMessage(dBean.getToInNumbers(), null,
dBean.getMessage(), sentSMS, deliveredSMS);
} else {
ToastObject.postMessage("Error code 233!");
}
}
Before that, i did initialize the Pending Intents that I use. i put them on the onCreate method i have:
smsSentReceiver = new SentReceiver(this);
smsDeliveredReceiver = new DeliveredReceiver(this);
and then I register them at onResume():
super.onResume();
registerReceiver(smsSentReceiver, new IntentFilter(TAG_SENT));
registerReceiver(smsDeliveredReceiver, new IntentFilter(TAG_DELIVERED));
and at onPause() I unregister them:
super.onPause();
unregisterReceiver(smsSentReceiver);
unregisterReceiver(smsDeliveredReceiver);
And before sending the SMS, I do preparing the AlarmManager to execute the intent
after 2 seconds. Using this code:
MBean dbbean = new MBean(txt_destNum.getText().toString(), txt_date
.getText().toString(), txt_time.getText().toString(),
MBean.STATUS_WAIT, txt_msg.getText().toString());
db.addMBean(dbbean);
Intent intentAlarm = new Intent(TAG_SENT);
Intent intentAlarm2 = new Intent(TAG_DELIVERED);
// putting the values in
intentAlarm.putExtra(MBean.MBEAN_NAME, dbbean.toString());
sentSMS = PendingIntent.getBroadcast(getApplicationContext(), 0,
intentAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
deliveredSMS = PendingIntent.getBroadcast(getApplicationContext(),
0, intentAlarm2, PendingIntent.FLAG_UPDATE_CURRENT);
int sec = 2;
// create the object
AlarmManager alarmManager = (AlarmManager) getSystemService(getApplicationContext().ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + (sec * 1000), sentSMS);
The problem is not on the above method and nor the AlarmManager.
Instead, the problem is on the BroadcastReceiver that i Have.
The Broadcast receivers are never called. Why is that happened?
Here are the Broadcast Receiver class in details:
SentReceiver.java
public class SentReceiver extends BroadcastReceiver {
private MBean dBean;
private Context dCont;
private ToastSender dToast;
private Activity dNMessageActivity;
public SentReceiver(Activity obAct){
dNMessageActivity = obAct;
}
private void sendMessage() {
((NMessageActivity)dNMessageActivity).sendSMS(dBean);
}
private void vibratePhone() {
Vibrator vibrator = (Vibrator) dCont
.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(1000);
}
#Override
public void onReceive(Context arg0, Intent arg1) {
dToast = new ToastSender(arg0);
dCont = arg0;
// TODO Auto-generated method stub
switch (getResultCode()) {
case Activity.RESULT_OK:
// SMS has been sent
dBean = new MBean(arg1.getStringExtra(MBean.MBEAN_NAME));
vibratePhone();
sendMessage();
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
// Generic Failure
dToast.postMessage("Error code 48!");
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
// No Service
dToast.postMessage("Error code 53!");
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
// Null PDU
dToast.postMessage("Error code 58!");
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
// Radio Off
dToast.postMessage("Error code 63!");
break;
default:
break;
}
}
}
and another receiver is
DeliveredReceiver.java
public class DeliveredReceiver extends BroadcastReceiver {
private ToastSender dToast;
private Context dCont;
private Activity dNMessageActivity;
public DeliveredReceiver(Activity obAct){
dNMessageActivity = obAct;
}
private void vibratePhone() {
Vibrator vibrator = (Vibrator) dCont
.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(2000);
}
#Override
public void onReceive(Context arg0, Intent arg1) {
dCont = arg0;
dToast = new ToastSender(arg0);
switch (getResultCode()) {
case Activity.RESULT_OK:
dToast.postMessage("SMS Sent");
vibratePhone();
break;
case Activity.RESULT_CANCELED:
dToast.postMessage("SMS Pending");
break;
}
}
}
I wonder is that the Broadcast Receiver problem or the Pending Intent problem that I passed over the Alarmmanager? Why the program doesnt call the Broadcast Receiver at all?
I am trying to send an urgent SMS from my application. I have to make sure that the SMS is being sent successfully.
The SMS is being sent after the boot of the Android system and after a check is being made.
So I have a service class that handles the BOOT_COMPLETED intent-filter. This class makes a check and if something is true then it tries to send an SMS message via another class that "extends Service"
After it makes sure that the sms is successfully sent, both services (the one that handles the boot call and the one that sends the sms) must exit.
Question 1: How to make my sms sending function be called with a timeout without getting the application being unresponsive message? Currently I am using this (I don't know if it is the correct way to do it, though it works):
Timer mTimer = new Timer();
//wait a small timeout prior to sending the message.
mTimer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
this.cancel(); //I don't want to run the timer more than once
sms_sender sms = new sms_sender();
sms.sendSMS(phoneNumber, messageText);
}
}, 30000, 30000); //run sendSMS() after 30 seconds
Question 2: How to implement the sendSMS function so as to retry every 30 seconds after realizing that the last attempt was a fail?
Question 3: How to stop both services after I realize that the SMS was successfully sent?
This is my code which does not work:
public class sms_sender extends Service {
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
final String SENT = "SMS_SENT";
public void sendSMS(final String phoneNumber, final String message, final boolean check_result)
{
PendingIntent sentPI = PendingIntent.getBroadcast(this, 0, new Intent(SENT), 0);
registerReceiver(new BroadcastReceiver(){
#Override
public void onReceive(Context arg0, Intent arg1) {
if(!check_result)
return;
switch (getResultCode())
{
case Activity.RESULT_OK:
//exit
stopSelf();
return;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
case SmsManager.RESULT_ERROR_NO_SERVICE:
case SmsManager.RESULT_ERROR_NULL_PDU:
case SmsManager.RESULT_ERROR_RADIO_OFF:
//try again in 1 minute
Timer mTimer = new Timer();
mTimer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
this.cancel(); //no need to run again, if it fails, this exact code will run again
sendSMS(phoneNumber, message, true);
}
}, 60000, 60000);
return;
}
}
}, new IntentFilter(SENT));
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNumber, null, message, sentPI, null);
}
}
Currently the program crashes on the PendingIntent call. I tried to implement the BroadCastReceiver on the onCreate method using private member variables so as to call the sendSMS() function again through the onReceive method, but the onReceive never seemed to run.
-- EDIT --
So, this is my final working code. I guess that my case is special because it doesn't work on a UI thread. I have a Broadcast Receiver that runs on Boot. I am trying to send an SMS message until it is successfully sent.
This Boot Broadcast Receiver starts a service. This is some code from it:
public class service extends Service{
static public service serv;
//member variable. Initializing to null so as to know whether to unregister the service or not
private BroadcastReceiver messageSent = null;
...
...
#Override
public void onStart(Intent intent, int startid)
{
serv=this; //will use this static variable in order to shutdown the service when the message is successfully sent
...
...
if(somethingIsTrue()){
//register receiver
messageSent = new sent_message();
registerReceiver(messageSent, new IntentFilter(sms_sender.INTENT_MESSAGE_SENT));
startMessageServiceIntent(messageText, phoneNumber); //function code can be found on accepted answer
}
}
}
The sent_message class is the following:
public class sent_message extends BroadcastReceiver {
private Context pubCon;
private void startMessageServiceIntent(String message, String receiver) {
Intent i = new Intent(pubCon, sms_sender.class);
i.putExtra(sms_sender.EXTRA_MESSAGE, message);
i.putExtra(sms_sender.EXTRA_RECEIVERS, new String[] { receiver });
pubCon.startService(i);
}
#Override
public void onReceive(Context context, Intent intent) {
pubCon=context;
switch (getResultCode()) {
case Activity.RESULT_OK:
//all went OK, stop the service where this is called from
service.serv.stopSelf();
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
case SmsManager.RESULT_ERROR_NO_SERVICE:
case SmsManager.RESULT_ERROR_NULL_PDU:
case SmsManager.RESULT_ERROR_RADIO_OFF:
//try sending the message again after 30s
new Handler().postDelayed(new Runnable(){
#Override
public void run(){
startMessageServiceIntent(service.messageText, service.phoneNumber);
}
}, 30000);
break;
}
}
}
And a simplified (accepts only one receiver) version of the sms_sender class is the following:
public class sms_sender extends IntentService {
public static final String INTENT_MESSAGE_SENT = "message.sent";
public static final String INTENT_MESSAGE_DELIVERED = "message.delivered";
public static final String EXTRA_MESSAGE = "extra.message";
public static final String EXTRA_RECEIVERS = "extra.receivers";
public sms_sender() {
super("sms_sender");
}
private static class IDGenerator {
private static final AtomicInteger counter = new AtomicInteger();
public static int nextValue() {
return counter.getAndIncrement();
}
}
public void sendSMS(String message, String receiver) {
SmsManager sm = SmsManager.getDefault();
PendingIntent sentPI = null;
Intent sentIntent = new Intent(INTENT_MESSAGE_SENT);
int sentID = IDGenerator.nextValue();
sentPI = PendingIntent.getBroadcast(sms_sender.this, sentID, sentIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
try {
sm.sendTextMessage(receiver, null, message, sentPI, null);
} catch (IllegalArgumentException e) {
System.out.println("Illegal argument");
}
}
protected void onHandleIntent(Intent intent) {
String message = intent.getStringExtra(EXTRA_MESSAGE);
String[] receivers = intent.getStringArrayExtra(EXTRA_RECEIVERS);
sendSMS(message, receivers[0]);
}
}
Here is what I have done:
public class SMSSender extends IntentService {
public static final String INTENT_MESSAGE_SENT = "message.sent";
public static final String INTENT_MESSAGE_DELIVERED = "message.delivered";
public static final String EXTRA_MESSAGE = "extra.message";
public static final String EXTRA_RECEIVERS = "extra.receivers";
public SMSSender() {
super("SMSSender");
}
private final String TAG = "SendSMS";
private static class IDGenerator {
private static final AtomicInteger counter = new AtomicInteger();
public static int nextValue() {
return counter.getAndIncrement();
}
}
private void sendSMS(String message, String[] receivers) {
SmsManager sm = SmsManager.getDefault();
ArrayList<String> parts = sm.divideMessage(message);
PendingIntent sentPI = null;
PendingIntent deliveredPI = null;
Intent sentIntent = new Intent(INTENT_MESSAGE_SENT);
int sentID = IDGenerator.nextValue();
sentPI = PendingIntent.getBroadcast(SMSSender.this, sentID, sentIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
Intent deliveryIntent = new Intent(INTENT_MESSAGE_DELIVERED);
int deliveredID = IDGenerator.nextValue();
deliveredPI = PendingIntent.getBroadcast(SMSSender.this, deliveredID,
deliveryIntent, PendingIntent.FLAG_CANCEL_CURRENT);
Log.i(TAG, "sending SMS: parts: " + parts.size() + " message: "
+ message);
if (parts.size() > 1) {
ArrayList<PendingIntent> sentIntents = null;
ArrayList<PendingIntent> deliveredIntents = null;
sentIntents = new ArrayList<PendingIntent>();
deliveredIntents = new ArrayList<PendingIntent>();
for (int i = 0; i < parts.size(); i++) {
sentIntents.add(sentPI);
deliveredIntents.add(deliveredPI);
}
for (String receiver : receivers) {
try {
sm.sendMultipartTextMessage(receiver, null, parts,
sentIntents, deliveredIntents);
} catch (IllegalArgumentException e) {
Log.e(TAG, "illegal receiver: " + receiver);
}
}
} else {
for (String receiver : receivers) {
try {
sm.sendTextMessage(receiver, null, parts.get(0), sentPI,
deliveredPI);
} catch (IllegalArgumentException e) {
Log.e(TAG, "illegal receiver: " + receiver);
}
}
}
}
#Override
protected void onHandleIntent(Intent intent) {
String message = intent.getStringExtra(EXTRA_MESSAGE);
String[] receivers = intent.getStringArrayExtra(EXTRA_RECEIVERS);
sendSMS(message, receivers);
}
And to use it:
private void startMessageServiceIntent(String message, String receiver) {
Intent i = new Intent(context, SMSSender.class);
i.putExtra(SMSSender.EXTRA_MESSAGE, message);
i.putExtra(SMSSender.EXTRA_RECEIVERS, new String[] { receiver });
startService(i)
}
Notice it supports multiple receivers, which this method does not demonstrate/use.
Remember in your manifest:
<uses-permission android:name="android.permission.SEND_SMS" />
<service android:name="your.package.SMSSender" android:enabled="true" />
Optionally you can listen for when messages are sent and/or delivered:
#Override
protected void onCreate() {
...
// ---when the SMS has been sent---
private BroadcastReceiver messageSent; // <- stored as a field
messageSent = new SentMessage();
registerReceiver(messageSent, new IntentFilter(SMSSender.INTENT_MESSAGE_SENT));
// ---when the SMS has been delivered---
private BroadcastReceiver messageDelivered; // <- stored as a field
messageDelivered = new MessageDelivered();
registerReceiver(messageDelivered, new IntentFilter(
SMSSender.INTENT_MESSAGE_DELIVERED));
}
#Override
protected void onDestroy() { // remember to unregister
unregisterReceiver(messageSent);
unregisterReceiver(messageDelivered );
}
I know this does not demonstrate answers to all your questions but I hope that it is sufficient.
Edit: Added my implementations of messageSent and messageDelivered
These are specific to my implementation, so includes some code that you cannot use, it is simply for demonstration.
Message sent:
public class SentMessage extends BroadcastReceiver {
private final String TAG = "SentMessage";
#Override
public void onReceive(Context context, Intent intent) {
long _id = intent.getLongExtra(EXTRA_ID, -1);
long protocol_id = intent.getLongExtra(EXTRA_PROTOCOL, -1);
Log.d(TAG, "SentMessage");
switch (getResultCode()) {
case Activity.RESULT_OK:
Log.d(TAG, "RESULT_OK");
if (MessageData.sentMessage(_id, protocol_id)) {
try {
Database.messageSent(_id);
} catch (DatabaseRowNotFoundException e) {
Log.e(TAG, e.toString(), e);
}
}
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
Log.d(TAG, "RESULT_ERROR_GENERIC_FAILURE");
MessageData.postponeMessage(_id);
ApplicationData.hasSignal(false);
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
Log.d(TAG, "RESULT_ERROR_NO_SERVICE");
MessageData.postponeMessage(_id);
ApplicationData.hasSignal(false);
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
Log.d(TAG, "RESULT_ERROR_NULL_PDU");
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
Log.d(TAG, "RESULT_ERROR_RADIO_OFF");
MessageData.postponeMessage(_id);
ApplicationData.hasSignal(false);
break;
}
}
Message delivered:
public class DeliveredMessage extends BroadcastReceiver {
private final String TAG = "DeliveredMessage ";
#Override
public void onReceive(Context context, Intent intent) {
long _id = intent.getLongExtra(EXTRA_ID, -1);
long protocol_id = intent.getLongExtra(EXTRA_PROTOCOL, -1);
switch (getResultCode()) {
case Activity.RESULT_OK:
if (_id != -1 && MessageData.deliveredMessage(_id, protocol_id)) {
try {
Database.messageDelivered(_id);
Cursor messageCursor = Database.getCursorByID(MessageOutboxContentProvider.CONTENT_URI, MessageOutboxContentProvider._ID, _id);
messageCursor.close();
} catch (DatabaseRowNotFoundException e) {
Log.e(TAG, e.toString(), e);
}
}
break;
case Activity.RESULT_CANCELED:
break;
}
}
}
I was in the need for reliable sending too, so kept references to all pending messages in a database, which I would frequently scan for postponed messages. A message would get postponed if there is no radio, or the sending simply fails for whatever reason.
I also used GCM together with SMS to get the message delivered as fast as possible, sending messages using both channels at the same time.
Edit2: Oh well, might as well address the questions, we are almost there anyway:
Question 1: Since using IntentService the sending is done in the background.
You only want the sending to happen once after a delay so you should do this instead:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// send sms
}
}, delay);
Question 2: Easy, when your sent message broadcast detects an error do the above method. You could add an extra information, besides receiver and message, counting the number of retries up until now so you have a chance of stopping the send/retry loop.
Question 3: The sending stops by itself, as it is an Intent Service. As for the other service the most simple approach, I think, would be to send a common broadcast, which is picked up by your main activity. This way you can get a hold of the service the right place and stop it.