1

I want to launch the app's particular activity by tapping the nfc tag. I can able to write only package name. but along with package name, i want to add one string and based on the string, i want to launch the activity.

  • Sounds like you are confused with Android Application Records and Manifest Intent Filters. Do you expect you application to already be installed when you tap the NFC tag? and already running another Activity. – Andrew Oct 13 '20 at 10:59
  • If app already installed, it should open the app's particular activity. Not installed it should ask to install that from playstore. – Priyasudhesh Oct 13 '20 at 11:28

1 Answers1

2

So there is a more standard way for static and dynamic mapping of tag data to activity and a better way for static mapping.

The better way for static mapping is your Tag needs to have an AAR Record on the Tag and then another NDEF Record with a custom mimetype with the String encoded in to the mimeType (content of the NDEF record does not matter). (see https://developer.android.com/reference/android/nfc/NdefRecord#createMime(java.lang.String,%20byte[]) on how to create mimeType NDEF records)
This is a better way because you can use the manifest Intent filters to guide Android OS to which activity to start.

e.g.
Tag A which is to start "Activity A" has an AAR record for you App and a NDEF record with mimeType "my-app/activity-a"
Tag B which is to start "Activity B" has an AAR record for you App and a NDEF record with mimeType "my-app/activity-b"

Your manifest would then look like :-

<activity
  android:name=".MainActivity">
  <intent-filter>
     <action android:name="android.intent.action.MAIN" />
     <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>
<activity
  android:name=".ActivityA">
  <intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:mimeType="my-app/activity-a" />
  </intent-filter>
</activity>
<activity
  android:name=".ActivityB">
  <intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:mimeType="my-app/activity-b" />
  </intent-filter>
</activity>

All Activities must handle Tag reading with enableForegroundDispatch (or the better enableReaderMode) and read the mimeType of the record and then StartActivity based on the custom mimeType of the Tag to start the right Activity. (You might be able to get the Android OS to do this for you with all your Activities having launch mode of "singleTask" but I'm not sure this will work correctly for you)

So then there are a number of scenarios to handle

  1. No App installed - AAR Record on the Tag will cause Android to open the Play Store to prompt the user to install your App. Once your App is installed the Tag needs to go out of range and enter range again to Trigger Android OS to read it again and Trigger scenario 2

  2. App already installed - The custom mimeType on the Tag will cause Android to start the matching activity.

  3. The App is already running - enableForegroundDispatch (or the better enableReaderMode) will read the Tag's custom mimeType and switch to the right Activity based on the mimeType read.

Note I've not personally tried using multiple custom mimeTypes, I've only needed a single custom mimeType.

Updated:

The standard way is your Tag needs to have an AAR Record on the Tag as well as an NDEF Text Record with your String in it or you could use a custom mimeType with createMime as this stops Android from asking which NFC App to launch if there are other NFC Apps that also handle NDEF Text Records (they are less likely to handle your custom mimeType)

Then a combination of AAR record on the Tag and Intent filters in the application manifest and Handling an initial Intent in Main Activity and enableForegroundDispatch (or the better enableReaderMode) with one Activity that reads the Text or custom mimeType NDEF message and then launches the correct Activity based on the text string directly or as using it to look up which Activity to launch.

Example using a custom mimeType with Dynamic lookup. Your manifest would then look like :-

<activity
  android:name=".MainActivity">
  <intent-filter>
     <action android:name="android.intent.action.MAIN" />
     <category android:name="android.intent.category.LAUNCHER" />
     <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
     <category android:name="android.intent.category.DEFAULT"/>
     <data android:mimeType="my-app/text" />
  </intent-filter>
</activity>

Main Activity using enableForegroundDispatch


....
private NfcAdapter adapter;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent mIntent = getIntent();

        // Check to see if the App was started because of the Intent filter in the manifest, if yes read the data from the Intent
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
          handleIntent(intent);
        }

        // Show other stuff here for Manual App start from Icon
        // Which could be just start the Default other Activity
    }

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    // Check intent is from NFC NDEF from foregroundDispatch
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
      handleIntent(intent);
    }
}

public void onResume() {
    super.onResume();
    pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
            getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
    IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
    try {
        // Only look for custom mimeType
        ndef.addDataType("my-app/text");
    }
    catch (MalformedMimeTypeException e) {
        throw new RuntimeException("fail", e);
    }
    intentFiltersArray = new IntentFilter[] {ndef, };

    adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, null);
}

@Override
public void onPause() {
    super.onPause();
    adapter.disableForegroundDispatch(this);

}

private void handleIntent(Intent intent) {

  Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
    if ((rawMessages != null) && (rawMessages.length > 0)) {
        ndefMessage = (NdefMessage)rawMessages[0];
    }
    NdefRecord firstRecord = ndefMessage.getRecords()[0];
    String payload = firstRecord.getPayload();
    // Dynamically Lookup Activity to start
    int activityId = lookup(payload);
    // Start the right Activity
    Intent intent;
    switch (activityId) {
      case 0:
         intent = new Intent(this, ActivityA.class);
      case 1:
         intent = new Intent(this, ActivityB.class);
     }
     startActivity(intent);

}

The onPause, onResume, onNewIntent and handleIntent should be in all activities, so that if a card is presented during these then the correct activity is started.

So then there are a number of scenarios to handle

  1. No App installed - AAR Record on the Tag will cause Android to open the Play Store to prompt the user to install your App. Once your App is installed the Tag needs to go out of range and enter range again to Trigger Android OS to read it again and Trigger scenario 2

  2. App already installed - The custom mimeType on the Tag will cause Android to start the Main activity with will read the payload and start the correct Activity.

  3. The App is already running - enableForegroundDispatch (or the better enableReaderMode) will read the Tag's custom mimeType and payload String and switch to the right Activity based on the mimeType read.

Andrew
  • 8,198
  • 2
  • 15
  • 35
  • Thank you. I can able to launch the particular activity now using mime type tag. Now my tag contains AAR and mime type data. Because i want to install from play store also. – Priyasudhesh Oct 14 '20 at 12:17
  • Using MIME type, if i launched the app, i want to read the mime data,which i stored in the tag, by the time i want to perform task based on the data. Example: my tag contains some id. Based on that id, I want to check with the database and show the activity. – Priyasudhesh Oct 14 '20 at 12:21
  • The custom mimeType is good for a static map of String->Activity if you want to dynamically choose the Activity based on a database lookup then the custom mimeType method is not so helpful, you will need to use a standard method with intermediate Activity to do the choosing. I'll update the answer based on this new info to detail the standard method as well, when I get a chance. – Andrew Oct 14 '20 at 12:49
  • @Andrew , Thanks for your response, it helped me to open the app when NFC is detected. Now only problem is: the action is not `android.nfc.action.NDEF_DISCOVERED` it's `android.intent.action.MAIN` as if the user tapped app icon to open it. any thoughts? Thanks! – Sam Feb 01 '23 at 17:05