0

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);
    }



}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
e5lite
  • 11
  • 6
  • My understanding of `enableForegroundDispatch` is that it gives your Activity priority for handling NFC intents while the Activity is in the foreground. E.g. if a tag is discovered that could be handled by more than one app, then you get to handle it first. So disabling foreground dispatch disables that prioritization, but it doesn't stop NFC intents from being sent to your app. If you only want to use NFC while in the foreground then it sounds like you should be using reader mode instead of registering for intents. – Michael Dec 08 '21 at 14:19
  • @Michael what would I have to change in order to do so? – e5lite Dec 08 '21 at 14:24
  • Call `enableReaderMode` and `disableReaderMode` at appropriate times (e.g. onResume and onPause). One of the arguments to `enableReaderMode` is a callback that will be invoked when an NFC tag is discovered. – Michael Dec 08 '21 at 14:32
  • @Michael I already tried that earlier and implemented the line ```nfcAdapter.disableReaderMode(this)```, which did not change anything. It still executed the NFC-Method! – e5lite Dec 08 '21 at 14:38
  • Remove any NFC intent filters from your manifest. And get rid of the foreground dispatch code. – Michael Dec 08 '21 at 14:41

1 Answers1

0

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 enableForegroundDispatch gives you very little control and have no control over the NFC event sound the system generate, then you need to use the better API of enableReaderMode.

So in All Activities enableReaderMode in onResume and disableReaderMode in onPause. 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 onTagDiscovered is 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 onTagDiscovered method does your NFC processing. (And then should do the sound notification itself instead of the system)

You should also have a Broadcast receiver to enableReaderMode when 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 enableReaderMode in this answer https://stackoverflow.com/a/59397667/2373819

This 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)

Andrew
  • 8,198
  • 2
  • 15
  • 35
  • Unfortunately, I did not create an onTagDiscovered method yet. Where would I have to implement that into my existing Scan.activity? – e5lite Dec 09 '21 at 15:24
  • You would `implements NfcAdapter.ReaderCallback` then in All activities, but the code in `scan.activity` would would have your Tag reading/writing code in this solution, all the other activities would be an empty method. – Andrew Dec 09 '21 at 17:10