Cordova-plugin-sms too big SMS error - android

I´m using Cordova-plugin-sms and it is working fine.
I can send sms except I cant exceed the standart 140-70 characters for each sms.
I need to send 2-in-1 sms like default SMS app.
For the exceeded size sms, it gives a successCallback, but sms is not sent.
https://github.com/floatinghotpot/cordova-plugin-sms
var successCallback = function () {
msgSentUser(message);
};
var failureCallback = function (e) {
};
SMS.sendSMS(number, fullMsg, successCallback, failureCallback);
Thank you,

Looking at the source code for that plugin, you can see that it uses the SmsManager#sendTextMessage() method. This method only handles single-part messages, and it will fail silently if you pass it a message that exceeds the character limit for a single part in the alphabet you're using. You still get the successCallback, however, since no Exception is thrown, and the plugin itself uses no other method of confirmation. The solution is to alter the code to utilize the sendMultipartTextMessage() method.
In the original source, lines 206 through 212, inclusive, handle the message send, and are what we need to replace. That is, these lines:
PendingIntent sentIntent = PendingIntent.getBroadcast((Context)this.cordova.getActivity(),
(int)0, (Intent)new Intent("SENDING_SMS"), (int)0);
SmsManager sms = SmsManager.getDefault();
for (int i = 0; i < n; ++i) {
String address;
if ((address = addressList.optString(i)).length() <= 0) continue;
sms.sendTextMessage(address, null, text, sentIntent, (PendingIntent)null);
}
The following replacement block divides the message into the appropriate parts, and creates the necessary ArrayList of PendingIntents to pass to the sendMultipartTextMessage() method. Please note that if you are handling the SENDING_SMS broadcast, it will now fire once for each message part, instead of once per send, as it did for single-part messages.
SmsManager sms = SmsManager.getDefault();
ArrayList<String> parts = sms.divideMessage(text);
final int count = parts.size();
ArrayList<PendingIntent> sentPIs = new ArrayList<PendingIntent>(count);
int req = 0;
PendingIntent pi = null;
for (int i = 0; i < n; i++) {
String address;
if ((address = addressList.optString(i)).length() <= 0) continue;
sentPIs.clear();
for (int j = 0; j < count; j++) {
req = i * count + j;
pi = PendingIntent.getBroadcast((Context) this.cordova.getActivity(),
req, new Intent("SENDING_SMS"), 0);
sentPIs.add(pi);
}
sms.sendMultipartTextMessage(address, null, parts, sentPIs, null);
}
The incoming message handling in that plugin is not correct, and will result in multipart messages appearing as multiple, separate messages. Two code sections will need to be altered to fix this. The first is lines 350 to 354, inclusive:
for (int i = 0; i < pdus.length; ++i) {
SmsMessage sms = SmsMessage.createFromPdu((byte[])((byte[])pdus[i]));
JSONObject json = SMSPlugin.this.getJsonFromSmsMessage(sms);
SMSPlugin.this.onSMSArrive(json);
}
Which we change to:
JSONObject json = SMSPlugin.this.getJsonFromSmsMessage(pdus);
SMSPlugin.this.onSMSArrive(json);
Next, we need to alter the getJsonFromSmsMessage() method; lines 447 through 466, inclusive:
private JSONObject getJsonFromSmsMessage(SmsMessage sms) {
JSONObject json = new JSONObject();
try {
json.put( ADDRESS, sms.getOriginatingAddress() );
json.put( BODY, sms.getMessageBody() );
json.put( DATE_SENT, sms.getTimestampMillis() );
json.put( DATE, System.currentTimeMillis() );
json.put( READ, MESSAGE_IS_NOT_READ );
json.put( SEEN, MESSAGE_IS_NOT_SEEN );
json.put( STATUS, sms.getStatus() );
json.put( TYPE, MESSAGE_TYPE_INBOX );
json.put( SERVICE_CENTER, sms.getServiceCenterAddress());
} catch ( Exception e ) {
e.printStackTrace();
}
return json;
}
This method will now be as follows. Please note that the method's parameter type has changed, and the JSONObject's value for the BODY key has, as well.
private JSONObject getJsonFromSmsMessage(Object[] pdus) {
SmsMessage sms = null;
StringBuilder sb = new StringBuilder();
JSONObject json = new JSONObject();
for (int i = 0; i < pdus.length; i++) {
sms = SmsMessage.createFromPdu((byte[]) pdus[i]);
sb.append(sms.getMessageBody());
}
try {
json.put(ADDRESS, sms.getOriginatingAddress());
json.put(BODY, sb.toString());
json.put(DATE_SENT, sms.getTimestampMillis());
json.put(DATE, System.currentTimeMillis());
json.put(READ, MESSAGE_IS_NOT_READ);
json.put(SEEN, MESSAGE_IS_NOT_SEEN);
json.put(STATUS, sms.getStatus());
json.put(TYPE, MESSAGE_TYPE_INBOX);
json.put(SERVICE_CENTER, sms.getServiceCenterAddress());
} catch (Exception e) {
e.printStackTrace();
}
return json;
}

For the sms concat i used this: ( i use this because 1 to 1 chat)
var totalSms = "";
function timeToAdd(newSms) {
totalSms = totalSms + newSms;
if (totalSms == newSms) { // only waits the first time
window.setTimeout(
function () {
msgReceived(totalSms);
addConversationMessage(totalSms, "sender");
totalSms = "";
}, 1000);
}
}
it basicly waits 1 second after the first "onsmsarrive" event to concat all the sms received (since every sms takes >1s to send) it should work

It seems like the problem is in:
safesmsExport.sendSMS = function(address, text, successCallback, failureCallback) {
var numbers;
if( Object.prototype.toString.call( address ) === '[object Array]' ) {
numbers = address;
} else if(typeof address === 'string') {
numbers = [ address ];
} else {
if(typeof failureCallback === 'function') {
failureCallback("require address, phone number as string, or array of string");
}
return;
}
cordova.exec(successCallback, failureCallback, 'SMS', 'sendSMS', [numbers, text]);
};
This is not calling the function sendSMS from the smsPlugin.java. Stands alone on SMS send even if smsPlugin sendSMS is commented.

I chose to change plugin. I got this one to work sending big sms: https://github.com/cordova-sms/cordova-sms-plugin , the problem is that this one doesn't have the startwatch. im trying to add it ( I'm new on plugins and JS as you can see)

Related

Strange character when reading NFC tag

I am trying to read an NFC tag using Android. I'm a beekeeper and this is to ID my hives when I approach them. I have searched here but I am still having issues reading the tag. I want to read the text, but when it reads, there is a square-like character and characters displayed like " Ten" before the desired text.
Here is the code I'm using. I know that the payload bytes have to be correct and I have tried changing them but to no avail.
private static NdefMessage getTestMessage() {
byte[] mimeBytes = "application/com.android.cts.verifier.nfc"
.getBytes(Charset.forName("US-ASCII"));
byte[] id = new byte[] {1, 3, 3, 7};
byte[] payload = "CTS Verifier NDEF Push Tag".getBytes(Charset.forName("US-ASCII"));
return new NdefMessage(new NdefRecord[] {
new NdefRecord(NdefRecord.TNF_MIME_MEDIA, mimeBytes, id, payload)
});
}
#Override
protected void onResume() {
super.onResume();
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
mNfcAdapter.setNdefPushMessageCallback(this, this);
}
// sending message
#Override
public NdefMessage createNdefMessage(NfcEvent event) {
return getTestMessage();
}
private NdefMessage[] getNdefMessages(Intent intent) {
Parcelable[] rawMessages = intent
.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMessages != null) {
NdefMessage[] messages = new NdefMessage[rawMessages.length];
for (int i = 0; i < messages.length; i++) {
messages[i] = (NdefMessage) rawMessages[i];
}
return messages;
} else {
return null;
}
}
static String displayByteArray(byte[] bytes) {
String res="";
StringBuilder builder = new StringBuilder().append("");
for (int i = 0; i < bytes.length; i++) {
res+=(char)bytes[i];
}
return res;
}
// displaying message
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
NdefMessage[] messages = getNdefMessages(intent);
edtUser.setText(displayByteArray(messages[0].toByteArray()));
Toast.makeText(this, "NFC tag entered", Toast.LENGTH_LONG).show();
}
You are getting odd additional characters when displaying the message since you try to display the whole raw NDEF record as text string (using some odd decoding method):
NdefMessage[] messages = getNdefMessages(intent);
edtUser.setText(displayByteArray(messages[0].toByteArray()));
There are several problems with this. First of all, you would typically want to decode the text using the same encoding that you used to write the text. For instance, if you used
String text = "...";
byte[] bytes = text.getBytes(Charset.forName("US-ASCII"));
to get a byte array in US-ASCII encoding for a given text string, you would also want to use that same US-ASCII encoding to translate the bytes into a text string again:
byte[] bytes = ...;
String text = new String(bytes, "US-ASCII");
Second, you are interpreting the whole NDEF message as a text string. However, the text that you stored on the tag is typically only contained inside the payload of an NDEF record. In your case, the prefix "Ten" suggests that you used an NFC Forum Text record (type name "T") with the language indication "en" (for English). You would, therefore, want to search the NDEF message for the Text record:
for (NdefRecord r : messages[0].getRecords()) {
if (r.getTnf() == NdefRecord.TNF_WELL_KNOWN) {
if (Arrays.equals(r.getType(), NdefRecord.RTD_TEXT)) {
Once you found the Text record, you can decode its text payload. The payload consists of a status byte, a language field and the actual text:
byte[] payloadBytes = ndefRecord.getPayload();
boolean isUTF8 = (payloadBytes[0] & 0x080) == 0; //status byte: bit 7 indicates encoding (0 = UTF-8, 1 = UTF-16)
int languageLength = payloadBytes[0] & 0x03F; //status byte: bits 5..0 indicate length of language code
int textLength = payloadBytes.length - 1 - languageLength;
String languageCode = new String(payloadBytes, 1, languageLength, "US-ASCII");
String payloadText = new String(payloadBytes, 1 + languageLength, textLength, isUTF8 ? "UTF-8" : "UTF-16");
edtUser.setText(payloadText);
}
}
}

Android send SMS fail because of 160 character limit but sent as multipart

Some of my clients (~100) are reporting that the SMS is not being sent by the app. So I have added debug logs in the app and the following error comes:
(1) RESULT_ERROR_GENERIC_FAILURE (Generic failure cause)
According to the docs, the source of the error can't be identified, maybe the device didn't had network, airplaine mode was on, etc.
This is how the SMS are being sent:
private void sendSMS(#NonNull String phoneNumber, #NonNull String message, #Nullable PendingIntent pendingIntentSent, #Nullable PendingIntent pendingIntentDelivered) {
SmsManager smsManager = null;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
int smsId = SmsManager.getDefaultSmsSubscriptionId();
smsManager = SmsManager.getSmsManagerForSubscriptionId(smsId);
}
else {
smsManager = SmsManager.getDefault();
}
}
catch (Exception e) {
e.printStackTrace();
}
if (smsManager == null)
smsManager = SmsManager.getDefault();
ArrayList<String> messageDivided = smsManager.divideMessage(message);
if (messageDivided.size() == 1) {
smsManager.sendTextMessage(phoneNumber, null, message, pendingIntentSent, pendingIntentDelivered)
}
else {
ArrayList<PendingIntent> piSentList = null;
ArrayList<PendingIntent> piDeliveredList = null;
if (pendingIntentSent != null) {
piSentList = new ArrayList<>();
}
if (pendingIntentDelivered != null) {
piDeliveredList = new ArrayList<>();
}
for (int i = 0; i < messageDivided.size(); i++) {
if (pendingIntentSent != null)
piSentList.add(pendingIntentSent);
if (pendingIntentDelivered != null)
piDeliveredList.add(pendingIntentDelivered);
}
smsManager.sendMultipartTextMessage(phoneNumber, null, messageDivided, piSentList, piDeliveredList);
}
}
What have I figured out that if the message length is less than 160 characters, the error disappears and the SMS can be sent successfully. However, this is a partial fix, because I don't want that my users to limit the SMS.
Also the SMS length limit is different from carrier to carrier.
US Cellular:
Each text message is limited to 150 characters, including spaces and
punctuation. If a message is longer than 150 characters, the remainder
will be lost, resulting in incomplete messages being received. Note:
Some handsets may have a smaller character limit.
Other network operators have different SMS length, 140 or 160.

Android email client app tracking unread gmail emails

So I've been using javamail API as part of my android app. After a login to a gmail account, the user can write new emails, check the inbox and sent mails. The emails are displayed in a listview with the help of an adapter class. (more accurately the sender, the subject and the sending date is displayed, and if the user clicks on the listview item, then the mail content will be displayed too on a new activity). All this is working well.
I would like to display unread emails differently (unread in the gmail client too), like set the textSyle bold if the mail is unread, but i'm having trouble adding this feature. I've been trying to check the flags of each fetched email message, but for some reason i dont see these flags in the variables.
My code snippet for fetching the mails (display is not here, that's in the adapter class):
protected ArrayList<Email_Message> doInBackground(Void... args) {
try {
Properties properties = new Properties();
properties.put("mail.store.protocol", "imaps");
Session emailSession = Session.getDefaultInstance(properties);
Store store = emailSession.getStore("imaps");
store.connect("imap.gmail.com", username, password);
// create the folder object and open it
Folder emailFolder = store.getFolder("INBOX");
emailFolder.open(Folder.READ_WRITE);
Flags flags2 = emailFolder.getPermanentFlags(); //has 2 weird user flags in it ($phishing, $notphising) and systemflags = -2147483061 ???
Flags seen = new Flags(Flags.Flag.RECENT);
FlagTerm unseenFlagTerm = new FlagTerm(seen, false);
Message messages2[] = emailFolder.search(unseenFlagTerm); //this will net the same result as getMessages(), only here for testing
int test = emailFolder.getUnreadMessageCount(); //as far as i can tell this is working (i have 5000+ emails and 37 them are unread somewhere) but when i get a new email and the number goes up by 1 (38), and if i run the code again, after i already fetched the mails once, it's gonna be 37 again, and the mail marked as read in my gmail webclient too
// retrieve the messages from the folder in an array and print it
Message[] messages = emailFolder.getMessages();
int j = messages.length - 1;
for (int i = j - startIndex; i > j - startIndex - offset && i > (-1); i--) { //startIndex and offset are for displaying only 10 messages at the start and loading another 10 if the user scrolls to bottom
if (isCancelled()){
break;
}
Email_Message mailMessage = new Email_Message(); //my class for storing email messages
mailMessage.messageType = 1;
//some tricks to get the address in the right format
Address[] email_address = messages[i].getFrom();
String tempAddress = email_address[0].toString();
tempAddress = MimeUtility.decodeText(tempAddress);
//still tempering with address, not important
if(tempAddress.contains("=?")){
String[] AddressParts = tempAddress.split("\\?=");
mailMessage.messageAddress = AddressParts[1].substring(2);
}
else {
mailMessage.messageAddress = tempAddress;
}
Flags flags = messages[i].getFlags(); //user_flags = null, system_flags = 32
Flags.Flag[] systemflags = flags.getSystemFlags(); //has 1 item in it: bit = 32
String str[]= flags.getUserFlags(); //empty, these are all true for all my mails, not just one
mailMessage.messageDate = messages[i].getSentDate().toString();
mailMessage.messageSubject = messages[i].getSubject();
Object msgContent = messages[i].getContent();
String content = ""; //getting the content of the mail with these multipart stuffs
if (msgContent instanceof Multipart) {
Multipart multipart = (Multipart) msgContent;
Log.e("BodyPart", "MultiPartCount: " + multipart.getCount());
for (int k = 0; k < multipart.getCount(); k++) {
BodyPart bodyPart = multipart.getBodyPart(k);
String disposition = bodyPart.getDisposition();
if (disposition != null && (disposition.equalsIgnoreCase("ATTACHMENT"))) {
DataHandler handler = bodyPart.getDataHandler();
content = handler.getName();
} else {
content = bodyPart.getContent().toString();
}
}
} else
content = messages[i].getContent().toString();
mailMessage.messageContent = content;
EmailInbox.add(mailMessage);
}
// close the store and folder objects
emailFolder.close(false);
store.close();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (MessagingException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return EmailInbox;
}
I put some comments in the code to explain what i've found in the flags. What can I do to make this work? I already predict problems, like what happens after I read an unread mail in my app only, set its flag to seen, and when I start the activity again and fetch the mails, it's gonna be unread again, since I don't store them locally, but that's a problem after I managed to find a solution for this first.
Thanks for any help!
I'm not clear on how you're looking for the flags. Using messages[i].isSet(Flags.Flag.SEEN) will tell you if the SEEN flag has been set. Note that the SEEN flag will normally be set as soon as you access the content of the message, so you should not have to set it yourself.
Hint: use the InternetAddress.toUnicodeString method, or get the name and address separately using the getPersonal and getAddress methods. This will avoid any need to decode them yourself.

Android Multi-Part sms content becomes unreadable on reception

I am working on an android project, that deals with device authentication via sms.
The problem I am facing is, when the authentication key is being sent, the receiving device gets a garbled text and not the original sent content.
I am using two instances of the emulator to test the code.
Here is the relevant code :
String MyPublic = "__key("+N.toString()+")yek__";
ArrayList<String> parts = smsmgr.divideMessage(MyPublic);
smsmgr.sendMultipartTextMessage(senderNumber, null, parts, null, null);
How ever when I am sending a single sms within 160 characters, then this problem isn't disappears.
Here is the code I am using to listen for incoming messages.
public void onReceive(final Context context, Intent intent) {
msgReceived = false;
Object[] pdus=(Object[])intent.getExtras().get("pdus");
Bundle bundle = intent.getExtras();
if (bundle != null) {
pdus = (Object[])bundle.get("pdus");
final SmsMessage[] messages = new SmsMessage[pdus.length];
for (int i = 0; i < pdus.length; i++) {
messages[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
}
SmsMessage sms = messages[0];
String body;
if (messages.length == 1 || sms.isReplace()) {
body = sms.getDisplayMessageBody();
} else {
StringBuilder bodyText = new StringBuilder();
for (int i = 0; i < messages.length; i++) {
bodyText.append(messages[i].getMessageBody());
}
body = bodyText.toString();
}
}
The message that is received when the 'Multi-part' thing is used is of this type :
The "HelloWorld" was sent as a single-part message(Non-Multipart) and the 3rd and second from below are parts of that multipart authentication key.
Need Help resolving this.
Regards
Priyabrata.

Base64 incompatible with 7-bit GSM encoding?

Since I am not able to compress a 256-byte long md5-like string within 160 characters, I decided to use Multi-part message in Android to divide the SMS into several parts and the receiver will listen the SMS then combine those messages.
I use the combination algorithm from here: Android - receiving long SMS (multipart)
And after a few testing I found that the SMS I sent was totally messed, though I encoded them with Base64 before sending.
Thinking the SMS is a 7-bit Encoding method I thought it might be the encoding problem.
But I have successfully sent Base 64 encoded message, but it was short and within 1 SMS.
My question is:
If it is a encoding problem, then why I can send a totally readable Base64 encoded messages within 160 characters, but cannot get a readable result when sending messages exceeding 160 characters?
I've attached my code here:
The string I intend to send:
static final String bigNum = "C196BA05AC29E1F9C3C72D56DFFC6154A033F1477AC88EC37F09BE6C5BB95F51C296DD20D1A28A067CCC4D4316A4BD1DCA55ED1066D438C35AEBAABF57E7DAE428782A95ECA1C143DB701FD48533A3C18F0FE23557EA7AE619ECACC7E0B51652A8776D02A425567DED36EABD90CA33A1E8D988F0BBB92D02D1D20290113BB562CE1FC856EEB7CDD92D33EEA6F410859B179E7E789A8F75F645FAE2E136D252BFFAFF89528945C1ABE705A38DBC2D364AADE99BE0D0AAD82E5320121496DC65B3930E38047294FF877831A16D5228418DE8AB275D7D75651CEFED65F78AFC3EA7FE4D79B35F62A0402A1117599ADAC7B269A59F353CF450E6982D3B1702D9CA83";
The Code
private void sendMsg (String phoneNum, String msg) {
SmsManager sms = SmsManager.getDefault();
ArrayList<String> parts = sms.divideMessage(msg);
sms.sendMultipartTextMessage(phoneNum, null, parts, null, null);
}
public void onStart() {
super.onStart();
final String bigNum64 = Base64.encodeToString(bigNum.getBytes(), 0);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendMsg("5554", bigNum64);
textView.setText(bigNum64);
}
});
}
public void onResume() {
super.onResume();
Bundle receiver = getIntent().getExtras();
if (receiver != null) {
String msg = receiver.getString("SMS");
textView.setText("Received" + msg);
}
}
}
The SMS Receiver:
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Bundle bundle = intent.getExtras();
Object[] pdus = (Object[]) bundle.get("pdus");
SmsMessage[] messages = new SmsMessage[pdus.length];
String body = "";
for (int i = 0; i < pdus.length; i++) {
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
}
SmsMessage sms = messages[0];
try {
if (messages.length == 1 || sms.isReplace()) {
body = sms.getDisplayMessageBody();
}
else {
StringBuilder bodyText = new StringBuilder();
for (int i = 0; i < messages.length; i++) {
bodyText.append(messages[i].getMessageBody());
}
body = bodyText.toString();
}
}
catch (Exception e) {
}
Intent start = new Intent(context, SendLongSMSActivity.class);
start.putExtra("SMS", body);
start.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(start);
}
Thanks for your help!
Related Post: Any way to compress a 256 bytes "MD5-like" string into 160 bytes or less?
Even if I give a rather simple long string, like
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
and no matter with or without Base64 encoding, the receiver cannot get the correct results.
UPDATE #2
When using real phone for testing, the receiver can receive correct message without base 64, it was due to the emulator garbled the text.
Question now closed.
Thanks for everyone who helps! And thanx #Dan
UPDATE:
The string "0123456789ABCDEF..." without base64, divided into 2 parts:
part [0] "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF012345678"
part [1] "9ABCDEF0123456789ABCDEF"
Here's the divided base64 string of the "0123456789ABCDEF...:
part[0] "MDEyMzQ1Njc4OUFCQ0RFRjAxMjM0NTY3ODlBQkNERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4\nOUFCQ0RFRjAxMjM0NTY3ODlBQkNERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCQ0RFRjAx"
part[1] "\nMjM0NTY3ODlBQkNERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCQ0RFRjAxMjM0NTY3ODlB\nQkNERUY=\n"
It appears that the sms divide is adding \n characters after every 77 characters and at the end, just strip those from your string and it will decode properly.

Categories

Resources