Skip to content

Commit

Permalink
Merge pull request revtel#133 from Brewskey/master
Browse files Browse the repository at this point in the history
Added support for `NfcAdapter.enableReaderMode`
  • Loading branch information
whitedogg13 authored Feb 12, 2019
2 parents 476331a + 69f420c commit c78d1b0
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 47 deletions.
58 changes: 47 additions & 11 deletions NfcManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ import Ndef from './ndef-lib'
const NativeNfcManager = NativeModules.NfcManager;
const NfcManagerEmitter = new NativeEventEmitter(NativeNfcManager);

const DEFAULT_REGISTER_TAG_EVENT_OPTIONS = {
invalidateAfterFirstRead: false,
isReaderModeEnabled: false,
readerModeFlags: 0,
};

const Events = {
DiscoverTag: 'NfcManagerDiscoverTag',
SessionClosed: 'NfcManagerSessionClosed',
Expand All @@ -28,6 +34,16 @@ const NfcTech = {
MifareUltralight: 'MifareUltralight',
}

const NfcAdapter = {
FLAG_READER_NFC_A: 0x1,
FLAG_READER_NFC_B: 0x2,
FLAG_READER_NFC_F: 0x4,
FLAG_READER_NFC_V: 0x8,
FLAG_READER_NFC_BARCODE: 0x10,
FLAG_READER_SKIP_NDEF_CHECK: 0x80,
FLAG_READER_NO_PLATFORM_SOUNDS: 0x100,
};

const LOG = 'NfcManagerJs';

class NfcManager {
Expand Down Expand Up @@ -122,19 +138,38 @@ class NfcManager {
})
}

registerTagEvent(listener, alertMessage = '', invalidateAfterFirstRead = false) {
registerTagEvent(listener, alertMessage = '', options = {}) {
// Support legacy `invalidateAfterFirstRead` boolean
if (options === true || options === false) {
options = {
invalidateAfterFirstRead: options,
};
}

options = {
...DEFAULT_REGISTER_TAG_EVENT_OPTIONS,
...options,
};

if (!this._subscription) {
return new Promise((resolve, reject) => {
NativeNfcManager.registerTagEvent(alertMessage, invalidateAfterFirstRead, (err, result) => {
if (err) {
reject(err);
} else {
this._clientTagDiscoveryListener = listener;
this._subscription = NfcManagerEmitter.addListener(Events.DiscoverTag, this._handleDiscoverTag);
resolve(result);
}
})
})
NativeNfcManager.registerTagEvent(
alertMessage,
options,
(err, result) => {
if (err) {
reject(err);
} else {
this._clientTagDiscoveryListener = listener;
this._subscription = NfcManagerEmitter.addListener(
Events.DiscoverTag,
this._handleDiscoverTag,
);
resolve(result);
}
},
);
});
}
return Promise.resolve();
}
Expand Down Expand Up @@ -574,5 +609,6 @@ export {
ByteParser,
NdefParser,
NfcTech,
NfcAdapter,
Ndef,
}
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,23 @@ Start to listen to *ANY* NFC tags.
__Arguments__
- `listener` - `function` - the callback when discovering NFC tags
- `alertMessage` - `string` - (iOS) the message to display on iOS when the NFCScanning pops up
- `invalidateAfterFirstRead` - `boolean` - (iOS) when set to true this will not have you prompt to click done after NFC Scan.
- `options` - `object` - Object containing (iOS)invalidateAfterFirstRead, (Android)isReaderModeEnabled, (Android)readerModeFlags. Use `NfcAdapter` flags. **Reader mode can only be used in Android 19 or later**.

**Examples**

__Examples__
```js
NfcManager.registerTagEvent(tag => {
NfcManager.registerTagEvent(
tag => {
console.log('Tag Discovered', tag);
}, 'Hold your device over the tag', true)
},
'Hold your device over the tag',
{
invalidateAfterFirstRead: true,
isReaderModeEnabled: true,
readerModeFlags:
NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
},
);
```

### unregisterTagEvent()
Expand Down
67 changes: 46 additions & 21 deletions android/src/main/java/community/revteltech/nfc/NfcManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,19 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Base64;
import android.util.Log;
import android.provider.Settings;
import com.facebook.react.bridge.*;
import com.facebook.react.modules.core.RCTNativeAppEventEmitter;

import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter.MalformedMimeTypeException;
import android.net.Uri;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.NfcEvent;
import android.nfc.Tag;
import android.nfc.TagLostException;
import android.nfc.tech.TagTechnology;
Expand All @@ -41,16 +35,7 @@
import org.json.JSONObject;
import org.json.JSONException;

import java.io.File;
import java.util.*;
import java.nio.charset.Charset;

import static android.app.Activity.RESULT_OK;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;

import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;

class NfcManager extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener {
private static final String LOG_TAG = "ReactNativeNfcManager";
Expand All @@ -63,6 +48,10 @@ class NfcManager extends ReactContextBaseJavaModule implements ActivityEventList
private WriteNdefRequest writeNdefRequest = null;
private TagTechnologyRequest techRequest = null;

// Use NFC reader mode instead of listening to a dispatch
private Boolean isReaderModeEnabled = false;
private int readerModeFlags = 0;

class WriteNdefRequest {
NdefMessage message;
Callback callback;
Expand Down Expand Up @@ -753,8 +742,11 @@ public void getLaunchTagEvent(Callback callback) {
}

@ReactMethod
private void registerTagEvent(String alertMessage, Boolean invalidateAfterFirstRead, Callback callback) {
Log.d(LOG_TAG, "registerTag");
private void registerTagEvent(String alertMessage, ReadableMap options, Callback callback) {
this.isReaderModeEnabled = options.getBoolean("isReaderModeEnabled");
this.readerModeFlags = options.getInt("readerModeFlags");

Log.d(LOG_TAG, "registerTag");
isForegroundEnabled = true;

// capture all mime-based dispatch NDEF
Expand Down Expand Up @@ -818,10 +810,43 @@ private void enableDisableForegroundDispatch(boolean enable) {

if (nfcAdapter != null && currentActivity != null && !currentActivity.isFinishing()) {
try {
if (enable) {
nfcAdapter.enableForegroundDispatch(currentActivity, getPendingIntent(), getIntentFilters(), getTechLists());
if (isReaderModeEnabled) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
throw new RuntimeException("minSdkVersion must be Honeycomb (19) or later.");
}

if (enable) {
nfcAdapter.enableReaderMode(currentActivity, new NfcAdapter.ReaderCallback() {
@Override
public void onTagDiscovered(Tag tag) {
WritableMap nfcTag = null;
for (String tagTech : tag.getTechList()) {
Log.d(LOG_TAG, tagTech);
if (tagTech.equals(NdefFormatable.class.getName())) {
// fireNdefFormatableEvent(tag);
nfcTag = tag2React(tag);
} else if (tagTech.equals(Ndef.class.getName())) { //
Ndef ndef = Ndef.get(tag);
nfcTag = ndef2React(ndef, new NdefMessage[] { ndef.getCachedNdefMessage() });
} else {
nfcTag = tag2React(tag);
}
}

if (nfcTag != null) {
sendEvent("NfcManagerDiscoverTag", nfcTag);
}
}
}, readerModeFlags, null);
} else {
nfcAdapter.disableReaderMode(currentActivity);
}
} else {
nfcAdapter.disableForegroundDispatch(currentActivity);
if (enable) {
nfcAdapter.enableForegroundDispatch(currentActivity, getPendingIntent(), getIntentFilters(), getTechLists());
} else {
nfcAdapter.disableForegroundDispatch(currentActivity);
}
}
} catch (IllegalStateException | NullPointerException e) {
Log.w(LOG_TAG, "Illegal State Exception starting NFC. Assuming application is terminating.");
Expand Down
26 changes: 16 additions & 10 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ declare module 'react-native-nfc-manager' {
id: number[];
}

interface RegisterTagEventOpts {
invalidateAfterFirstRead: boolean;
isReaderModeEnabled: boolean;
readerModeFlags: number;
}

interface NdefWriteOpts {
format?: boolean
formatReadOnly?: boolean
Expand All @@ -52,16 +58,16 @@ declare module 'react-native-nfc-manager' {
/** [ANDROID ONLY] */
getLaunchTagEvent(): Promise<TagEvent | null>;

/**
* Start to listen to ANY NFC Tags
* @param listener The callback function when a tag is found.
* @param alertMessage [iOS ONLY] Message displayed when NFC Scanning popup appears.
* @param invalidateAfterFirstRead [iOS ONLY] When set to true, will auto-dismiss the NFC Scanning popup after scanning.
*/
registerTagEvent(
listener: (tag: TagEvent) => void,
alertMessage?: string,
invalidateAfterFirstRead?: boolean
/**
* Start to listen to ANY NFC Tags
* @param listener The callback function when a tag is found.
* @param alertMessage [iOS ONLY] Message displayed when NFC Scanning popup appears.
* @param invalidateAfterFirstRead [iOS ONLY] When set to true, will auto-dismiss the NFC Scanning popup after scanning.
*/
registerTagEvent(
listener: (tag: TagEvent) => void,
alertMessage?: string,
options?: RegisterTagEventOpts,
): Promise<any>;

unregisterTagEvent(): Promise<any>;
Expand Down
3 changes: 2 additions & 1 deletion ios/NfcManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,11 @@ + (BOOL)requiresMainQueueSetup
}
}

RCT_EXPORT_METHOD(registerTagEvent: (NSString *)alertMessage invalidateAfterFirstRead:(BOOL)invalidateAfterFirstRead callback:(nonnull RCTResponseSenderBlock)callback)
RCT_EXPORT_METHOD(registerTagEvent: (NSString *)alertMessage options:(NSDictionary)options callback:(nonnull RCTResponseSenderBlock)callback)
{
if (@available(iOS 11.0, *)) {
if (session == nil) {
BOOL invalidateAfterFirstRead = [[options objectForKey:@"invalidateAfterFirstRead"] boolValue];
session = [[NFCNDEFReaderSession alloc] initWithDelegate:self queue:dispatch_get_main_queue() invalidateAfterFirstRead:invalidateAfterFirstRead];
session.alertMessage = alertMessage;
[session beginSession];
Expand Down

0 comments on commit c78d1b0

Please sign in to comment.