In my android code, I am trying to detect incoming SMS messages. The code below was working since 2 years, but now it stopped working. What updates am I missing?
public class SmsListener extends BroadcastReceiver {
private String msgBody;
private SharedPreferences preferences;
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if(intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")){
Toast.makeText(context,"message received",Toast.LENGTH_SHORT).show();
Bundle bundle = intent.getExtras(); //---get the SMS message passed in---
SmsMessage[] msgs = null;
String msg_from;
if (bundle != null){
//---retrieve the SMS message received---
try{
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]);
msg_from = msgs[i].getOriginatingAddress();
msgBody = msgs[i].getMessageBody();
MainActivity.handleMessage(msgBody);
}
Toast.makeText(context,"message is:"+msgBody,Toast.LENGTH_SHORT).show();
}catch(Exception e){
Log.d("Exception caught",e.getMessage());
}
}
}
}
In my Main Activity I am requesting the user permission, and using the SMS receiver as the following:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Updated///////////////////////////////////////////////////////////////////////////////////
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
requestSmsPermission();
else {
smsListener = new SmsListener();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(smsListener, intentFilter);
}
///////Updated///////////////////////////////////////////////////////////////////////////////////
}
private void requestSmsPermission() {
String permission = Manifest.permission.RECEIVE_SMS;
int grant = ContextCompat.checkSelfPermission(this, permission);
if ( grant != PackageManager.PERMISSION_GRANTED) {
String[] permission_list = new String[1];
permission_list[0] = permission;
ActivityCompat.requestPermissions(this, permission_list, 1);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
smsListener = new SmsListener();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(smsListener, intentFilter);
}
}
My Manifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.zaidalmahmoud.expenseless">
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".GraphActivity"></activity>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".SmsListener"
android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
<activity android:name=".DetailedExpenseActivity" />
</application>
</manifest>
When the SMS is received, my android application does not detect it, but it does not crash or show anything. Why? Thanks.
NOTE: I am not interested in catching SMS with verification code. I want my application to catch any incoming SMS.
The Android app needs SMS receive/read permission to retrieve SMS content.
Google has introduced SMS Retriever API, this API allows to retrieve the OTP without needing of the SMS permission in your application.
Add These Dependency for SMS Retriever API
implementation 'com.google.android.gms:play-services-base:16.0.1'
implementation 'com.google.android.gms:play-services-identity:16.0.0'
implementation 'com.google.android.gms:play-services-auth:16.0.1'
implementation 'com.google.android.gms:play-services-auth-api-phone:16.0.0'
Then craete an interface like below:
public interface OnNewMessageListener {
void onNewMessageReceived(String activationCode);
}
Then, create a broadCastReceiver to catch sms:
public class SmsBroadcastReceiver extends BroadcastReceiver {
OnNewMessageListener onNewMessageListener;
public SmsBroadcastReceiver() {
}
public SmsBroadcastReceiver(OnNewMessageListener onNewMessageListener) {
this.onNewMessageListener = onNewMessageListener;
}
#Override
public void onReceive(Context context, Intent intent) {
if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
Bundle extras = intent.getExtras();
if (extras != null) {
Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
if (status != null)
switch (status.getStatusCode()) {
case CommonStatusCodes.SUCCESS:
// Get SMS message contents
String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
// Extract one-time code from the message and complete verification
// by sending the code back to your server.
if (!TextUtils.isEmpty(message)) {
String activationCode = null;
Pattern p = Pattern.compile("your pattern like \\b\\d{4}\\b");
Matcher m = p.matcher(message);
if (m.find()) {
activationCode = (m.group(0)); // The matched substring
}
if (onNewMessageListener != null && !TextUtils.isEmpty(activationCode))
onNewMessageListener.onNewMessageReceived(activationCode);
}
break;
case CommonStatusCodes.TIMEOUT:
// Waiting for SMS timed out (5 minutes)
// Handle the error ...
break;
}
}
}
}
}
At your AndroidManifest declare broadcastReceiver:
<receiver
android:name=".SmsBroadcastReceiver"
android:exported="true"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED" />
</intent-filter>
</receiver>
Inside your activity add these code:
private SmsBroadcastReceiver smsListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Get an instance of SmsRetrieverClient, used to start listening for a matching
// SMS message.
SmsRetrieverClient client = SmsRetriever.getClient(Objects.requireNonNull(getContext()) /* context */);
// Starts SmsRetriever, which waits for ONE matching SMS message until timeout
// (5 minutes). The matching SMS message will be sent via a Broadcast Intent with
// action SmsRetriever#SMS_RETRIEVED_ACTION.
Task<Void> task = client.startSmsRetriever();
// Listen for success/failure of the start Task. If in a background thread, this
// can be made blocking using Tasks.await(task, [timeout]);
task.addOnSuccessListener(aVoid -> {
// Successfully started retriever, expect broadcast intent
// ...
});
task.addOnFailureListener(e -> {
// Failed to start retriever, inspect Exception for more details
// ...
});
OnNewMessageListener onNewMessageListener = activationCode -> {
if (!TextUtils.isEmpty(activationCode)) {
editText.setText(String.valueOf(activationCode));
}
};
smsListener = new SmsBroadcastReceiver(onNewMessageListener);
if (getContext() != null)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getContext().registerReceiver(smsListener, new IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION));
}
}
#Override
public void onStop() {
super.onStop();
try {
if (getContext() != null && smsListener != null) {
getContext().unregisterReceiver(smsListener);
smsListener = null;
}
} catch (Exception ignored) {
}
}
Your sms should be like this:
<#> Use 123456 as your verification code
FC+7qAH5AZu
Message must:
Be no longer than 140 bytes
Begin with the prefix <#>
Contain a one-time code that the client sends back to your server to complete the verification flow (see Generating a one-time code)
End with an 11-character hash string that identifies your app (see Computing your app’s hash string)
For more information see this link.
UPDATE
See this link.
EDIT
Change your activity to this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
requestSmsPermission();
else {
smsListener = new SmsListener();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(smsListener, intentFilter);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
smsListener= new SmsListener();
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(smsListener, intentFilter);
}
}
Update
Change your BroadcastReceiver to this:
public class SmsListener extends BroadcastReceiver {
private String msgBody;
private SharedPreferences preferences;
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) {
Toast.makeText(context, "message received", Toast.LENGTH_SHORT).show();
Bundle bundle = intent.getExtras();
try {
if (bundle != null) {
final Object[] pdus = (Object[]) bundle.get("pdus");
for (int i = 0; i < pdus.length; i++) {
SmsMessage smsMessage;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
smsMessage = SmsMessage.createFromPdu((byte[]) pdus[i], bundle.getString("format"));
else smsMessage = SmsMessage.createFromPdu((byte[]) pdus[i]);
msg_from = smsMessage.getDisplayOriginatingAddress();
msgBody = smsMessage.getMessageBody();
MainActivity.handleMessage(msgBody);
}
Toast.makeText(context, "message is:" + msgBody, Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
Log.d("Exception caught", e.getMessage());
}
}
}
}
Declare SMS Read and Receive permission in your android manifest file :
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
Then we need a SMS Receiver as follows:
class SMSReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
if (intent != null && intent.action != null && intent.action!!.equals("android.provider.Telephony.SMS_RECEIVED", ignoreCase = true)) {
val bundle = intent.extras
if (bundle != null) {
val sms = bundle.get(SMS_BUNDLE) as Array<Any>?
val smsMsg = StringBuilder()
var smsMessage: SmsMessage
if (sms != null) {
for (sm in sms) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val format = bundle.getString("format")
smsMessage = SmsMessage.createFromPdu(sm as ByteArray, format)
} else {
smsMessage = SmsMessage.createFromPdu(sm as ByteArray)
}
val msgBody = smsMessage.messageBody.toString()
val msgAddress = smsMessage.originatingAddress
smsMsg.append("SMS from : ").append(msgAddress).append("\n")
smsMsg.append(msgBody).append("\n")
}
sendBroadcast(smsMsg.toString())
}
}
}
}
private fun sendBroadcast(smsMSG: String) {
val broadcastIntent = Intent()
broadcastIntent.action = AppConstants.mBroadcastSMSUpdateAction
broadcastIntent.putExtra(AppConstants.message, smsMSG)
EventBus.getDefault().post(EventIntent(broadcastIntent))
}
companion object {
val SMS_BUNDLE = "pdus"
}
}
You can broadcast the received SMS information to your MainActivity.
Then you need to declare the SMS Broadcast receiver in your manifest file as follows:
<receiver
android:name=".SMSReceiver"
android:enabled="true">
<intent-filter android:priority="2147483647">
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
After that on application launch in MainActivity you need to check for SMS permissions by checking:
Manifest.permission.READ_SMS,
Manifest.permission.RECEIVE_SMS
You can use built in android permission module or RxPermission for that :
val rxPermissions = RxPermissions(this)
rxPermissions.request(
Manifest.permission.READ_SMS,
Manifest.permission.RECEIVE_SMS)
.subscribe(object : Observer<Boolean> {
override fun onNext(t: Boolean) {
if (t) {
} else {
Toast.makeText(activity, getString(R.string.permission_request_denied), Toast.LENGTH_LONG).show()
}
}
override fun onSubscribe(d: Disposable) {}
override fun onError(e: Throwable) {}
override fun onComplete() {}
})
Related
I have created Two App for NFC. 1. NFC Sender 2. NFC Receiver. NFC Sender sends simple text data to NFC Receiver App. But I am in a big problem here.
I am unable to read simple text from NFC. The text format I am sending using NFC is NdefRecord.
1. NFC Sender Activity code :
public class MainActivity extends AppCompatActivity {
private Button btnPay;
private LinearLayout llRootView;
private NfcAdapter mNfcAdapter;
private EditText edtAmount;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnPay = findViewById(R.id.btnPay);
llRootView = findViewById(R.id.rootView);
edtAmount = findViewById(R.id.etAmount);
btnPay.setOnClickListener(view -> {
hideKeyboard(MainActivity.this);
if (TextUtils.isEmpty(edtAmount.getText().toString().trim())) {
Snackbar.make(llRootView, "Please enter valid value.", Snackbar.LENGTH_SHORT).show();
return;
}
startNFC();
});
}
private void startNFC() {
//Check if NFC is available on device
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter != null) {
//This will be called if the message is sent successfully
mNfcAdapter.setOnNdefPushCompleteCallback(event -> runOnUiThread(() ->
Toast.makeText(MainActivity.this, "Message sent.", Toast.LENGTH_SHORT)
.show()), this);
//This will refer back to createNdefMessage for what it will send
mNfcAdapter.setNdefPushMessageCallback(event -> new NdefMessage(createRecords()), this);
Snackbar.make(llRootView, "Please attach receiver device to back of your device.", Snackbar.LENGTH_INDEFINITE).show();
} else {
Toast.makeText(this, "NFC not available on this device",
Toast.LENGTH_SHORT).show();
}
}
private NdefRecord[] createRecords() {
//Api is high enough that we can use createMime, which is preferred.
NdefRecord record = NdefRecord.createMime("text/plain", edtAmount.getText().toString().trim().getBytes(StandardCharsets.UTF_8));
return new NdefRecord[]{
record
, NdefRecord.createApplicationRecord("com.example.nfcreceiver")
};
}
public static void hideKeyboard(Activity activity) {
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
//Find the currently focused view, so we can grab the correct window token from it.
View view = activity.getCurrentFocus();
//If no view currently has focus, create a new one, just so we can grab a window token from it
if (view == null) {
view = new View(activity);
}
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
2. NFC Receiver Manifest :
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
3. NFC Receiver Activity Code :
public class MainActivity extends AppCompatActivity {
NfcAdapter mAdapter;
PendingIntent mPendingIntent;
TextView tvReceived, tvTagTech;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvReceived = findViewById(R.id.tvReceived);
tvTagTech = findViewById(R.id.tvTagList);
Initialization();
}
#Override
protected void onResume() {
super.onResume();
if (!WebConstant.isOnline()) {
Snackbar.make(tvReceived, R.string.str_no_internet_connection, Snackbar.LENGTH_LONG).show();
}
resolveIntent(getIntent());
if (mAdapter != null) {
if (!mAdapter.isEnabled()) {
Toast.makeText(this, "NFC Not Enabled", Toast.LENGTH_LONG).show();
}
mAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
}
}
#Override
protected void onPause() {
super.onPause();
if (mAdapter != null) {
mAdapter.disableForegroundDispatch(this);
}
}
private void Initialization() {
mAdapter = NfcAdapter.getDefaultAdapter(this);
if (mAdapter == null) {
Toast.makeText(this, "NFC Not found", Toast.LENGTH_LONG).show();
// finish();
return;
}
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
}
#Override
protected void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
new Handler().postDelayed(() -> {
setIntent(intent);
}, 0);
}
private void resolveIntent(Intent intent) {
String action = intent.getAction();
Log.d("ActionType", action);
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
getTagInfo(intent);
}
}
private void getTagInfo(Intent intent) {
sendToFirebase(intent);
}
private void sendToFirebase(Intent intent) {
String title, message;
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
NdefMessage[] messages = getNdefMessages(intent);
if (messages != null && messages.length > 0) {
title = "Success";
message = "Received NDEF Messages";
showAlert(title, message);
try {
String msg = new String(messages[0].getRecords()[0].getPayload(), StandardCharsets.UTF_8);
message = String.format("You received %sQAR", msg);
tvReceived.setText(message);
} catch (Exception e) {
e.printStackTrace();
title = "Error";
message = e.getLocalizedMessage();
showAlert(title, message);
}
} else {
title = "Failure";
message = "Received empty NDEF Messages";
showAlert(title, message);
}
} else {
title = "Failure";
message = "Received non NDEF Messages";
showAlert(title, message);
}
Map<String, Object> body = new HashMap<>();
body.put("Title", title);
body.put("Message", message);
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag != null) {
body.put("TagList", Arrays.toString(tag.getTechList()));
} else {
body.put("TagList", "");
}
RestClient.getInstance().push("NFCDetails", body, mResponse -> {
});
}
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;
}
}
public void showAlert(String title, String message) {
AlertDialog dialog = new AlertDialog.Builder(this)
.create();
dialog.setTitle(title);
dialog.setMessage(message);
dialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", (dialog1, which) -> dialog1.dismiss());
dialog.show();
}
}
Please Help me to solve this puzzle.
The tag technology I am receiving in the Receiver app is below :
[android.nfc.tech.IsoDep, android.nfc.tech.NfcA]
Android Beam was deprecated in Android 10
Therefore the Pixel phone does not have the capability to send or receive NDEF messages sent from other old Android phones via this method you are trying to use.
Update:
The IsoDep Tag you are seeing is most likely being generated by the secure element part of the NFC hardware in the device as part of the NFC wallet type functionality that can hold your contactless bank card details. You would read this as though you were reading a contactless bank cards with, using the right AID's and higher level protocols and standards (But if you have not loaded a credit/debit card in to Google Wallet, it won't respond to any of the standard AID's for credit/debit cards)
The only thing you could really read from at the IsoDep level is a randomly generated UID.
Android does allow you to do Host Card Emulation (HCE) which allows you to set the response to selected AID queries. There is an AID for NDef data, thus using HCE you could replicate the Android Beam type funtionality, but this is complicated.
The recommended way to send data between 2 devices is via Bluetooth or Wifi Direct
I have read many posts related to NFC scanning on Android, however I am unable to make it work.
The onNewIntent function never gets fired by my app sample.
In my activity, I have the following code:
//The array lists to hold our messages
private ArrayList<String> messagesToSendArray = new ArrayList<>();
private ArrayList<String> messagesReceivedArray = new ArrayList<>();
//Text boxes to add and display our messages
private EditText txtBoxAddMessage;
private TextView txtReceivedMessages;
private TextView txtMessagesToSend;
private NfcAdapter mNfcAdapter;
public void addMessage(View view) {
String newMessage = txtBoxAddMessage.getText().toString();
messagesToSendArray.add(newMessage);
txtBoxAddMessage.setText(null);
updateTextViews();
Toast.makeText(this, "Added Message", Toast.LENGTH_LONG).show();
}
private void updateTextViews() {
txtMessagesToSend.setText("Messages To Send:\n");
//Populate Our list of messages we want to send
if(messagesToSendArray.size() > 0) {
for (int i = 0; i < messagesToSendArray.size(); i++) {
txtMessagesToSend.append(messagesToSendArray.get(i));
txtMessagesToSend.append("\n");
}
}
txtReceivedMessages.setText("Messages Received:\n");
//Populate our list of messages we have received
if (messagesReceivedArray.size() > 0) {
for (int i = 0; i < messagesReceivedArray.size(); i++) {
txtReceivedMessages.append(messagesReceivedArray.get(i));
txtReceivedMessages.append("\n");
}
}
}
//Save our Array Lists of Messages for if the user navigates away
#Override
public void onSaveInstanceState(#NonNull Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putStringArrayList("messagesToSend", messagesToSendArray);
savedInstanceState.putStringArrayList("lastMessagesReceived",messagesReceivedArray);
}
//Load our Array Lists of Messages for when the user navigates back
#Override
public void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
messagesToSendArray = savedInstanceState.getStringArrayList("messagesToSend");
messagesReceivedArray = savedInstanceState.getStringArrayList("lastMessagesReceived");
}
private PendingIntent mPendingIntent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtBoxAddMessage = (EditText) findViewById(R.id.txtBoxAddMessage);
txtMessagesToSend = (TextView) findViewById(R.id.txtMessageToSend);
txtReceivedMessages = (TextView) findViewById(R.id.txtMessagesReceived);
Button btnAddMessage = (Button) findViewById(R.id.buttonAddMessage);
btnAddMessage.setText("Add Message");
updateTextViews();
//Check if NFC is available on device
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if(mNfcAdapter != null) {
//Handle some NFC initialization here
}
else {
Toast.makeText(this, "NFC not available on this device",
Toast.LENGTH_SHORT).show();
}
//Check if NFC is available on device
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if(mNfcAdapter != null) {
//This will refer back to createNdefMessage for what it will send
mNfcAdapter.setNdefPushMessageCallback(this, this);
//This will be called if the message is sent successfully
mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
}
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
this.onNewIntent(getIntent());
}
#Override
protected void onResume() {
super.onResume();
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
}
#Override
protected void onPause() {
super.onPause();
if (mNfcAdapter != null) {
mNfcAdapter.disableForegroundDispatch(this);
}
}
#Override
public NdefMessage createNdefMessage(NfcEvent nfcEvent) {
//This will be called when another NFC capable device is detected.
if (messagesToSendArray.size() == 0) {
return null;
}
//We'll write the createRecords() method in just a moment
NdefRecord[] recordsToAttach = createRecords();
//When creating an NdefMessage we need to provide an NdefRecord[]
return new NdefMessage(recordsToAttach);
}
#Override
public void onNdefPushComplete(NfcEvent nfcEvent) {
//This is called when the system detects that our NdefMessage was
//Successfully sent.
messagesToSendArray.clear();
}
public NdefRecord[] createRecords() {
NdefRecord[] records = new NdefRecord[messagesToSendArray.size()];
for (int i = 0; i < messagesToSendArray.size(); i++){
byte[] payload = messagesToSendArray.get(i).
getBytes(StandardCharsets.UTF_8);
NdefRecord record = new NdefRecord(
NdefRecord.TNF_WELL_KNOWN, //Our 3-bit Type name format
NdefRecord.RTD_TEXT, //Description of our payload
new byte[0], //The optional id for our Record
payload); //Our payload for the Record
records[i] = record;
}
return records;
}
#Override
protected void onNewIntent(Intent intent) {
Log.e("TAG", "onNewIntent: "+intent.getAction());
super.onNewIntent(intent);
}
This is my manifest:
<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:launchMode="singleTask"
android:name="com.banalapps.nfcmesssenger.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
I am aware that this is probably a duplicate, but any help would be greatly appreciated as I struggled for days trying to make this work.
I am preparing an app which requires me to have user's phone number and then send a hashcode the user programatically as SMS so that it will be used as their token afterwards.
I am making the user enter the number in the MainActivty and have my receiver which listens to SMS_RECEIVED so that it will notiy the user, first of all I want to make sure I am sending and receiving the user's number correctly and receving it. However my receiver does not seems to listen to the trigger. I can see the received sms on notification as well as I can hear the sound but broadcast receiver does not listens to it.
My Code for MainActivity is
public class MainActivity extends Activity
{
Button submit;
EditText contact;
static String phNo;
ProgressDialog progress;
static Boolean wasMyOwnNumber;
static Boolean workDone;
final static int SMS_ROUNDTRIP_TIMOUT = 30000;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
contact = (EditText)findViewById(R.id.mobileNumber);
submit = (Button) findViewById(R.id.button1);
wasMyOwnNumber = false;
workDone = false;
submit.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
phNo = contact.getText().toString();
new CheckOwnMobileNumber().execute();
}
});
}
private class CheckOwnMobileNumber extends AsyncTask<String, Void, String>
{
#Override
protected void onPostExecute(String result)
{
// TODO Auto-generated method stub
if(progress.isShowing())
{
progress.dismiss();
if(wasMyOwnNumber)
{
Toast.makeText(getApplicationContext(), "Number matched.", Toast.LENGTH_LONG).show();
wasMyOwnNumber = false;
workDone = false;
}
else
{
Toast.makeText(getApplicationContext(), "Wrong number.", Toast.LENGTH_LONG).show();
wasMyOwnNumber = false;
workDone = false;
return;
}
}
super.onPostExecute(result);
}
#Override
protected String doInBackground(String... params)
{
// TODO Auto-generated method stub
String msg = phNo;
try
{
SmsManager sms = SmsManager.getDefault();
sms.sendTextMessage(phNo, null, msg, null, null);
timeout();
}
catch(Exception ex)
{
Log.v("Exception :", ""+ex);
}
return null;
}
#Override
protected void onPreExecute()
{
// TODO Auto-generated method stub
progress = ProgressDialog.show(MainActivity.this, "","Checking Mobile Number...");
progress.setIndeterminate(true);
progress.getWindow().setLayout(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
super.onPreExecute();
}
}
private boolean timeout()
{
int waited = 0;
while (waited < SMS_ROUNDTRIP_TIMOUT)
{
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
waited += 100;
if(phoneNumberConfirmationReceived())
{
waited=SMS_ROUNDTRIP_TIMOUT;
workDone = true;
}
}
/*Log.v("MainActivity:timeout2: Waited: " , ""+waited);
Log.v("MainActivity:timeout2:Comparision: ", ""+ phoneNumberConfirmationReceived());
Log.v("MainActivity:timeout2: WorkDone value after wait complete : ", ""+workDone);*/
return workDone;
}
private boolean phoneNumberConfirmationReceived()
{
if(wasMyOwnNumber)
{
workDone = true;
}
return workDone;
}
MyCode for SMSReceiver is
public class SMSReceiver extends BroadcastReceiver
{
private static final String ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
Context mContext;
private Intent mIntent;
static String address, str = null;
boolean isSame;
// Retrieve SMS
public void onReceive(Context context, Intent intent)
{
Log.v("ONMESSAGE", "RECEIVED");
mContext = context;
mIntent = intent;
String action = intent.getAction();
SmsMessage[] msgs = getMessagesFromIntent(mIntent);
if (msgs != null)
{
for (int i = 0; i < msgs.length; i++)
{
address = msgs[i].getOriginatingAddress();
str = msgs[i].getMessageBody().toString();
}
}
Log.v("Originating Address : Sender :", ""+address);
Log.v("Message from sender :", ""+str);
isSame = PhoneNumberUtils.compare(str, MainActivity.phNo);
Log.v("Comparison :", "Yes this true. "+isSame);
if(isSame)
{
MainActivity.wasMyOwnNumber = isSame;
MainActivity.workDone=true;
}
// ---send a broadcast intent to update the SMS received in the
// activity---
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("SMS_RECEIVED_ACTION");
broadcastIntent.putExtra("sms", str);
context.sendBroadcast(broadcastIntent);
}
public static SmsMessage[] getMessagesFromIntent(Intent intent)
{
Object[] messages = (Object[]) intent.getSerializableExtra("pdus");
byte[][] pduObjs = new byte[messages.length][];
for (int i = 0; i < messages.length; i++)
{
pduObjs[i] = (byte[]) messages[i];
}
byte[][] pdus = new byte[pduObjs.length][];
int pduCount = pdus.length;
SmsMessage[] msgs = new SmsMessage[pduCount];
for (int i = 0; i < pduCount; i++)
{
pdus[i] = pduObjs[i];
msgs[i] = SmsMessage.createFromPdu(pdus[i]);
}
return msgs;
}
}
*My entries in Manifest is *
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.getphonenumber"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.example.getphonenumber.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="com.example.getphonenumber.SMSReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
</application>
</manifest>
Give a priority value for the intent..
change your <intent-filter> as following
<intent-filter android:priority="999" >
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
Have a try..
Also add permission to receive sms
<uses-permission android:name="android.permission.RECEIVE_SMS" />
After spending most of a day stuck here, figured I'd share my experience in case anyone is stuck in the same boat. I spent several hours troubleshooting a failure to get any sms notifications which I finally resolved with calls to acquire permissions. I believe it is related to the changes in permissions in recent sdk versions.
I set up receiver in androidmanifest.xml as stated above, and I also added it programmatically without any luck.
I had permissions in androidmanifest.xml:
<uses-feature android:name="android.permission.RECEIVE_SMS" android:required="true"></uses-feature>
<uses-feature android:name="android.permission.READ_SMS" android:required="true"></uses-feature>
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
I had the same issue whether I put the receiver in the androidmanifest.xml (in the receiver block under the application block) or if I did it programmatically as follows:
private void InstallFunction () {
final YourActivity activity = this;
this.smsReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
activity.onReceiveIntent(context, intent);
}
};
filter = new IntentFilter();
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); // tried 0, 999, Integer.MaxValue, etc.
filter.addAction(Telephony.Sms.Intents.SMS_RECEIVED_ACTION); // SMS
this.registerReceiver(smsReceiver, filter);
It gave no error nor logged any warnings, but the onReceive was never called using sms on emulator or on my phone.
A call to check if I had the permissions as per the one below said I had both.
canReadSMS = ActivityCompat.checkSelfPermission(this,
Manifest.permission.READ_SMS) == PackageManager.PERMISSION_GRANTED;
canReceiveSMS = ActivityCompat.checkSelfPermission(this,
Manifest.permission.Manifest.permission.RECEIVE_SMS) == PackageManager.PERMISSION_GRANTED);
canReadSMS and canReceiveSMS were both true.
Despite that, no events ever arrived. After rewriting it several times, and trying everything a million ways, I finally decided to ignore the permissions responses and ask for permission anyway.
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.RECEIVE_SMS) ||
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_SMS)) {
new AlertDialog.Builder(this)
.setMessage("Would you like TheApp to handle confirmation codes text messages for you to make registration easier?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(activity,
new String[]{
Manifest.permission.RECEIVE_SMS,
Manifest.permission.READ_SMS
},
SMS_PERMISSIONS_REQUEST_ID);
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
})
.create()
.show();
After running this, the notifications started arriving and I was very excited. Originally, I did not have the calls to 'shouldShowRequestPermissionRationale' which seemed to lead to problems with crashing or ending up at an activity where the input wouldn't let me enter data. After some research, I came across the ActivityCompat.shouldShowRequestPermissionRationale(... as per the code above. After the user has given the app permissions for sms, that function seems to no longer return true. The end result is that they are asked a single time for the permission for the app. The other complication I think brought about by the new permissions scheme was that if I asked for permission on the confirmation page, they could be responding to the permissions query when the text arrived -- in which case the text would not arrive to the message handler. I resorted to asking for permissions on the registration page prior to opening up the confirmation page. This worked nicely.
Note: SMS_PERMISSIONS_REQUEST_ID is a just variable with a particular integer value assigned. Assign it any reasonable name and number you like.
In general, you implement the override "onRequestPermissionsResult" function to determine if you got permissions or not, but in my case the permission was optional -- if the app cannot read the sms, the user can still manually type or past the code. That being the case, I was able to omit that override. Depending on your implementation, you might need it -- many samples are available for that handler. The ID defined above is used in that handler to determine that you are getting the result for the permissions request you just made.
There are also numerous examples of processing the sms messages, but in case anyone lands here, I'll include my code that processed incoming SMS. It's intentionally written a bit wordy and easy to digest as I was hunting problems.
private void onReceiveIntent (Context context, Intent intent) {
if (context == this &&
intent != null) {
if (intent.getAction ().toString ().equals (Telephony.Sms.Intents.SMS_RECEIVED_ACTION)) {
Bundle bundle;
bundle = intent.getExtras ();
if (bundle != null) {
Object[] pdus;
String format;
format = bundle.getString ("format");
pdus = (Object[]) bundle.get("pdus");
if (pdus != null &&
pdus.length > 0) {
StringBuilder body;
SmsMessage header;
SmsMessage sms;
body = new StringBuilder();
header = SmsMessage.createFromPdu((byte[]) pdus[0],
format);
for (Object pdu : pdus) {
//sms = SmsMessage.createFromPdu ((byte[]) pdu); <-- has been deprecated
sms = SmsMessage.createFromPdu ((byte[]) pdu,
format); // <-- the new way -- get the format as per above.
if (sms != null) {
body.append (sms.getDisplayMessageBody());
}
}
ProcessSmsMessage (header,
body.toString ());
}
}
}
}
}
private void ProcessSmsMessage(SmsMessage header,
String message) {
if (header != null &&
message != null) {
String originator;
originator = header.getOriginatingAddress();
ProcessSmsMessage(originator,
message);
}
}
private void ProcessSmsMessage(String originator,
String message) {
if (originator != null &&
originator.equals ("##########")) { // does it come from my twilio sms number?
if (message.startsWith("$TheApp$ Device Verification Code:")) {
final DeviceConfirmationActivity confirmationActivity = this;
final String code;
code = message.substring (##); // past the name and text message above
this.mConfirmationCodeView.setText (code); // <-- the manual entry box now filled for them
// now you can do some work. I use a custom timer to do this work on main thread
// per much research, you should avoid doing lengthy operation in this thread
// You can also set up a job.
confirmationActivity.DoDeviceConfirmation(code);
}
}
}
As mentioned in the code, the
sms = SmsMessage.createFromPdu ((byte[]) pdu);
used in most sample code is now deprecated. Fortunately, found some samples that showed the new version:
sms = SmsMessage.createFromPdu ((byte[]) pdu,
format);
and that the format comes from: bundle.getString ("format"); as per code above.
Thanks a lot to Shirish Herwade especially: SmsMessage.createFromPdu is deprecated in android API level 23
Hope that helps someone stuck the way I was. I am fairly new to Android programming having resorted to cordova in the past, so someone else may be able to provide better insight on why that permissions call had to be made before getting notifications even though permissions checks return values that would make one think I already had said permission.
Add permission :
<uses-permission android:name="android.permission.RECEIVE_SMS" />
Write in manifest:
<receiver
android:name="yourpackagename.SmsReceive"
android:enabled="true" >
<intent-filter android:priority="1000" >
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
SmsReceive class:
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
SmsMessage[] msgs = null;
if (bundle != null) {
str = "";
substr = null;
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();
sender=msgs[i].getOriginatingAddress();
str += msgs[i].getMessageBody().toString();
if(str.length()!=0)
{
//write the logic
}
}
}
}
I want to open my application when I get notification message "abc". I can do this with SMSReceiver but only get sms message. I want do this whatsapp message. sory for bad English.
#Override
public void onReceive(Context context, Intent intent) {
String hangiNumaradan = "";
String neYazmis = "";
Bundle bundle = intent.getExtras();
SmsMessage[] msgs = null;
if (bundle != null) {
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]);
hangiNumaradan += msgs[i].getOriginatingAddress();
neYazmis += msgs[i].getMessageBody().toString();
}
Toast.makeText(context, hangiNumaradan + " gelen mesaj " + neYazmis, Toast.LENGTH_LONG).show();
}
}
This code is smsreceiver and toast message.
After doing some investigation I realized that since Facebook bought Whatsapp, they closed all public Whatsapp API and right now it's impossible to create Whatsapp message receiver, BUT there is one thing that you can do.
Create AccessibilityService:
public class MyAccessibilityService extends AccessibilityService {
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
//here you can implement your reaction for incoming notification
//here you can find some tags that could be helpful for you
String helpful = event.getContentDescription();
//In code below you could also retrieve some helpful info from Notification
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
return;
}
// Grab the parent of the view that fired the event.
AccessibilityNodeInfo rowNode = getListItemNodeInfo(source);
if (rowNode == null) {
return;
}
// Using this parent, get references to both child nodes, the label and the checkbox.
AccessibilityNodeInfo labelNode = rowNode.getChild(0);
if (labelNode == null) {
rowNode.recycle();
return;
}
AccessibilityNodeInfo completeNode = rowNode.getChild(1);
if (completeNode == null) {
rowNode.recycle();
return;
}
// Determine what the task is and whether or not it's complete, based on
// the text inside the label, and the state of the check-box.
if (rowNode.getChildCount() < 2 || !rowNode.getChild(1).isCheckable()) {
rowNode.recycle();
return;
}
CharSequence anotherInfoFromNotification = labelNode.getText();
}
#Override
public void onInterrupt() {
}
}
Create serviceconfig.xml file:
<accessibility-service
android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
android:packageNames="com.whatsapp"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:settingsActivity="com.example.android.your.way.to.activity"
android:canRetrieveWindowContent="true"/>
Register service in manifest:
<service android:name=".MyAccessibilityService">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data android:name="android.accessibilityservice"
android:resource="#xml/serviceconfig" />
</service>
So to summ it up: you will be able to get listener to all incoming whatsapp messages, you have to try little bit more with code in onAccessibilityEvent method to retrive info from message.
I am newbie in developing android application. Currently I am trying to develop my own tag reader which can read MiFare Ultralight tag.
BUt I failed to read the tag as NfcAdapter.ACTION_TECH_DISCOVERED.equals(action) always return false. Could somebody help me out?
NfcReader.java
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mText = (TextView) findViewById(R.id.text);
mText.setText("Scan a tag");
mAdapter = NfcAdapter.getDefaultAdapter();
// Create a generic PendingIntent that will be deliver to this activity. The NFC stack
// will fill in the intent with the details of the discovered tag before delivering to
// this activity.
mPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
// Setup an intent filter for all MIME based dispatches
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
try {
ndef.addDataType("*/*");
} catch (MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
mFilters = new IntentFilter[] {
ndef,
};
// Setup a tech list for all NfcF tags
mTechLists = new String[][] { new String[] { NfcF.class.getName() } };
Intent intent = getIntent();
getNdefMessages(intent);
}
public void getNdefMessages(Intent intent) {
// Parse the intent
NdefMessage[] msgs = null;
String action = intent.getAction();
if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
}
}
else {
// Unknown tag type
byte[] empty = new byte[] {};
NdefRecord record = new NdefRecord(NdefRecord.TNF_UNKNOWN, empty, empty, empty);
NdefMessage msg = new NdefMessage(new NdefRecord[] {record});
msgs = new NdefMessage[] {msg};
}
}
else {
//Log.e(TAG, "Unknown intent " + intent);
finish();
}
}
#Override
public void onResume() {
super.onResume();
mAdapter.enableForegroundDispatch(this, mPendingIntent, mFilters, mTechLists);
}
#Override
public void onNewIntent(Intent intent) {
Log.i("Foreground dispatch", "Discovered tag with intent: " + intent);
getNdefMessages(intent);
}
#Override
public void onPause() {
super.onPause();
//mAdapter.disableForegroundDispatch(this);
throw new RuntimeException("onPause not implemented to fix build");
}
Manifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.nfc.reader"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.NFC" />
<uses-sdk android:minSdkVersion="10"/>
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<application android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name="NfcReader"
android:theme="#android:style/Theme.NoTitleBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED"/>
<data android:mimeType="text/plain" />
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="#xml/nfctech" />
</activity>
</application>
</manifest>
When a tag with an NDEF message is detected, android looks for any Application with an Activity with intent-filter for action "android.nfc.action.NDEF_DISCOVERED". If two or more applications can handle this action, the one with the most precise filter receives the intent. When applications with the same filter are found, is the user who chooses one. So in your AndroidManifest.xml you have to use this action for detecting NDEF formatted tags.
If you want to read NDEF messages don't worry about which type of tag you are reading (Ultralight, Felica, Topaz,...). Android uses android.nfc.tech.Ndef for dispatch NDEF formatted tags. So for getting all NdefMessages when you activity is on foreground, you can use this snippet:
//Called in onCreate()
private void nfcConfig() {
mAdapter = NfcAdapter.getDefaultAdapter(this);
pendingIntent = PendingIntent.getActivity(
this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
techListsArray = new String[][]{new String[]{
Ndef.class.getName()
}};
}
#Override
public void onResume() {
super.onResume();
mAdapter.enableForegroundDispatch(this, pendingIntent, null, techListsArray);
}
#Override
public void onPause() {
super.onPause();
mAdapter.disableForegroundDispatch(this);
}
#Override
public void onNewIntent(Intent intent) {
getNdefMessages(intent);
}
If you want to read an Ultralight tag ( or another technology), it should be because you need the raw data from the tag. It must not be executed from the main application thread. For example:
private void nfcConfig(){
...
//Mifare Ultralight filter
techListsArray = new String[][]{new String[]{
NfcA.class.getName(),
MifareUltralight.class.getName()
}};
...
}
#Override
public void onNewIntent(Intent intent) {
getRawData(intent);
}
private void getRawData(Intent intent){
final Tag tag = (Tag) intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
MifareUltralight ulTag = MifareUltralight.getTag();
new ReadMFULTask(ulTag).execute();
}
private static class ReadMFULTask extends AsyncTask<Void, Void, Void> {
private Tag mTag;
public ReadMFULTask(Tag tag){
mTag = tag;
}
#Override
protected Void doInBackground(Void... arg0) {
MifareUltralight ulTag = MifareUltralight.get(mTag);
try {
ulTag.connect();
//Read all pages from an Ul tag
for(int i=0; i < 16; i=i+4){
byte[] data = ulTag.readPages(i);
String dataString = HexUtilities.getHexString(data);
Log.d("RAW DATA", dataString);
}
ulTag.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
In your code you are setting your intent filter based on ACTION_TECH_DISCOVERED, but then you try ndef.addDataType(/), which is the way you would go about setting up an intent filter for ACTION_NDEF_DISCOVERED. Instead you could simply do something like this:
IntentFilter ntech = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
mFilters = new IntentFilter[]{ ntech };
make sure you have nfc_tech_list.xml set up properly:
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.NfcA</tech>
<tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>
</resources>