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>
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With