Search code examples
javaandroidandroid-intentnfcandroid-mvp

Android NFC Reader in MVP - onNewIntent not firing


I have a working NFC reader/writer code. Using the same code, I added the reader function in another app which is following MVP architecture.

The activity is named NFCReaderActivity. A separate NFC class is created (NFCReader), which implements Sensor interface.

The app is supposed to work both in the foreground and launch showing the NFC tag info. The launch part is working fine and app launches and reads the tag and shows its content.

However, in the foreground, on scanning, it does nothing. I only hear the scan beep but no onNewIntent is firing.

Below are the log entries captured for foreground and launch actions. There is a difference in the class names:

When not launching
I/ActivityManager: START u0 {act=android.nfc.action.NDEF_DISCOVERED typ=application/com.abc.vi flg=0x14008000 cmp=com.abc.vi/.ui.reader.NFCReader (has extras)} from uid 10038 on display 0

When launching
I/ActivityManager: START u0 {act=android.nfc.action.NDEF_DISCOVERED typ=application/com.abc.vi cmp=com.abc.vi/.ui.reader.NFCReaderActivity (has extras)} from uid 1027 on display 0

Activity

onCreate

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "__onCreate__  " );

        setContentView(R.layout.activity_nfc_reader);

        VI.setNFCReaderActivityContext(this); //VI is the Application class
        ButterKnife.bind(this);

        presenter = new ReaderPresenter(this);
    }

onNewIntent

@Override
    public void onNewIntent(Intent intent) {
        Log.i(TAG, "__onNewIntent__  " );
        // onResume gets called after this to handle the intent
//        setIntent(intent);
        presenter.onNewIntent(intent);
    }

onResume, onPause

@Override
    protected void onResume() {
        super.onResume();
        Log.i(TAG, "__onResume__  " );
        presenter.onResume();
    }

@Override
    protected void onPause() {
        super.onPause();
        Log.i(TAG, "__onPause__  " );
        presenter.onPause();
    }

Presenter

ReaderPresenter(ReaderContract.View view) {
        this.view = view;
        initSensor();
    }

    @Override
    public void initSensor() {
        nfcReader = new NFCReader(VI.getNFCReaderActivityContext(), this); //VI is the Application class
    }

    @Override
    public void onNewIntent(Intent intent) {
        nfcReader.resolveIntent(intent);
    }


    @Override
    public void onResume() {
        nfcReader.onResume();
    }

    @Override
    public void onPause() {
        nfcReader.onPause();
    }

    @Override
    public void onDestroy() {
        speech.onDestroy();
    }

NFCReader

public class NFCReader implements Sensors {

    private static final String TAG = NFCReader.class.getSimpleName();

    private NfcAdapter nfcAdapter;
    private PendingIntent nfcPendingIntent;
    private NFCReaderActivity activity;
    private ReaderPresenter presenter;


    NFCReader(NFCReaderActivity nfcReaderActivity, ReaderPresenter readerPresenter) {
        this.activity = nfcReaderActivity;
        this.presenter = readerPresenter;
        init();
    }

    @Override
    public void init() {
        //Initialize NFC adapter
        nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
        nfcPendingIntent = PendingIntent.getActivity(activity, 0, new Intent(activity,
                getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP), 0);
    }

    public void onResume() {
        if (nfcAdapter != null) {

            nfcAdapter.enableForegroundDispatch(activity, nfcPendingIntent, null, null);
            // if NFC not enabled
            if (!nfcAdapter.isEnabled()) {
                new AlertDialog.Builder(activity)
                        .setPositiveButton(activity.getString(R.string.update_setting_btn),
                                (dialog, which) -> {
                                    Intent setNfc = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
                                    activity.startActivity(setNfc);
                                })
                        .setOnCancelListener(
                                dialog -> activity.finish()
                        )
                        .create().show();
            }
            resolveIntent(activity.getIntent());
        } else {
            Toast.makeText(VI.getAppContext(),
                    activity.getString(R.string.error_no_nfc_found), Toast.LENGTH_LONG).show();
        }
    }

    public void onPause() {
        if (nfcAdapter != null) {
            nfcAdapter.disableForegroundDispatch(activity);
        }
    }

    public void resolveIntent(Intent intent){
        Log.i(TAG, "__resolveIntent__");
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            NdefMessage[] messages = null;
            Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            if (rawMsgs != null) {
                messages = new NdefMessage[rawMsgs.length];
                for (int i = 0; i < rawMsgs.length; i++) {
                    messages[i] = (NdefMessage) rawMsgs[i];
                }
            }
            if ((messages != null ? messages[0] : null) != null) {
                StringBuilder result = new StringBuilder();
                byte[] payload = messages[0].getRecords()[0].getPayload();
                for (byte aPayload : payload) { 
                    result.append((char) aPayload);
                }
                Log.i(TAG,"Decoded --> "+result.toString());
                presenter.getData(result.toString());
            }
        }
    }
}

Manifest

 <activity android:name=".ui.reader.NFCReaderActivity">
            <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="@string/mime_type" />
            </intent-filter>
        </activity>

UPDATE
I moved all the code from NFCReader class to NFCReaderActivity and both foreground and launch modes are working. The issue is with MVP architecture. How to convert it back to MVP?


Solution

  • You seem to register the pending intent for the wrong (actually an invalid) component (not your activity class). The reason is that when you create the PendingIntent that you assign to nfcPendingIntent, you use getClass() to obtain the class of the NFCReader instance. Instead you would need to use activity.getClass() to obtain the class of your activity component.