user1322930
user1322930

Reputation:

Unable to push NdefMessage with 4.x Android NFC API

I am implementing CreateNdefMessageCallback and OnNdefPushCompleteCallback. For some reason the callback methods are NEVER touched, no errors on the log either.

I do hear the sound from the API though, the phone that I am debugging on is a Nexus S running version 4.0.4.

Here is my activity:

public class TestActivity extends Activity implements CreateNdefMessageCallback, OnNdefPushCompleteCallback
{
  private static SoundHelper soundHelper;

  private PowerManager.WakeLock wakeLock;

  private NfcAdapter nfcAdapter;
  private PendingIntent pendingIntent = null;
  private IntentFilter[] intentFiltersArray;
  private String[][] techListsArray;

  private TextView onScreenLog;
  private List<String> uniqueTagsRead = new ArrayList<String>();

  /** handler stuff */
  private static final int MESSAGE_SENT = 1;
  private final Handler handler = new Handler()
  {
    @Override
    public void handleMessage(Message msg)
    {
      switch (msg.what)
      {
        case MESSAGE_SENT:
          if (soundHelper != null)
          {
            soundHelper.playSound(R.raw.smw_coin);
          }

          updateTagCount();
          break;
      }
    }
  };

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.test);

    soundHelper = new SoundHelper(this);

    onScreenLog = (TextView) findViewById(R.id.log);

    // nfc adapter
    nfcAdapter = NfcAdapter.getDefaultAdapter(this);
    if (nfcAdapter != null)
    {
      // callbacks
      nfcAdapter.setNdefPushMessageCallback(this, this);
      nfcAdapter.setOnNdefPushCompleteCallback(this, this);

      // other stuff
      nfcAdapter = NfcAdapter.getDefaultAdapter(this);
      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
      {
        ndef.addDataType("*/*");
      }
      catch (MalformedMimeTypeException e)
      {
        throw new RuntimeException("fail", e);
      }
      intentFiltersArray = new IntentFilter[] {ndef, };
      techListsArray = new String[][] {
          new String[] { IsoDep.class.getName() },
          new String[] { NfcA.class.getName() },
          new String[] { NfcB.class.getName() },
          new String[] { NfcF.class.getName() },
          new String[] { NfcV.class.getName() },
          new String[] { Ndef.class.getName() },
          new String[] { NdefFormatable.class.getName() },
          new String[] { MifareClassic.class.getName() },
          new String[] { MifareUltralight.class.getName() },
      };
    }
    else
    {
      onScreenLog.setText("NFC is not available on this device. :(");
    }
  }

  public void onPause()
  {
    super.onPause();

    // end wake lock
    wakeLock.release();

    nfcAdapter.disableForegroundDispatch(this);
  }

  public void onResume()
  {
    super.onResume();

    // start wake lock
    PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
    wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "DoNotDimScreen");
    wakeLock.acquire();

    nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);
  }

  private void updateTagCount()
  {
    String newCount = String.valueOf(uniqueTagsRead.size());
    String text = getString(R.string.format_count);
    text = getString(R.string.format_count).replace("0", newCount);

    onScreenLog.setText(text);
  }

  @Override
  public NdefMessage createNdefMessage(NfcEvent event)
  {
    String message = "This is NFC message";
    NdefRecord mimeRecord = createMimeRecord("application/param.android.sample.beam",
    message.getBytes());
    NdefRecord appRecord = NdefRecord.createApplicationRecord("param.android.sample.beam");
    NdefRecord[] ndefRecords = new NdefRecord[] {
    mimeRecord,
    appRecord
    };
    NdefMessage ndefMessage = new NdefMessage(ndefRecords);
    return ndefMessage;

    /*
    String mimeType = "text/plain"; // "text/plain";

    NdefRecord[] data = {createMimeRecord(mimeType, TEXT_TO_WRITE.getBytes())};
    // data[data.length - 1] = NdefRecord.createApplicationRecord(); // com.test.nfc.application.activities.

    return new NdefMessage(data);
    */
  }

  /**
   * Creates a custom MIME type encapsulated in an NDEF record
   *
   * @param mimeType
   */
  public NdefRecord createMimeRecord(String mimeType, byte[] payload)
  {
    byte[] mimeBytes = mimeType.getBytes(Charset.forName("US-ASCII"));
    NdefRecord mimeRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, mimeBytes, new byte[0], payload);

    return mimeRecord;
  }

  @Override
  public void onNdefPushComplete(NfcEvent event)
  {
    handler.obtainMessage(MESSAGE_SENT).sendToTarget();
  }
}

manifest:

    <uses-sdk android:minSdkVersion="14" />

    <supports-screens android:anyDensity="true" /> 

    <uses-permission android:name="android.permission.NFC"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <uses-feature android:name="android.hardware.nfc" />

    <application android:name="com.test.nfc.application.Application"
        android:icon="@drawable/icon_launcher_nfc_droid_hdpi"
        android:theme="@android:style/Theme.Light"
        android:label="@string/app_name">

        <activity
            android:label="@string/app_name"
            android:name=".application.activities.MainActivity"
            android:configChanges="orientation|keyboardHidden">
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:label="@string/test"
            android:name=".application.activities.TestActivity"
            android:configChanges="orientation|keyboardHidden"
            android:launchMode="singleTop">

            <intent-filter>
              <action android:name="android.nfc.action.TECH_DISCOVERED"/>
            </intent-filter>
            <meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_list" />

        </activity>

    </application>

</manifest>

techlist

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
        <tech>android.nfc.tech.NfcA</tech>
        <tech>android.nfc.tech.NfcB</tech>
        <tech>android.nfc.tech.NfcF</tech>
        <tech>android.nfc.tech.NfcV</tech>
        <tech>android.nfc.tech.Ndef</tech>
        <tech>android.nfc.tech.NdefFormatable</tech>
        <tech>android.nfc.tech.MifareClassic</tech>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
</resources>

Upvotes: 0

Views: 2558

Answers (2)

robertly
robertly

Reputation: 2132

It looks like you are getting the default NFC adapter twice?

nfcAdapter = NfcAdapter.getDefaultAdapter(this);

You do it once before your check for null on nfcAdapter, then in your if statement you do it again. This might have some weird effects. I'm not sure though. Also it looks like you are declaring intent filters at runtime. Do this in the manifest to debug if you still have problems. It's just easier to be certain something is filtering intents correctly that way.

See this sample code and the Android Beam sample in the SDK for more examples:

http://developer.android.com/guide/topics/nfc/nfc.html#p2p

Upvotes: 0

NFC guy
NFC guy

Reputation: 10228

From your question and example code it is not entirely clear to me whether you want to receive NDEF messages, send them or both.

When using NfcAdapter.enableForegroundDispatch(), your Activity will be notified about new NFC intents by a call to onNewIntent(), so you should override that method in your Activity to receive the intents.

NfcAdapter.CreateNdefMessageCallback and NfcAdapter.OnNdefPushCompleteCallback are used to send NDEF data via Android Beam to another NFC device. The user needs to tap the screen to activate sending the NDEF message, which will cause calls to createNdefMessage() and onNdefPushComplete().

One more remark: if you pass null for the filters and techLists parameters to NfcAdapter.enableForegroundDispatch() that will act as a wild card (so you don't need to declare a complete list of technologies, as you are doing now).

Upvotes: 2

Related Questions