Launch Specific App when NFC is discovered - android

I am using NFC in my app and it is working fine. However I want to make sure that only my app is launched and no other App is there to handle the intent. Following is the code for it in my Manifest file:
<activity android:name="com.mypackage.name.BeamActivity">
<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>
I have another sample app on my phone which is detecting NFC Intent and providing me Intent Chooser. Following is the code for it in Manifest file.
<activity android:name="com.package2.name.NFCStickyNotesActivity" android:label="Sticky Notes" >
<!-- Handle notes detected from outside our application -->
<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>
I would like my App to be the only app to handle the particular NFC Intent when my App push it across from another device.
I am not sure whether I have to do something specific in the manifest file or in the code. Any help is appreciated.

The reason why you get an intent chooser is that multiple activities are registered for the data type text/plain. This is a rather common case and you should therefore avoid using such generic data types for the NDEF record that should launch your activity. You have two options to overcome this problem:
Use an NFC Forum external type for your NDEF record (this is what ThomasRS already mentioned). With this method you create a custom record type that is meaningful to your application only. You can create such a record (to write it to your tag or to send over Beam) with something like this:
NdefRecord extRecord = NdefRecord.createExternal(
"yourdomain.com", // your domain name
"yourtype", // your type name
textBytes); // payload
You can then register your activity to launch upon this record like this:
<activity ...>
<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="/yourdomain.com:yourtype" />
</intent-filter>
</activity>
Use an Android Application Record (AAR). An AAR will make sure that the NDEF_DISCOVERED intent is delivered to an app with a specific package name only. You can create such a record (to write it to your tag or to send over Beam) with something like this:
NdefRecord appRecord = NdefRecord.createApplicationRecord(
"com.yourdomain.yourapp");
NdefRecord textRecord = NdefRecord.createTextRecord(
"en", // language code
"yourtext" // human-readable text);
NdefMessage msg = new NdefMessage(
textRecord,
appRecord); // use the AAR as the *last* record in your NDEF message

Use the External Type NDEF record with your own domain and give your app a corresponding intent-filter.

Related

NFC is launching app but intent is "action.MAIN" instead of "action.NDEF_DISCOVERED" [duplicate]

I am working on an NFC-application. To start my app, I am using a NDEF-tag with an AAR NDEF Record inside.
This works fine. But now I want to read the tag content with the app directly. How can I do this?
(It already works, when I remove the tag from the phone and touch it again, but I want to eliminate this step.)
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="nfctagscanner.mobileapps.lt.nfctagscanner" >
<uses-feature android:name="android.hardware.nfc" android:required="true"/>
<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=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".NFCScanActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="nfctagscanner.mobileapps.lt.nfctagscanner" android:host="ext"
android:pathPrefix="/android.com:pkg" />
</intent-filter>
</activity>
</application>
</manifest>
And I just want to get NDEF_DISCOVERED intent. But I always get action.MAIN/category.LAUNCHER Intent with my debugger.
Any help will be appreciated. I was doing my work based on this: Android / NFC: Get Tag in onCreate() without new Intent
If you want your app to be started by your tag and want to receive the parsed NDEF message, your tag should contain the following:
An NDEF record that you want to filter upon as the first record of the NDEF message (you can only create intent-filters for the very first NDEF record on an NFC tag). Given your own answer, this would be a Text record (or a MIME type record with MIME type text/plain) in your case. However, you might want to consider using an NFC Forum external type record with your custom application-specific type name instead (see here as this would allow your app to better distingush your tags from tags used with other apps. The Text record should only be used if it contains human-readable non-interpreted text.
If you want to make sure that only your app is started with the tag and that your app's Play Store page is automatically opened if the user does not have the app installed, then you should also use an Android Application Record (AAR). This record should be the last record in the NDEF message. (Note that an AAR will be honored regardless of the position in the NDEF message, but using it as the first record will cause the problem described in the post you linked in your question.)
Once, you have your tag containing a proper NDEF message ready, you have to create an appropriate intent filter for the activity that should receive the tag discovery event and the NDEF message that triggered it:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
Or if you used your custom NFC Forum external type:
<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="/yourdomain.com:yourtypename"/>
</intent-filter>
If your app does not have an NDEF_DISCOVERED intent filter that matches the first record of the NDEF message on your tag and your tag contains an Android Application Record, the first activity declared in your manifest that has an intent filter for android.intent.action.MAIN and with android.intent.category.LAUNCHER will be started. As that activity, therefore, does not declare to expect an NFC intent it will be passed the android.intent.action.MAIN (which does not contain the NDEF message/tag handle).
I don't know if this is correct answer but I found a fix:
<activity
android:name=".NFCScanActivity"
android:label="#string/app_name" >
<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>

Get NFC tag with NDEF Android Application Record (AAR)

I am working on an NFC-application. To start my app, I am using a NDEF-tag with an AAR NDEF Record inside.
This works fine. But now I want to read the tag content with the app directly. How can I do this?
(It already works, when I remove the tag from the phone and touch it again, but I want to eliminate this step.)
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="nfctagscanner.mobileapps.lt.nfctagscanner" >
<uses-feature android:name="android.hardware.nfc" android:required="true"/>
<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=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".NFCScanActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="nfctagscanner.mobileapps.lt.nfctagscanner" android:host="ext"
android:pathPrefix="/android.com:pkg" />
</intent-filter>
</activity>
</application>
</manifest>
And I just want to get NDEF_DISCOVERED intent. But I always get action.MAIN/category.LAUNCHER Intent with my debugger.
Any help will be appreciated. I was doing my work based on this: Android / NFC: Get Tag in onCreate() without new Intent
If you want your app to be started by your tag and want to receive the parsed NDEF message, your tag should contain the following:
An NDEF record that you want to filter upon as the first record of the NDEF message (you can only create intent-filters for the very first NDEF record on an NFC tag). Given your own answer, this would be a Text record (or a MIME type record with MIME type text/plain) in your case. However, you might want to consider using an NFC Forum external type record with your custom application-specific type name instead (see here as this would allow your app to better distingush your tags from tags used with other apps. The Text record should only be used if it contains human-readable non-interpreted text.
If you want to make sure that only your app is started with the tag and that your app's Play Store page is automatically opened if the user does not have the app installed, then you should also use an Android Application Record (AAR). This record should be the last record in the NDEF message. (Note that an AAR will be honored regardless of the position in the NDEF message, but using it as the first record will cause the problem described in the post you linked in your question.)
Once, you have your tag containing a proper NDEF message ready, you have to create an appropriate intent filter for the activity that should receive the tag discovery event and the NDEF message that triggered it:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
Or if you used your custom NFC Forum external type:
<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="/yourdomain.com:yourtypename"/>
</intent-filter>
If your app does not have an NDEF_DISCOVERED intent filter that matches the first record of the NDEF message on your tag and your tag contains an Android Application Record, the first activity declared in your manifest that has an intent filter for android.intent.action.MAIN and with android.intent.category.LAUNCHER will be started. As that activity, therefore, does not declare to expect an NFC intent it will be passed the android.intent.action.MAIN (which does not contain the NDEF message/tag handle).
I don't know if this is correct answer but I found a fix:
<activity
android:name=".NFCScanActivity"
android:label="#string/app_name" >
<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>

Open my app when an nfc tag of a custom format is discovered

I'm making an app for stock management. Each stock_item will have a tag, with a unique identifier, with a custom_format.
Like this:
stock_manager_pro:stock_item:12345
or
stock_manager_pro:user:john
I want to make my app open automatically when the device discovers a tag with whose content that starts with stock_manager_pro
With this code my apps its opened when the phone discovers a nfc tag with plain/text content, but I want to be able to filter customer texts.
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<data android:mimeType="text/plain" />
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
I m working on same problem...
I Found that you should make your own Custom MIME type.
and in manifest file You should simply remove
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
eg
<intent-filter >
<!-- <action android:name="android.nfc.action.NDEF_DISCOVERED" />-->
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/vnd.com.nfctagwriter" />
</intent-filter>
You can add your own mime type and it doesn't have to have the fully qualified "application" at the begining. It also understands "app/customname" .. this is useful for keeping the data size small.
For example stack overflow mime type could be "app/so" instead of "application/stackoverflow".
Although I guess it depends how likely you feel someone else would be using "app/so". In our case it doesn't matter because we are only using it internally.

How to add custom mime type?

What I want: To be able to send my custom file by mail and import it with my application from the preview button in GMail or when opening it in a file browser.
What I know: I've read a lot of custom mime type handlers, that android doesn't care about file extension etc., but how to create the mime type for my custom file?
The question: Do I need to be a content provider? I just want to import files (from backup) not provide anything. I've seen people having handlers for "application/abc" saying it's working fine, but how to add that connection for my file "myFile.abc" and the mime type?
Some direction how to register/map custom mime types would be appreciated! :)
As far as I can tell, mime types are pretty flexible (I created mine as application/whatever) and they're accepted immediately by Android, as far back as Dalvik version 2.1. To handle them properly, I added this intent-filter:
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="application/whatever" />
</intent-filter>
There is a caveat though. Even though I always set the type of the send Intent with intent.setType("application/whatever");, on some phones I've seen the actual data on arrival as application/octet (to see the value, I assigned the incoming Intent and inspected its value directly Intent currentIntent = getIntent();). The receiving Android device didn't know what to do with the incoming data and told me so. So I added
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="application/octet-stream" />
</intent-filter>
This approach could be troublesome of course, but the problem with Gmail at least is that it doesn't necessarily write the file with the name as it comes in, which renders any Path I choose to define useless. And at least with an incoming octet-stream you know it's not any app's specific data you're stealing away... Still, you should validate the data afterwards and not just assume it's valid for your app.
I have added custom mime type in android contacts list. After a long research i decided to share this with you guys, i have tested this on all Android cell phone including android 9.0.
here is my Github link
Untested, but something like this should work. Put it in your AndroidManifest.xml with the activity you want to open the file:
<activity name=".ActivityHere">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="mimeTypeHere" />
</intent-filter>
</activity>
<activity
android:name="MainActivity"
android:label="#string/app_name"
android:theme="#android:style/Theme.NoTitleBar.Fullscreen" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="{your mime}.com"
android:scheme="http" >
</data>
</intent-filter>
</activity>
<!--
android:scheme="http" will make android "think" thats this is a link
-->
Now, when you receiving a sms with the text "http://{your mime}.com" or clicking link on the web with this text, your activity (MainActivity) will run.
You also can add parameters:
text = "http://{your mime}.com/?number=111";
Then in onCreate() or onResume() methods you'll add:
Intent intentURI = getIntent();
Uri uri = null;
String receivedNum = "";
Log.d("TAG", "intent= "+intentURI);
if (Intent.ACTION_VIEW.equals(intentURI.getAction())) {
if (intentURI!=null){
uri = intentURI.getData();
Log.d("TAG", "uri= "+uri);
}
if (uri!=null)
receivedNum = uri.getQueryParameter("number");
}
Register a custom mime type using android.webkit.MimeTypeMap

Creating a default viewer / receiver for Android email attachments

I'm trying to create a default handler for .p7s/.p7b files in Android.
I figured the best start would be to create a BroadcastReceiver that will capture the intent from the Android email application (or K-9 if that's a need) for opening of certain attachments (filtered by mime type). Specifically I'm trying to handle s/mime email so looking for the "application/x-pkcs7-certificates" and "application/x-pkcs7-certificates" mime types.
Just as a basic test I've been trying something like this:
Manifest.xml
<receiver android:name=".IntentReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:mimeType="application/x-pki-signature"/>
</intent-filter>
</receiver>
Class file.
public class IntentReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("TEST", "Intent was caught");
//Do something here
}
}
I've tried using the following mimeType filters as well (testing signatures first):
application/pkcs7-signature
application/x-pkcs7-signature
application/keychain_access
I can't seem to get the IntentReceiver class to capture the intent no matter what filtering I use though. Am I going about this the wrong way?
FIXED: I tried to simply use a regular activity and add the mimeType filtering into that and BAM! it worked. Seems there must be a discrepancies with the BroadcastReceiver not supporting this type of VIEW intent.
New manifest:
<activity android:name=".PkixReceiver"
android:label="#string/app_name">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="application/x-pkcs7-signature" />
<data android:mimeType="application/pkcs7-signature" />
<data android:mimeType="application/keychain_access" />
</intent-filter>
</activity>
And now the activity gets fired when the "open" attachment is clicked, success!
I would include all of those MIME types (just use multiple <data> elements).
It may be that the MIME type is not being included in the email, though. I don't know how you are sending the emails, but try sending them to some account where you can examine the full source of the email message. If the MIME types are not included in the message, you are out of luck.
You might also test a link to one of these files in a Web page (where the Web server serves up the MIME type header), to confirm that your basic handling is OK.

Categories

Resources