I want to do the following:
Send an SMS
Check if it was sent
Store it in a SQLite instance if it wasn't.
Resend any SMS that got stored before.
So you got a main SMS sending action, which will require feedback on its status (to tell the user if it could be sent or not), and a background SMS sending action which will just try to resend previously unsent SMS silently.
The solution I came up with involves an IntentService which has two actions:
Send an SMS message.
Try to send previously stored SMS messages.
So far so good, this exact same thing worked wonders for sending a TCP message to a server. Now the issue is that I can't seem to be able to send SMS from the IntentService.
The idea was to have the IntentService create a PendingIntent, stuff the main Activity provided PendingIntent inside (thats basically the callback to tell the activity the SMS was sent), then send the SMS with it.
Then with a static receiver fetch the PendingIntent and start a new action on the IntentService to delete the SMS from the SQLite instance if it was sent properly.
This is the main message sending method in the IntentService:
private void sendMessage(Configuration cfg, SMSMessage msg, PendingIntent resultIntent) {
final Intent smsIntent;
// Select which kind of intent we're creating.
if (resultIntent == null) {
// This one is the silent background SMS.
smsIntent = new Intent(this.getApplicationContext(), RedirectPendingMessages.class);
} else {
// This one is the one from the application.
smsIntent = new Intent(this.getApplicationContext(), RedirectMessage.class);
smsIntent.putExtra(EXTRA_PENDING_RESULT, resultIntent);
}
// Now store the message.
smsIntent.putExtra(EXTRA_SMS, msg);
// Construct broadcast intent.
PendingIntent pi = PendingIntent.getBroadcast(this.getApplicationContext(), 0, smsIntent, 0);
// Now send message.
SmsManager smsMng = SmsManager.getDefault();
smsMng.sendTextMessage(cfg.phoneNumberFor(msg.level), null, msg.content, pi, null);
}
It gets to the 'SmsManager.sendTextMessage' method fine but nothing happens, even if I hardcode the phone number and dont pass any PendingIntent the SMS still doesn't gets sent. It might be because the IntentService ceases to exist after the method call?
The receivers both just grab the broadcasted Intent, fetch the data inside, and start appropiate actions in the IntentService to delete the SMSs if they have been sent, and to broadcast the application's PendingIntent so the UI can give some feedback to the user ("SMS sent", "Error", etc).
My TCP implementation of the same thing pretty much just has a Socket write instead of that 'sendTextMessage', it blocks the IntentService until its done and works fine.
Any ideas on why the SMS isn't being sent or how to better implement this?
It seems that your message exceeds the character limit for the alphabet you're using, which is causing the SmsManager#sendTextMessage() method to fail silently.
For the basic 7-bit default alphabet, the character limit is 160; for 8-bit, it's 140; and for 16-bit, which sounds like your situation, it's 70, explaining why your 120-character message split into two parts.
As you discovered, you can use the SmsManager#divideMessage() method to split a message into useable parts, and the SmsManager#sendMultipartTextMessage() to send the parts correctly.
SmsManager Reference
Related
I want to implement pub/sub in my app, for that server send notification in specific event, I will do some modification on data which is display in my activity
onMessageReceived() doesn't need PendingIntent to be call. It will always call if you have correct setup. This link provide the data type you should sent to FCM server
With FCM, you can send two types of messages to clients:
Notification messages, sometimes thought of as "display messages."
Data messages, which are handled by the client app.
If you would like to always trigger use just data message so it will always trigger the onMessageReceived().If you try to use data-message and notification-message together the onMessageReceived() will not get trigger when your app is in background.
Just do anything you would like to do such as save to database, sharedPeference etc inside your onMessageReceived()
So how you sent to the activity?
Use Broadcast Receiver here is how you sent a broadcast receiver in your case you will like to put it inside your onMessageReceived() so anytime you received a new notification this code will help you sent the data to the specific activity.
Intent intent = new Intent("Use anything you like");
intent.putExtra("data","The data you receive");
sendBroadcast(intent);
In your activity register it in your onStart()
registerReceiver(broadCastReceiver,new IntentFilter("must match the intent filter parameter"));
Here is how you handle your data
class broadCastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("Your data",intent.getData());
}
}
Note: your intent filter parameter must match the intent parameter you set in your onMessageReceived()
If your app never received data from the FCM this answer will be useless since your question still unclear this is the best I can do for you.
I would like to intercept incomming MMS to enable mobile data. For that, I need to intercept them before any other app.
I have setup my intent filter to receive WAP_PUSH_RECEIVED_ACTION broadcasts with the highest possible priority.
But, in the Android documentation (https://developer.android.com/reference/android/provider/Telephony.Sms.Intents.html), there are the two following broadcasts:
WAP_PUSH_DELIVER_ACTION (Only sent to default sms app)
WAP_PUSH_RECEIVED_ACTION (Sent to all apps)
Please, can you tell me which of these broadcasts is sent first (WAP_PUSH_DELIVER_ACTION or WAP_PUSH_RECEIVED_ACTION) and where did you find this information ?
From where are them send in Android source code ?
Does listening for WAP_PUSH_RECEIVED_ACTION with the highest possible priority let me be the first to receive WAP PUSH broadcasts ?
Thanks
This topic seems to be not so popular!
I tried to answer the question myself and I found something interesting.
Analysis
SMS and MMS reception are mainly managed in the file InboundSmsHandler.java.
This file starts with a comment block that explains the SMS/MMS receiving state machine.
Here is an extract of this comment with explanations:
The state machine starts in InboundSmsHandler.IdleState state.
When the SMSDispatcher receives a new SMS from the radio, it calls dispatchNormalMessage(com.android.internal.telephony.SmsMessageBase), which transitions to InboundSmsHandler.DeliveringState state.
From the InboundSmsHandler.DeliveringState state, processMessagePart(InboundSmsTracker tracker) is called. Within this method, if the destination port number of the SMS is SmsHeader.PORT_WAP_PUSH (in other words if the SMS is an MMS), the WapPushOverSms.dispatchWapPdu(byte[] pdu, BroadcastReceiver receiver, InboundSmsHandler handler) method is called.
Inside the dispatchWapPdu method, they call InboundSmsHandler.dispatchIntent(Intent intent, String permission, int appOp, BroadcastReceiver resultReceiver, UserHandle user). They check if there is a default MMS app and if it's the case, configure the intent to only be delivered to this app.
Code:
// Direct the intent to only the default MMS app. If we can't find a default MMS app
// then sent it to all broadcast receivers.
ComponentName componentName = SmsApplication.getDefaultMmsApplication(mContext, true);
if (componentName != null) {
// Deliver MMS message only to this receiver
intent.setComponent(componentName);
if (DBG) Rlog.v(TAG, "Delivering MMS to: " + componentName.getPackageName() +
" " + componentName.getClassName());
}
handler.dispatchIntent(intent, permission, appOp, receiver, UserHandle.OWNER);
Inside the dispatchIntent we have what we are looking for, the call to Context.sendOrderedBroadcastAsUser(...). So, it is this method that sends the WAP_PUSH_DELIVER_ACTION broadcast as an ordered broadcast.
This broadcast is also handled (default app and SmsBroadcastReceiver) by the SmsBroadcastReceiver.onReceive(Context context, Intent intent) handler located in InboundSmsHandler.java. Inside this handler, the WAP_PUSH_DELIVER_ACTION case is processed. The intent is changed to WAP_PUSH_RECEIVED_ACTION and broadcasted again through the InboundSmsHandler.dispatchIntent(Intent intent, String permission, int appOp, BroadcastReceiver resultReceiver, UserHandle user) method. This time, not only the default app is concerned, but all interested apps.
Code:
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intents.SMS_FILTER_ACTION)) {
// ...
} else if (action.equals(Intents.SMS_DELIVER_ACTION)) {
// ...
} else if (action.equals(Intents.WAP_PUSH_DELIVER_ACTION)) {
// Now dispatch the notification only intent
intent.setAction(Intents.WAP_PUSH_RECEIVED_ACTION);
intent.setComponent(null);
// Only the primary user will receive notification of incoming mms.
// That app will do the actual downloading of the mms.
dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
AppOpsManager.OP_RECEIVE_SMS, this, UserHandle.OWNER);
} else {
// ...
}
}
Conclusion (Quick answer to the original question)
When an MMS is received, WAP_PUSH_DELIVER_ACTION is broadcasted first to the default app followed by the WAP_PUSH_RECEIVED_ACTION.
Both broadcasts are ordered broadcasts that means that priorities can be used.
Well, it's a bad news for me because it also means that I cannot be the first to be notified for an incoming MMS and turn on the modile data before the MMS app is notified.
Ahh Google, with Lollipop, you make the things harder for us : Android Issue 78084 - setMobileDataEnabled removed
So, I have to look for another way in order to do that.
as of right now here is what I have. However the client would now like to know if the sms was actually sent, and upon the send button being clicked they would like to return to the app.
how would you listen to see if the sms was sent? would it be in the activities onActivityResult()?
how would you tell the sms client to exit upon sending?
do I just need to make my own screen for sending the sms, and send the sms directly without launching a sms app?
public static void launchSMSIntent(String to, String body, Context context)
{
Intent smsIntent = new Intent(Intent.ACTION_VIEW);
smsIntent.setType("vnd.android-dir/mms-sms");
if(to != null)
smsIntent.putExtra("address", to);
if(body != null)
smsIntent.putExtra("sms_body", body);
((Activity)context).startActivityForResult(smsIntent, Constants.SMS_INTENT);
}
If you need to listen for the SMS being sent, you'll need to do it yourself via the android.telephony.SMSManager apis. There's no way to guarantee that whatever random SMS app the user uses will return a flag to tell you whether it was sent or not.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to programmatically send SMS on the iPhone?
I am not sure whether it is possible, but what I would like to achieve is, after asked for users' permission, my app would like to send a formatted sms through my app on their phone. I would like it to happen in the background without them seeing the sms input screen and I would like the sent sms to not present in the message list, only the formatted received message.
Is it even possible? initially I would like to implement it on iPhone, but later on I would like to extend it to Android and wp7. Thanks in advance.
On iOS, no, you can't.
You could use a third party service, though.
I dont know for the other platforms but on iOS if your app wants to send an sms it will ask for the users permission and the user will be taken to the sms interface. Apple is very strict about these but in android this might be possible.
Sending SMS on iOS documentation
edit: I dont know what you are trying to do, but why not use the web? If you are trying to send a message that the user doesnt know the content or destination it doesnt need to be by SMS.
You can Send a SMS In Background like this way :
Here i have use on button click you can send sms in background no screen appear in front of user.
(Note : Return If applicable otherwise return empty value.)
Get Phone Number of owner :
TelephonyManager tMgr =(TelephonyManager)mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
String number = tMgr.getLine1Number();
write Pending Intent this code in on click event.
String message = "HI THIS IS TEST SMS IN ANDROID.";
/** Creating a pending intent which will be broadcasted when an sms message is successfully sent */
PendingIntent piSent = PendingIntent.getBroadcast(getBaseContext(), 0, new Intent("sent_msg") , 0);
/** Creating a pending intent which will be broadcasted when an sms message is successfully delivered */
PendingIntent piDelivered = PendingIntent.getBroadcast(getBaseContext(), 0, new Intent("delivered_msg"), 0);
/** Getting an instance of SmsManager to sent sms message from the application*/
SmsManager smsManager = SmsManager.getDefault();
/** Sending the Sms message to the intended party */
smsManager.sendTextMessage(number, null, message, piSent, piDelivered);
Create class name with SmsNotifications which extends BroadcastReceiver
/**
* This class handles the SMS sent and sms delivery broadcast intents
*/
public class SmsNotifications extends BroadcastReceiver{
/**
* This method will be invoked when the sms sent or sms delivery broadcast intent is received
*/
#Override
public void onReceive(Context context, Intent intent) {
/**
* Getting the intent action name to identify the broadcast intent ( whether sms sent or sms delivery )
*/
String actionName = intent.getAction();
if(actionName.equals("sent_msg")){
switch(getResultCode()){
case Activity.RESULT_OK:
Toast.makeText(context, "Message is sent successfully" , Toast.LENGTH_SHORT).show();
break;
default:
Toast.makeText(context, "Error in sending Message", Toast.LENGTH_SHORT).show();
break;
}
}
if(actionName.equals("delivered_msg")){
switch(getResultCode()){
case Activity.RESULT_OK:
Toast.makeText(context, "Message is delivered" , Toast.LENGTH_SHORT).show();
break;
default:
Toast.makeText(context, "Error in the delivery of message", Toast.LENGTH_SHORT).show();
break;
}
}
}
}
Manage your Manifest File :
Permission :
<uses-permission android:name="android.permission.SEND_SMS" />
And
<receiver android:name=".SmsNotifications" >
<intent-filter >
<action android:name="sent_msg" />
<action android:name="delivered_msg" />
</intent-filter>
</receiver>
The only alternative I can think if is to send an NSHTTPURLRequest to a web service that provides an SMS gateway. That you could certainly do in the background, though likely you (the developer, not the user) would incur the cost of sending the messages, and the sender would not appear to be the user.
You can't do this in Windows Phone 7. You'd have to start a SmsComposeTask which is similar to the MFMessageComposeViewController. This means all the text sending logic is handled in there and you can only adjust some parameters.
I'm developing an android application that sends an sms message from a window (Activity) then moves to another window(Activity). I'm trying to display a message to the user how is seeing another activity that the message is delivered.
I imagine the solution would be to it fire a thread that send the message and and wait until it's delivered and show a toast or Dialog. but I don't know if it's right or how to do that.
pls help.
sendDataMessage has following parameters for that:
sentIntent - if not NULL this PendingIntent is broadcast when the message is sucessfully sent, or failed.
deliveryIntent - if not NULL this PendingIntent is broadcast when the message is delivered to the recipient.
[edit] - example how to create pending intent
final PendingIntent sentIntent = PendingIntent.getActivity(this, 0, new Intent(this, SmsSendCheck.class), 0);
SmsSendCheck - this is a special activity to show your Toast
Toast is a good way. Otherwise, when your PendingIntent was broadcast after SMS was sent, display something in the current Activity.