I need to have a verified phone number for my users, but after researching, there doesn't appear to be a reliable way to get the phone# from the device. One suggestion I've seen a couple of times is to send a text message, then catch it in a Broadcast receiver and parse the phone number from the inco ming intent. I have seen this question:
I could not get data from Intent after broadcast received, and this one, but they do not address my problem.
Currently, I have the receiver working, but I can't get any data about the SMS message, which presumably is accessible through the intent.
Here's the code that fires when the user presses the SEND button:
btnSend.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
String SENT = "sent";
String DELIVERED = "delivered";
Intent sentIntent = new Intent(SENT);
/*Create Pending Intents*/
PendingIntent sentPI = PendingIntent.getBroadcast(
getApplicationContext(), 0, sentIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
/* Register for SMS send action */
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
try {
---> **SmsMessage[] smsMessage = Telephony.Sms.Intents.getMessagesFromIntent(intent);**
String messageBody = smsMessage[0].getMessageBody();
} catch (Exception e) {
e.printStackTrace();
}
int resultcode = getResultCode();
Log.d(" RESULT=", Integer.toString(resultcode));
}
}, new IntentFilter(SENT));
SmsManager smsManager = SmsManager.getDefault();
if ((ContextCompat.checkSelfPermission(getApplicationContext(),
Manifest.permission.SEND_SMS) !=
PackageManager.PERMISSION_GRANTED) ||
(ContextCompat.checkSelfPermission(getApplicationContext(),
Manifest.permission.RECEIVE_SMS) !=
PackageManager.PERMISSION_GRANTED)){
ActivityCompat.requestPermissions(SMSActivity.this,
new String[] {
Manifest.permission.SEND_SMS, Manifest.permission.RECEIVE_SMS
}, 1);
}
smsManager.sendTextMessage(phoneNo.getText().toString(), null,
msg.getText().toString(), sentPI, null);
} catch (Exception ex) {
Toast.makeText(getApplicationContext(),
ex.getMessage(), Toast.LENGTH_LONG)
.show();
ex.printStackTrace();
}
}
});
When the call to getMessagesFromIntent() is made (line in bold), an exception is thrown as listed here at "onReceive(SMSActivity.java:60):" The Intent is not null, so it appears that "null array" is internal to getMessagesFromIntent.
W/System.err: java.lang.NullPointerException: Attempt to get length of null array
W/System.err: at android.provider.Telephony$Sms$Intents.getMessagesFromIntent(Telephony.java:1345)
W/System.err: at com.jdot.jsontest.SMSActivity$1$1.onReceive(SMSActivity.java:60)
W/System.err: at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:993)
W/System.err: at android.os.Handler.handleCallback(Handler.java:739)
W/System.err: at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err: at android.os.Looper.loop(Looper.java:158)
W/System.err: at android.app.ActivityThread.main(ActivityThread.java:7224)
W/System.err: at java.lang.reflect.Method.invoke(Native Method)
W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
I assume I may have a permissions related issue. I've added this to my manifest:
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
I've also seen this manifest addition a few times, but I don't know how to name MyBroadcastListener, since it it isn't a class like the examples I've seen.
<receiver android:name="MyBroadcastListener">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
Finally, I tried the following block of code (as an alternative) in the the body of onReceive(). Though intent.getExtras() returns a Bundle, the call bundle.get("pdus") returns null so everything else fails.
Bundle bundle = intent.getExtras();
SmsMessage[] msgs = null;
String str = "";
if (bundle != null) {
//---retrieve the SMS message received---
Object[] pdus = (Object[]) bundle.get("pdus");
msgs = new SmsMessage[pdus.length];
for (int i=0; i<msgs.length; i++){
msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
str += "SMS from " + msgs[i].getOriginatingAddress();
str += " :";
str += msgs[i].getMessageBody().toString();
str += "\n";
}
//---display the new SMS message---
Toast.makeText(context, str, Toast.LENGTH_SHORT).show();
}
My questions are:
- Do I need the Receiver/Intent-filter block in my manifest, and if so, how do I name the listener in the first line?
- What is the likely reason I can not receive a copy of the incoming message in onReceive(), and what should I do to correct it?
- Do I need to send a message and "catch it" in onReceive() in order to reliably get the devices phone number, or can I request permission to read SMS from the user, and just read the first message to get the incoming phone number?
Please note that I have read through all similar questions and have not found an answer.
Best Answer
Here's your problem:
You're registering that Receiver for the action you're using for the sent confirmation, not for the
SMS_RECEIVED
action, which is what is broadcast when a message arrives.The sent
PendingIntent
is used to confirm that the message has successfully left the device. The resultingIntent
from that will not have any message PDUs on it, which is why you're crashing.The action you need to register an incoming SMS Receiver for is
"android.provider.Telephony.SMS_RECEIVED"
, or the constantTelephony.Sms.Intents.SMS_RECEIVED_ACTION
, if you're compiling with API 19 or above.To address the specific questions:
Registering a Receiver in the manifest will allow your app to receive messages even when it's not already running, or in the foreground. If you don't need to do that, you don't necessarily need to register in the manifest. However, if the user navigates away from your
Activity
before receiving the message, the pausedActivity
won't get the broadcast.The "safest" way to handle it would be to register in the manifest, but there you're registering a class, not a dynamic instance. You would need to create a
MyBroadcastListener
class, and somehow notify theActivity
from that upon receipt; e.g., usingstartActivity()
, orLocalBroadcastManager
, or some other event bus implementation, etc.As mentioned above, the
PendingIntent
passed in thesendTextMessage()
call is for sent confirmation. Its resultingIntent
won't have the incoming message on it. You just need to listen for the correct broadcast.You need to actually send a message. Incoming messages won't have the recipient number attached.