Skip to content
This repository has been archived by the owner on May 15, 2024. It is now read-only.

GH-120: Add NFC API #131

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions Caboodle/Nfc/Nfc.android.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using Android;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Nfc;
using Android.OS;

namespace Microsoft.Caboodle
{
public static partial class Nfc
{
static NfcAdapter nfcAdapter;

internal static bool IsSupported
=> Platform.HasSystemFeature(PackageManager.FeatureNfc);

static void StartTagListeners()
{
Ensure();

var activity = Platform.CurrentActivity;

var intent = new Intent(activity, activity.GetType())
.AddFlags(ActivityFlags.SingleTop);
var pendingIntent = PendingIntent.GetActivity(activity, 0, intent, 0);

var tagDetected = new IntentFilter(NfcAdapter.ActionTagDiscovered);
var filters = new[] { tagDetected };

nfcAdapter.EnableForegroundDispatch(activity, pendingIntent, filters, null);
}

static void StopTagListeners()
{
}

static void StartNdefMessageListeners()
{
Ensure();
}

static void StopNdefMessageListeners()
{
}

static void PlatformPublishMessage(string messageType, byte[] message)
{
Ensure();
}

static void PlatformStopPublishing()
{
}

static void Ensure()
{
// Permissions.RequireAsync(PermissionType.Flashlight);

nfcAdapter = NfcAdapter.GetDefaultAdapter(Platform.CurrentContext);

if (nfcAdapter?.IsEnabled != true)
throw new NotSupportedException();
}
}
}
183 changes: 183 additions & 0 deletions Caboodle/Nfc/Nfc.ios.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
using System;
using System.IO;
using CoreFoundation;
using CoreNFC;
using Foundation;

namespace Microsoft.Caboodle
{
public static partial class Nfc
{
static NfcReader nfcReader;

internal static bool IsSupported
=> NFCNdefReaderSession.ReadingAvailable;

static void EnsureReader()
{
}

static void PlatformPublishMessage(string messageType, byte[] message)
{
throw new NotSupportedException();
}

static void PlatformStopPublishing()
{
throw new NotSupportedException();
}

static void StartTagListeners()
{
EnsureReader();
nfcReader.Start();
}

static void StopTagListeners()
{
nfcReader.Stop();
}

static void StartNdefMessageListeners()
{
EnsureReader();
nfcReader.Start();
}

static void StopNdefMessageListeners()
{
nfcReader.Stop();
}

class NfcReader : NSObject, INFCNdefReaderSessionDelegate
{
NFCNdefReaderSession session;

public void Start()
{
if (session != null)
{
session = new NFCNdefReaderSession(this, DispatchQueue.CurrentQueue, true);
session.BeginSession();
}
}

public void Stop()
{
session?.InvalidateSession();
session = null;
}

public void DidDetect(NFCNdefReaderSession session, NFCNdefMessage[] messages)
{
TagArrived?.Invoke(NfcTagEventArgs.Empty);

foreach (var message in messages)
{
NdefMessageReceived?.Invoke(new NdefMessageReceivedEventArgs(message.ToByteArray()));
}

TagDeparted?.Invoke(NfcTagEventArgs.Empty);

// session has been invalidated after first read
session = null;
}

public void DidInvalidate(NFCNdefReaderSession session, NSError error)
{
// TODO: probably throw if error != null
}
}
}

static class NFCNdefMessageExtensions
{
public static byte[] ToByteArray(this NFCNdefMessage message)
{
var records = message?.Records;

// Empty message: single empty record
if (records == null || records.Length == 0)
{
records = new NFCNdefPayload[] { null };
}

var m = new MemoryStream();
for (var i = 0; i < records.Length; i++)
{
var record = records[i];
var typeNameFormat = record?.TypeNameFormat ?? NFCTypeNameFormat.Empty;
var payload = record?.Payload;
var id = record?.Identifier;
var type = record?.Type;

var flags = (byte)typeNameFormat;

// Message begin / end flags. If there is only one record in the message, both flags are set.
if (i == 0)
flags |= 0x80; // MB (message begin = first record in the message)
if (i == records.Length - 1)
flags |= 0x40; // ME (message end = last record in the message)

// cf (chunked records) not supported yet

// SR (Short Record)?
if (payload == null || payload.Length < 255)
flags |= 0x10;

// ID present?
if (id != null && id.Length > 0)
flags |= 0x08;

m.WriteByte(flags);

// Type length
if (type != null)
m.WriteByte((byte)type.Length);
else
m.WriteByte(0);

// Payload length 1 byte (SR) or 4 bytes
if (payload == null)
{
m.WriteByte(0);
}
else
{
if ((flags & 0x10) != 0)
{
// SR
m.WriteByte((byte)payload.Length);
}
else
{
// No SR (Short Record)
var payloadLength = (uint)payload.Length;
m.WriteByte((byte)(payloadLength >> 24));
m.WriteByte((byte)(payloadLength >> 16));
m.WriteByte((byte)(payloadLength >> 8));
m.WriteByte((byte)(payloadLength & 0x000000ff));
}
}

// ID length
if (id != null && (flags & 0x08) != 0)
m.WriteByte((byte)id.Length);

// Type length
if (type != null && type.Length > 0)
m.Write(type.ToArray(), 0, (int)type.Length);

// ID data
if (id != null && id.Length > 0)
m.Write(id.ToArray(), 0, (int)id.Length);

// Payload data
if (payload != null && payload.Length > 0)
m.Write(payload.ToArray(), 0, (int)payload.Length);
}

return m.ToArray();
}
}
}
8 changes: 8 additions & 0 deletions Caboodle/Nfc/Nfc.netstandard.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System;

namespace Microsoft.Caboodle
{
public static partial class Nfc
{
}
}
72 changes: 72 additions & 0 deletions Caboodle/Nfc/Nfc.shared.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;

namespace Microsoft.Caboodle
{
public static partial class Nfc
{
static bool isListening;

public static bool IsListening
{
get => isListening;
set
{
if (isListening != value)
{
isListening = value;
if (isListening)
{
StartTagListeners();
StartNdefMessageListeners();
}
else
{
StopNdefMessageListeners();
StopTagListeners();
}
}
}
}

public static event NfcTagEventHandler TagArrived;

public static event NfcTagEventHandler TagDeparted;

public static event NdefMessageReceivedEventHandler NdefMessageReceived;

public static void PublishMessage(string messageType, byte[] message)
=> PlatformPublishMessage(messageType, message);

public static void WriteTag(byte[] message)
=> PublishMessage("NDEF:WriteTag", message);

public static void ShareTag(byte[] message)
=> PublishMessage("NDEF", message);

public static void StopPublishing()
=> PlatformStopPublishing();
}

public delegate void NfcTagEventHandler(NfcTagEventArgs e);

public delegate void NdefMessageReceivedEventHandler(NdefMessageReceivedEventArgs e);

public class NfcTagEventArgs : EventArgs
{
public static new readonly NfcTagEventArgs Empty = new NfcTagEventArgs();

public NfcTagEventArgs()
{
}
}

public class NdefMessageReceivedEventArgs : EventArgs
{
public NdefMessageReceivedEventArgs(byte[] message)
{
Message = message;
}

public byte[] Message { get; }
}
}
Loading