Is it possible to create a cross platform NDEF Message to start an application on Android and Windows Phone passing additional data to the app?
What I am trying to do:
I have an application installed on Android and Windows Phone. It should be possible to start the applications with an NFC tag and I need to pass additional data from the tag (any string) to my applications.
To start the applications with the NFC tags, I created a Windows Phone LaunchApp Record and an Android Application Record (AAR) and store them on the tag.
Windows Phone needs the LaunchApp Record to be the first NDEF Record in the NDEF message. So the sequence of the NDEF message is:
- Windows Phone LaunchApp Record
- Android Application Record
To pass additional data, I can put some arguments in the Windows Phone LaunchApp Record, so that works fine. But it is not possible to put some additional data to the Android Application Record.
I tried to add a third NDEF Record to the message which contains the extra data for Android. I created an external record and I added the filter to my manifest.
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="vnd.android.nfc"
android:host="ext"
android:pathPrefix="/myapp.com:customtype"/>
</intent-filter>
The problem is the sequence of the the NDEF message:
The Windows Phone record must be the first record in the message, but the additional record must also be the first record to receive the intent on Android.
NDEF Message:
- External and LaunchApp Record
- Android Application Record
If I have following record sequence I receive the extra data on Android and the application starts but then I can't open the application on Windows Phone because the record is the second one.
- External Record
- Windows Phone LaunchApp
- Android Application Record
Is there any solution to this problem? Am I missing something?
The problem here is the following:
- Windows requires the LaunchApp record to be the first record of the NDEF message on the tag.
- Android will only pass the discovered NDEF message/NFC tag object to the app if the app has an intent filter for the first record of the NDEF message.
- The LaunchApp record is not a valid NDEF record (as its type name format indicates an absolute URI record but its type ("windows.com/LaunchApp") is not a valid absolute URI).
As a consequence, you cannot easily match that record (the LaunchApp record) with an intent filter on Android (URI intent filters match in the order scheme -> host -> path, but as there is no scheme, you cannot match for host/path).
The trick is to use an intent filter that matches any URI with an empty scheme:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="" />
</intent-filter>
Now if your NDEF message is
LaunchApp record | [optional other records] | Android Application record
your Android app will be invoked and the NDEF message/tag object will be passed in an NDEF_DISCOVERED
intent to the activity with the above intent filter.
Unfortunately, that intent filter will also be triggered for any other tag that contains a relative URI/any URI without a scheme and that does not contain an AAR for another app.
UPDATE
Code for creating my test LaunchApp record tag:
final Ndef ndefTag = Ndef.get(tag);
if (ndefTag != null) {
try {
ndefTag.connect();
ndefTag.writeNdefMessage(new NdefMessage(new NdefRecord[] {
new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI,
"windows.com/LaunchApp".getBytes("US-ASCII"),
null,
"la:uz".getBytes("US-ASCII")),
NdefRecord.createApplicationRecord("at.mroland.launchrecordtest")
}));
} catch (Exception e) {
} finally {
try {
ndefTag.close();
} catch (Exception ee) {
}
}
}
Manifest for the receiving app:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="at.mroland.launchrecordtest"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<application android:label="@string/app_name"
android:icon="@drawable/ic_launcher">
<activity android:name=".LaunchRecordTest"
android:label="LaunchRecordTest">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="" />
</intent-filter>
</activity>
</application>
</manifest>
In onCreate()
, I used getIntent()
to retrieve the intent.