1

I'm trying to implement a nfc-p2p comunication Android app.

I'd want to exchange strings between two devices at the same time.

Starting from this post #25963691 I've found that the easy way is to use the method android.nfc.NfcAdapter.invokeBeam() to call again the Android Beam after the first broadcast, to let the receiving user to just tap the display and starting the second broadcast (without move and place near again the devices).

I've tried to apply this to an example app from online tutorial.

My app at the moment does not work : after the first string transfer the Android Beam does not start again, even if in debug mode I can check that the method invokeBeam is called.

Can some one help me?

This are my manifest permissions:

<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true"/>

And this is my Activity:

public class MainActivity extends Activity implements NfcAdapter.OnNdefPushCompleteCallback, NfcAdapter.CreateNdefMessageCallback {

//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;

private  Boolean checkSent;
private  Boolean checkReceived;

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

@Override
public void onNdefPushComplete(NfcEvent event) {
    messagesToSendArray.clear();
    //This is called when the system detects that our NdefMessage was
    //Successfully sent

    checkSent = true;
}

public NdefRecord[] createRecords() {
    NdefRecord[] records = new NdefRecord[messagesToSendArray.size() + 1];
    //To Create Messages Manually if API is less than
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        for (int i = 0; i < messagesToSendArray.size(); i++){
            byte[] payload = messagesToSendArray.get(i).
                    getBytes(Charset.forName("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;
        }
    }
    //Api is high enough that we can use createMime, which is preferred.
    else {
        for (int i = 0; i < messagesToSendArray.size(); i++){
            byte[] payload = messagesToSendArray.get(i).
                    getBytes(Charset.forName("UTF-8"));

            NdefRecord record = NdefRecord.createMime("text/plain",payload);
            records[i] = record;
        }
    }
    records[messagesToSendArray.size()] = NdefRecord.createApplicationRecord(getPackageName());
    return records;
}


@Override
public NdefMessage createNdefMessage(NfcEvent event) {
    //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);
}

private void handleNfcIntent(Intent NfcIntent) {
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(NfcIntent.getAction())) {
        Parcelable[] receivedArray =
                NfcIntent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);

        if(receivedArray != null) {
            messagesReceivedArray.clear();
            NdefMessage receivedMessage = (NdefMessage) receivedArray[0];
            NdefRecord[] attachedRecords = receivedMessage.getRecords();

            for (NdefRecord record:attachedRecords) {
                String string = new String(record.getPayload());
                //Make sure we don't pass along our AAR (Android Applicatoin Record)
                if (string.equals(getPackageName())) { continue; }
                messagesReceivedArray.add(string);
            }
            Toast.makeText(this, "Received " + messagesReceivedArray.size() +
                    " Messages", Toast.LENGTH_LONG).show();
            updateTextViews();
        }
        else {
            Toast.makeText(this, "Received Blank Parcel", Toast.LENGTH_LONG).show();
        }
    }
}


@Override
public void onNewIntent(Intent intent) {
        handleNfcIntent(intent);
}

@Override
public void onResume() {
    super.onResume();
    updateTextViews();

    if(checkReceived){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mNfcAdapter.invokeBeam(MainActivity.this);
            //Toast.makeText(this, "invocato", Toast.LENGTH_SHORT).show();
            Log.e("DEBUG ---", "invocato");
        }
    }
    handleNfcIntent(getIntent());

}

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");
        }
        checkReceived = true;
    }
}

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

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    checkSent = false;
    checkReceived = false;

    //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);
    }
    else {
        Toast.makeText(this, "NFC not available on this device",
                Toast.LENGTH_SHORT).show();
    }


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

    if (getIntent().getAction().equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) {
        handleNfcIntent(getIntent());
    }
}

}

Anchor
  • 133
  • 1
  • 1
  • 7
  • Hi guys, I have made a little progress synchronizing the call to the right activity lifecycle event with a simply boolean flag (I don't usually like this tricks). My issue now is that I succeed to call "invokeBeam", but nothing appear on device display. I check the call event with a log and a toast message immediately after the invokeBeam call, I can see the log message in Logcat, I see the toast message on the display, but no Android Beam interface. What I'm wronging? – Anchor Jul 27 '17 at 13:10
  • @Override public void onResume() { super.onResume(); resumed = true; handleNfcIntent(getIntent()); callInvokeBeam(); } – Anchor Jul 27 '17 at 13:14
  • private void callInvokeBeam(){ if(resumed){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mNfcAdapter.invokeBeam(MainActivity.this); Toast.makeText(this, "Invoked", Toast.LENGTH_LONG).show(); Log.e("DEBUG ---", "Invoked"); } } } – Anchor Jul 27 '17 at 13:15
  • @Override public void onPause(){ super.onPause(); resumed = false; } – Anchor Jul 27 '17 at 13:15

0 Answers0