Alarmmanager sending SMS broadcast receiver not called - java

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?

Related

Difficulty sending data on the Broadcast receiver through Intent

I have a Touch Mobile Computer with a barcode scanner.
I'm trying to write an application that scans a barcode and imports data from the DB into the device. In order to use the scanner I use a broadcast receiver.
On the scan activity screen, there are a few barcodes to scan. I set the intent to transfer information from which edittext the scan was performed (using putextra). The broadcast receiver receives the scan the action but the input of the putextra is not exist (The handle variable gets a null value [Attached picture]).
I would be happy to help with what I'm doing wrong.
Activity Class:
public class MoveItemActivity extends AppCompatActivity {
private EditText item;
private EditText to;
private boolean mIsRegisterReceiver = false;
private BroadcastReceiver mBarcodeReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_move_item);
item = (EditText) this.findViewById(R.id.itemInEditText);
item.setOnFocusChangeListener(mEditText);
to = (EditText) this.findViewById(R.id.toInEditText);
to.setOnFocusChangeListener(mEditText);
this.registerReceiver();
}
EditText.OnFocusChangeListener mEditText = new EditText.OnFocusChangeListener(){
#Override
public void onFocusChange(View v, boolean hasFocus) {
Intent intent = new Intent();
switch (v.getId()){
case R.id.itemInEditText:
if (!hasFocus){
new CheckItem(MoveItemActivity.this).execute(item.getText().toString());
break;
}
else {
intent.setAction(BarcodeControllerConstants.ACTION_BARCODE_OPEN);
intent.putExtra(BarcodeControllerConstants.EXTRA_HANDLE, "item");
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(intent);
break;
}
case R.id.toInEditText:
if (!hasFocus){
new CheckLocation().execute(to.getText().toString());
break;
}
else
{
intent.setAction(BarcodeControllerConstants.ACTION_BARCODE_OPEN);
intent.putExtra(BarcodeControllerConstants.EXTRA_HANDLE, "to");
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(intent);
break;
}
}
}
};
private void registerReceiver() {
if (mIsRegisterReceiver)
return;
IntentFilter filter = new IntentFilter();
filter.addAction(BarcodeControllerConstants.ACTION_BARCODE_CALLBACK_DECODING_DATA);
filter.addAction(BarcodeControllerConstants.ACTION_BARCODE_CALLBACK_REQUEST_SUCCESS);
filter.addAction(BarcodeControllerConstants.ACTION_BARCODE_CALLBACK_REQUEST_FAILED);
filter.addAction(BarcodeControllerConstants.ACTION_BARCODE_CALLBACK_GET_STATUS);
mBarcodeReceiver = new BarcodeController();
registerReceiver(mBarcodeReceiver, filter);
mIsRegisterReceiver = true;
}
BroadcastReceiver Class:
public class BarcodeController extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
int mBarcodeHandle = -1;
if (action.equals(BarcodeControllerConstants.ACTION_BARCODE_CALLBACK_DECODING_DATA)) {
String handle = intent.getExtras().getString(BarcodeControllerConstants.EXTRA_HANDLE);
byte[] data = intent.getByteArrayExtra(BarcodeControllerConstants.EXTRA_BARCODE_DECODING_DATA);
String result = null;
if (data != null) {
result = new String(data);
Intent i = new Intent(context, MoveItemActivity.class);
i.putExtra(handle,result );
context.startActivity(i);
}
} else if (action.equals(BarcodeControllerConstants.ACTION_BARCODE_CALLBACK_REQUEST_SUCCESS)) {
mBarcodeHandle = intent.getIntExtra(BarcodeControllerConstants.EXTRA_HANDLE, 0);
System.out.println("ACTION_BARCODE_CALLBACK_REQUEST_SUCCESS:" + mBarcodeHandle);
} else if (action.equals(BarcodeControllerConstants.ACTION_BARCODE_CALLBACK_REQUEST_FAILED)) {
int result = intent.getIntExtra(BarcodeControllerConstants.EXTRA_INT_DATA2, 0);
System.out.println("ACTION_BARCODE_CALLBACK_REQUEST_FAILED:" + result);
} else if (action.equals(BarcodeControllerConstants.ACTION_BARCODE_CALLBACK_GET_STATUS)) {
int status = intent.getIntExtra(BarcodeControllerConstants.EXTRA_INT_DATA2, 0);
System.out.println("ACTION_BARCODE_CALLBACK_GET_STATUS:" + status);
}
}
}
The item that you're sending in broadcast has action:
intent.setAction(BarcodeControllerConstants.ACTION_BARCODE_OPEN);
But in onReceive() you're checking different action:
action.equals(BarcodeControllerConstants.ACTION_BARCODE_CALLBACK_DECODING_DATA)
Are you sure this is the same intent that the one you're putting extras to?
intent.putExtra(BarcodeControllerConstants.EXTRA_HANDLE, "item");
You don't even register BarcodeControllerConstants.ACTION_BARCODE_OPEN action in your BarcodeController broadcast, so I think it's not received.

Try sending sms again when for some reason is not sent the first time

I am a newcomer to androi. My problem is the following I have a code that sends text messages all right but I would like to be able to do that when the sending of the message fails automatically try to send it again about 3 times after that if it is not sent since it does nothing.
Greetings I hope you can help me I had a good time with this problem.
Here I leave the code.
public class Principal extends Activity {
EditText txtPhone;
EditText txtMsg;
Button btnSend;
TextView msj;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_principal);
txtPhone = ((EditText)findViewById(R.id.txtPhone ));
txtMsg = ((EditText)findViewById(R.id.txtMsg ));
btnSend = ((Button)findViewById(R.id.btnSend ));
msj = ((TextView)findViewById(R.id.txtmsj));
btnSend.setOnClickListener(
new View.OnClickListener() {
public void onClick(View view) {
PendingIntent sentIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent("SMS_SENT"), 0);
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
switch (getResultCode()){
case Activity.RESULT_OK:
msj.setText("SMS SENT");
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
msj.setText("I can not send SMS");
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
msj.setText("service not available");
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
msj.setText("PDU (Protocol Data Unit) es NULL");
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
msj.setText("turn on the radio");
break;
}
}
}, new IntentFilter("SMS_SENT"));
SmsManager sms = SmsManager.getDefault();
if( txtPhone.getText().toString().length()> 0 &&
txtMsg.getText().toString().length()>0 )
{
sms.sendTextMessage( txtPhone.getText().toString() , null, txtMsg.getText().toString() , sentIntent, null);
}
else
{
Toast.makeText(getApplicationContext(), "Can not send, data is incorrect", Toast.LENGTH_SHORT).show();
}
}
});
}

Android BroadcastReceiver in Service giving null pointer exception when sending broadcast to it

I have a broadcast receiver in my service, and when I send a broadcast to it from my activity to stop the service for example, I get the following error:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.content.BroadcastReceiver.onReceive(android.content.Context, android.content.Intent)' on a null object reference
Here is my Service class:
public class PhraseService extends Service{
public static List<Phrase> phrases;
private Context context;
private static final String PHRASE_SERVICE_BROADCAST_ID = "com.cryogenos.safephrase.PHRASE_SERVICE_BROADCAST_ID";
private BroadcastReceiver command_broadcast_receiver;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.context = getApplicationContext();
LocalBroadcastManager.getInstance(this).registerReceiver(command_broadcast_receiver, new IntentFilter(PHRASE_SERVICE_BROADCAST_ID));
Log.i("COS", "STARTED SELF");
/* Handle any command sent by the app here */
command_broadcast_receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("COS", "CALLED?");
try {
String command = intent.getStringExtra("command");
/* Based on the command given, perform an action */
switch (command) {
case "RESTART RECOGNIZER":
break;
case "STOP":
Toast.makeText(context, "Phrase Listener Stopped", Toast.LENGTH_SHORT).show();
stopSelf();
break;
}
}
catch (Exception e){}
}
};
Intent notif_intent = new Intent(this, Home.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, notif_intent, 0);
Notification notif = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Phrase Listener")
.setContentText("Listening for Phrases")
.setContentIntent(pi)
.build();
startForeground(34994, notif);
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(command_broadcast_receiver);
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {return null;}
}
Here is the activity from where I'm sending the broadcast from:
public class SettingsModel {
private Context context;
private static final String PHRASE_SERVICE_BROADCAST_ID = "com.cryogenos.safephrase.PHRASE_SERVICE_BROADCAST_ID";
private LocalBroadcastManager local_broadcast_manager;
public SettingsModel(Context context) {
this.context = context;
local_broadcast_manager = LocalBroadcastManager.getInstance(context);
}
public void handleSwitch(SettingsSwitch settings_switch, boolean status){
switch (settings_switch){
case POWER:
if(status) {
Intent i = new Intent(context, PhraseService.class);
context.startService(i);
}
else{
Intent i = new Intent(PHRASE_SERVICE_BROADCAST_ID);
i.putExtra("command", "STOP");
local_broadcast_manager.sendBroadcast(i);
}
break;
case VIBRATE:
break;
case RING:
break;
case TIMER:
break;
}
}
}
My service is suppose to be continuously running, and only when I tell it, it should stop. This is why I have set it up as a foreground service. The problem here is that I keep getting an error when I try to broadcast to it.
You are registering your brodcastreceiver before initializing it. So try to move the registration after initialization as below:
/* Handle any command sent by the app here */
command_broadcast_receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("COS", "CALLED?");
try {
String command = intent.getStringExtra("command");
/* Based on the command given, perform an action */
switch (command) {
case "RESTART RECOGNIZER":
break;
case "STOP":
Toast.makeText(context, "Phrase Listener Stopped", Toast.LENGTH_SHORT).show();
stopSelf();
break;
}
}
catch (Exception e){}
}
};
LocalBroadcastManager.getInstance(this).registerReceiver(command_broadcast_receiver, new IntentFilter(PHRASE_SERVICE_BROADCAST_ID));
Also, your SettingsModel is not an activity since it does not extend Activity.

Error notification when i close my app

i am doing an application that will notify the user after 2 weeks. but i set the timer in 2 mins so i could test if my application will notify the user. my problem is when i turn on the notification and close my app. i will get an error in 2 minutes no notification but when my app is running the notification is working fine. In the logcat my error is in Bundle showData = i.getExtras(); i think the problem is when i closed my app, the Bundle passData is getting null?
PhoneInformation.java
case R.id.bTurnOn:
Bundle passData = new Bundle();
Intent intent = new Intent(PhoneInformation.this,NotificationService.class);
passData.putInt("keyid", rowId);
intent.putExtras(passData);
startService(intent);
break;
NotificationService.java
public class NotificationService extends Service{
int rowId;
private Timer timer = new Timer();
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
Toast.makeText(this, "OnCreate()", Toast.LENGTH_SHORT).show();
}
#Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Toast.makeText(this, "OnDestroy()", Toast.LENGTH_SHORT).show();
}
#Override
#Deprecated
public void onStart(final Intent i, int startId) {
// TODO Auto-generated method stub
super.onStart(i, startId);
final Handler handler = new Handler();
Timer timer;
TimerTask timertask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
#SuppressWarnings("deprecation")
public void run() {
Bundle showData = i.getExtras();
rowId = showData.getInt("keyid");
Bundle passData = new Bundle();
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(NotificationService.this,SMSPhoneMessage.class);
passData.putInt("keyid", rowId);
notificationIntent.putExtras(passData);
PendingIntent pendingIntent = PendingIntent.getActivity(NotificationService.this, 0, notificationIntent, Intent.FLAG_ACTIVITY_NEW_TASK);
Notification notification = new Notification(R.drawable.ic_launcher, "New Message", System.currentTimeMillis());
notification.setLatestEventInfo(NotificationService.this, "New Message", "Please come to me immediately", pendingIntent);
nm.notify(123, notification);
}
});
}
};
timer = new Timer();
timer.schedule(timertask, 10000);
}
}

Send SMS until it is successful

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.

Categories