I am developing an application that requires the use of an NFC-Reader. However, the NFC-Reader should just run when using the Scan Activity.
I already implemented an onPause() method, including disableForegroundDispatch() which also gets performed when switching to a different activity which can be verified by
System.out.println("pause!!!");
So surprisingly, at least for me, one is still able to scan an NFC-Chip in different activities which forwards one back to the Scan-Activity.
Any help is highly appreciated!!! :D
Here's the source code (Scan.activity), in case that help solving the problem):
package com.example.bnm_10112021;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MenuItem;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.navigation.NavigationBarView;
import android.app.PendingIntent;
import android.content.Context;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Parcelable;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.w3c.dom.Text;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.ExecutionException;
public class Scan extends AppCompatActivity {
public static final String Error_Detected = "No NFC Tag Detected";
public static final String Write_Success = "Text Written Successfully!";
public static final String Write_Error = "Error during Writing, Try Again!";
NfcAdapter nfcAdapter;
PendingIntent pendingIntent;
IntentFilter writingTagFilters[];
boolean writeMode;
Tag myTag;
Context context;
TextView edit_message;
TextView nfc_contents;
Button ActivateButton;
BottomNavigationView bottomNavigationItemView;
**//Erstellung des dbConnection-Objekts**
public DatabaseConnection dbConnector = new DatabaseConnection();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan);
bottomNavigationItemView = findViewById(R.id.navigator);
bottomNavigationItemView.setSelectedItemId(R.id.scan);
nfc_contents = (TextView) findViewById(R.id.nfc_contents);
context = this;
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
/**if(nfcAdapter == null){
Toast.makeText(this, "This device does not support NFC", Toast.LENGTH_SHORT).show();
finish();
}**/
readFromIntent(getIntent());readFromIntent(getIntent());
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
//DOKU, VERALTET!!!!
bottomNavigationItemView.setOnItemSelectedListener(new NavigationBarView.OnItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
int id = item.getItemId();
switch (id){
case R.id.settings:
startActivity(new Intent(getApplicationContext(),Settings.class));
overridePendingTransition(0,0);
return true;
case R.id.stats:
startActivity(new Intent(getApplicationContext(),Statistics.class));
overridePendingTransition(0,0);
return true;
case R.id.scan:
return true;
case R.id.wash:
startActivity(new Intent(getApplicationContext(),Wash.class));
overridePendingTransition(0,0);
return true;
case R.id.mycloset:
startActivity(new Intent(getApplicationContext(),MyCloset.class));
overridePendingTransition(0,0);
return true;
}
return false;
}
});
}
//String: Parameter an doInBackground; Void: Parameter an publishProgress bzw. onProgressUpdate; String[]: Parameter von doInBackground an onPostExecute
private class DauertLange extends AsyncTask<String, Void, String[]> {
@Override
protected void onPreExecute() {
super.onPreExecute();
//Loading Animation
System.out.println("Vorher");
}
@Override
protected String[] doInBackground(String... pStrings) {
//publishProgress();
String sN = pStrings[0];
String tN = pStrings[1];
String bd = pStrings[2];
String z = pStrings[3];
System.out.println(tN);
String[] rueckgabe = dbConnector.select(sN,tN,bd,z);
return rueckgabe;
}
/** protected void onProgressUpdate(){
bottomSheetDialog.setContentView(R.layout.layout_bottom_sheet_loading);
}**/
protected void onPostExecute() {
super.onPreExecute();
//Loading Animation
System.out.println("Nacher");
}
}
private void readFromIntent(Intent intent) {
String action = intent.getAction();
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage[] msgs = null;
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
}
}
buildTagViews(msgs);
}
}
private void buildTagViews(NdefMessage[] msgs) {
if (msgs == null || msgs.length == 0) return;
String text = "";
byte[] payload = msgs[0].getRecords()[0].getPayload();
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16"; // Get the Text Encoding
int languageCodeLength = payload[0] & 0063; // Get the Language Code, e.g. "en"
try {
// Get the Text
text = new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
} catch (UnsupportedEncodingException e) {
Log.e("UnsupportedEncoding", e.toString());
}
System.out.println(text);
String chipID = text;
/**String rueckgabe = dbConnector.select("Seriennummer","Chips","NFCID = '" + chipID + "'");
System.out.println(rueckgabe);
String ausgabe = dbConnector.select("Art","Kleidungstypen","Seriennummer = '" + rueckgabe + "'");
System.out.println(ausgabe);
nfc_contents.setText(ausgabe);**/
final BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(
Scan.this, R.style.BottomSheetDialogTheme
);
View bottomSheetView = LayoutInflater.from(getApplicationContext())
.inflate(
R.layout.layout_bottom_sheet,
null
);
bottomSheetDialog.setContentView(bottomSheetView);
bottomSheetDialog.show();
//bottomSheetDialog.setContentView(R.layout.layout_bottom_sheet_loading);
LinearLayout ll = bottomSheetView.findViewById(R.id.bottomSheetContainer);
//Textfelder identifzieren
TextView tvBezeichnung = ll.findViewById(R.id.tvBezeichnung);
TextView tvMarke = ll.findViewById(R.id.tvMarke);
TextView tvColor = ll.findViewById(R.id.tvColor);
TextView tvSize = ll.findViewById(R.id.tvSize);
TextView tvHerkunft = ll.findViewById(R.id.tvHerkunft);
//Datenbankverbindung über AsyncTask mit Array als Rückgabe
String[] farbe = new String[10];
try {
farbe = new DauertLange().execute("Kleidungstypen.Bezeichnung, Kleidungstypen.Marke, Kleidungstypen.Farbe, Kleidungstypen.Size, Kleidungstypen.Herkunft","Kleidungstypen INNER JOIN Chips ON Kleidungstypen.Seriennummer = Chips.Seriennummer", "NFCID = '" + chipID + "'","5").get();
} catch (ExecutionException e) {
e.printStackTrace();
bottomSheetDialog.dismiss();
} catch (InterruptedException e) {
e.printStackTrace();
bottomSheetDialog.dismiss();
}
//Inhalte der Textfelder mit Daten aus DB setzen
tvBezeichnung.setText(farbe[0]);
tvMarke.setText(farbe[1]);
tvColor.setText(farbe[2]);
tvSize.setText(farbe[3]);
tvHerkunft.setText("Made in " + farbe[4]);
//RoundedImageView img = ll.findViewById(R.id.image111);
//img.setImageURI(uri);
bottomSheetView.findViewById(R.id.addToCloset).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
bottomSheetDialog.dismiss();
}
});
//bottomSheetDialog.setContentView(bottomSheetView);
//bottomSheetDialog.show();
}
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
readFromIntent(intent);
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
myTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
}
}
@Override
public void onPause(){
System.out.println("pause!!!");
super.onPause();
nfcAdapter.disableForegroundDispatch(this);
nfcAdapter.disableReaderMode(this);
}
@Override
public void onResume(){
super.onResume();
nfcAdapter.enableForegroundDispatch(this, pendingIntent, writingTagFilters, null);
}
}
If you really want to just make it look like scanning is happening in just one of your Activities then really what you need to do is silently handle NFC in all your other Activities.
This is because it NFC events are actually detected and processed in the System NFC App/Service and if you have have not asked them to be forwarded to your Foreground App then the System NFC App/Service will likely to handle the Tag data itself and do something with it (open another App or open it's own dialog to display the content of the Tag).
So just disabling it in your Scan Activity is just allowing other Actions to happen and not actually stopping NFC processing from happening.
As the old API of
enableForegroundDispatchgives you very little control and have no control over the NFC event sound the system generate, then you need to use the better API ofenableReaderMode.So in All Activities
enableReaderModeinonResumeanddisableReaderModeinonPause. You would set it to capture all Tag Technologies and to turn off NFC sounds.Then when you want an Activity not to respond to NFC Tags the
onTagDiscoveredis just an empty method. So system silently sends any Tag discovered event to your App for it to ignore.Then in the "Scan.activity" the
onTagDiscoveredmethod does your NFC processing. (And then should do the sound notification itself instead of the system)You should also have a Broadcast receiver to
enableReaderModewhen the NFC is turned on while you App is in the Foreground (Using the NFC quick settings does not pause your App).You don't need any Intent filters unless you want you App started by an NFC card with a certain type of data.
Example Broadcast receiver and
enableReaderModein this answer https://stackoverflow.com/a/59397667/2373819This silent capture of all NFC events in all non NFC activities is how I do it in my App (you can create a template class by extending the normal Activity class with the code for the silent handling of NFC and then use this as the base for your Activity classes)