Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I send a string through NFC while Screen-Pinning?

I am trying to send a String through NFC while my app is using screen pinning. It does not work: The transfer does not happen; but if I disable the screen pinning the transfer of the String works.

I can disable screen pinning for a bit and then perform the transfer, but that is a security risk.

How can I do this?


Here is all the code if you want to try. All you need to do is enable screen pinning manually through your app settings (so it is less code and still produces the same result). I tested this using two Nexus 7 both running Android 5.0.

You don't have to read all this code, this question can probably be solved if you know something I can add to my manifest that would allow NFC while screen pinning.


AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androidnfc"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="16"
        android:targetSdkVersion="19" />
    <uses-permission android:name="android.permission.NFC"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.androidnfc.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>
</manifest>

MainActivity.java

public class MainActivity extends Activity implements CreateNdefMessageCallback, OnNdefPushCompleteCallback
{ 
   TextView textInfo;
   EditText textOut;  
   NfcAdapter nfcAdapter;

   @Override
   protected void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      textInfo = (TextView)findViewById(R.id.info);
      textOut = (EditText)findViewById(R.id.textout);

      nfcAdapter = NfcAdapter.getDefaultAdapter(this);
      nfcAdapter.setNdefPushMessageCallback(this, this);
      nfcAdapter.setOnNdefPushCompleteCallback(this, this);
   }

   @Override
   protected void onResume() 
   {
      super.onResume();
      Intent intent = getIntent();
      String action = intent.getAction();

      if(action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED))
      {
         Parcelable[] parcelables = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
         NdefMessage inNdefMessage = (NdefMessage)parcelables[0];
         NdefRecord[] inNdefRecords = inNdefMessage.getRecords();
         NdefRecord NdefRecord_0 = inNdefRecords[0];
         String inMsg = new String(NdefRecord_0.getPayload());
         textInfo.setText(inMsg);
      }
   }

   @Override
   protected void onNewIntent(Intent intent) {
     setIntent(intent);
   }

   @Override
   public void onNdefPushComplete(NfcEvent event) {
      final String eventString = "onNdefPushComplete\n" + event.toString();
      runOnUiThread(new Runnable() {

        @Override
        public void run() {
          Toast.makeText(getApplicationContext(), eventString, Toast.LENGTH_LONG).show();
        }
       });
   }

   @Override
   public NdefMessage createNdefMessage(NfcEvent event) {
      String stringOut = textOut.getText().toString();
      byte[] bytesOut = stringOut.getBytes();

      NdefRecord ndefRecordOut = new NdefRecord(
         NdefRecord.TNF_MIME_MEDIA, 
         "text/plain".getBytes(),
                new byte[] {}, 
                bytesOut);

      NdefMessage ndefMessageout = new NdefMessage(ndefRecordOut);
      return ndefMessageout;
   }
}

layout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.example.androidnfc.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textStyle="bold" />

    <EditText
        android:id="@+id/textout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>
like image 476
J_Strauton Avatar asked Feb 09 '15 21:02

J_Strauton


1 Answers

I'm not sure if this actually answers your question, but I'd like to summarize my findings:

When trying your example on Android 5.0.1 (LRX22C on Nexus 4), the receiving side automatically unpins the screen upon receiving the NDEF message and (re-)starts the activity. So it seems that the intent filter that is registered in the manifest gets priority over (manual?) screen pinning.

I'm aware that this does not quite match the experiences described in the question. I'm wondering if this is due to the different Android version (5.0 vs. 5.0.1) or due to the use of manual screen pinning instead of programatic screen pinning...

In my test setup, I was able to solve the problem (i.e. prevent the activity from getting automatically unpinned) by using the foreground dispatch system to register the activity to receive its NDEF message:

In your onResume() method create a pending intent like this and enable foreground dispatch:

PendingIntent pi = this.createPendingResult(0x00A, new Intent(), 0);
nfcAdapter.enableForegroundDispatch(this, pi, null, null);

You will then receive intents notifying you about discovered tags through the activity's onActivityResult() method:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
        case 0x00A:
            onNewIntent(data);
        break;
    }
}

Moreover, you have to disable the foreground dispatch in your onPause() method:

nfcAdapter.disableForegroundDispatch(this);
like image 58
Michael Roland Avatar answered Nov 11 '22 06:11

Michael Roland