Plugin.NFC added to rpoject

This commit is contained in:
2024-12-17 17:01:50 +01:00
parent 40e651e30a
commit a605bb9947
12 changed files with 3270 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
# Suppress: EC112
# top-most EditorConfig file
root = true
# Don't use tabs for indentation.
[*]
indent_style = space
# (Please don't specify an indent_size here; that has too many unintended consequences.)
# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 4
# Xml project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
indent_size = 2
# Xml config files
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
indent_size = 2
# JSON files
[*.json]
indent_size = 2
# Dotnet code style settings:
[*.{cs,vb}]
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = false
# Avoid "this." and "Me." if not necessary
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
# Use language keywords instead of framework type names for type references
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# Suggest more modern language features when available
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
# Naming Conventions:
# Pascal Casing
dotnet_naming_symbols.method_and_property_symbols.applicable_kinds = method,property,enum
dotnet_naming_symbols.method_and_property_symbols.applicable_accessibilities = *
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
dotnet_naming_rule.methods_and_properties_must_be_pascal_case.severity = warning
dotnet_naming_rule.methods_and_properties_must_be_pascal_case.symbols = method_and_property_symbols
dotnet_naming_rule.methods_and_properties_must_be_pascal_case.style = pascal_case_style
# Non-public members must be lower-case
#dotnet_naming_symbols.non_public_symbols.applicable_kinds = property,event,delegate
#dotnet_naming_symbols.non_public_symbols.applicable_accessibilities = private
#dotnet_naming_style.all_lower_case_style.capitalization = camel_case
#dotnet_naming_rule.non_public_members_must_be_lower_case.severity = warning
#dotnet_naming_rule.non_public_members_must_be_lower_case.symbols = non_public_symbols
#dotnet_naming_rule.non_public_members_must_be_lower_case.style = all_lower_case_style
# Private fields must start with an underscore
dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private
dotnet_naming_style.prefix_underscore.capitalization = camel_case
dotnet_naming_style.prefix_underscore.required_prefix = _
dotnet_naming_rule.private_members_with_underscore.severity = warning
dotnet_naming_rule.private_members_with_underscore.symbols = private_fields
dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore
# CSharp code style settings:
[*.cs]
# Do not prefer "var" everywhere
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
# Prefer method-like constructs to have a block body
csharp_style_expression_bodied_methods = true:suggestion
csharp_style_expression_bodied_constructors = true:none
csharp_style_expression_bodied_operators = true:none
# Prefer property-like constructs to have an expression-body
csharp_style_expression_bodied_properties = true:suggestion
csharp_style_expression_bodied_indexers = true:none
csharp_style_expression_bodied_accessors = true:none
# Suggest more modern language features when available
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Newline settings
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
+341
View File
@@ -0,0 +1,341 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
**/wwwroot/lib/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
@@ -0,0 +1,90 @@
using Android.App;
using Android.Content;
using Android.OS;
using System;
namespace Plugin.NFC
{
/// <summary>
/// Cross NFC (Android specific)
/// </summary>
public static partial class CrossNFC
{
internal static ActivityLifecycleContextListener lifecycleListener;
/// <summary>
/// Initialization
/// </summary>
/// <param name="application">Android <see cref="Application"/></param>
public static void Init(Application application)
{
if (lifecycleListener != null) return;
lifecycleListener = new ActivityLifecycleContextListener();
application.RegisterActivityLifecycleCallbacks(lifecycleListener);
}
/// <summary>
/// Initialization
/// </summary>
/// <param name="activity">Android <see cref="Activity"/></param>
public static void Init(Activity activity)
{
Init(activity.Application);
lifecycleListener.Activity = activity;
}
/// <summary>
/// Overrides Activity.OnNewIntent()
/// </summary>
/// <param name="intent">Android <see cref="Intent"/></param>
public static void OnNewIntent(Intent intent) => ((NFCImplementation)Current).HandleNewIntent(intent);
/// <summary>
/// Overrides Activity.OnResume()
/// </summary>
public static void OnResume() => ((NFCImplementation)Current).HandleOnResume();
/// <summary>
/// Returns the current Android <see cref="Context"/>
/// </summary>
internal static Context AppContext => Application.Context;
/// <summary>
/// Returns the current Android <see cref="Activity"/>
/// </summary>
/// <param name="throwError"></param>
/// <returns></returns>
internal static Activity GetCurrentActivity(bool throwError)
{
var activity = lifecycleListener?.Activity;
if (throwError && activity == null)
throw new NullReferenceException("The current Activity can not be detected. Ensure that you have called Init in your Activity or Application class.");
return activity;
}
}
/// <summary>
/// James Montemagno's ActivityLifecycleContextListener from CurrentActivityPlugin
/// <see href="https://github.com/jamesmontemagno/CurrentActivityPlugin"/>
/// </summary>
class ActivityLifecycleContextListener : Java.Lang.Object, Application.IActivityLifecycleCallbacks
{
WeakReference<Activity> _currentActivity = new WeakReference<Activity>(null);
internal Context Context => Activity ?? Application.Context;
internal Activity Activity
{
get => _currentActivity.TryGetTarget(out var a) ? a : null;
set => _currentActivity.SetTarget(value);
}
void Application.IActivityLifecycleCallbacks.OnActivityCreated(Activity activity, Bundle savedInstanceState) => Activity = activity;
void Application.IActivityLifecycleCallbacks.OnActivityDestroyed(Activity activity) { }
void Application.IActivityLifecycleCallbacks.OnActivityPaused(Activity activity) => Activity = activity;
void Application.IActivityLifecycleCallbacks.OnActivityResumed(Activity activity) => Activity = activity;
void Application.IActivityLifecycleCallbacks.OnActivitySaveInstanceState(Activity activity, Bundle outState) { }
void Application.IActivityLifecycleCallbacks.OnActivityStarted(Activity activity) { }
void Application.IActivityLifecycleCallbacks.OnActivityStopped(Activity activity) { }
}
}
+570
View File
@@ -0,0 +1,570 @@
using Android;
using Android.App;
using Android.Content;
using Android.Nfc;
using Android.Nfc.Tech;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plugin.NFC
{
/// <summary>
/// Android implementation of <see cref="INFC"/>
/// </summary>
public class NFCImplementation : INFC
{
public event EventHandler OnTagConnected;
public event EventHandler OnTagDisconnected;
public event NdefMessageReceivedEventHandler OnMessageReceived;
public event NdefMessagePublishedEventHandler OnMessagePublished;
public event TagDiscoveredEventHandler OnTagDiscovered;
public event EventHandler OniOSReadingSessionCancelled;
public event TagListeningStatusChangedEventHandler OnTagListeningStatusChanged;
readonly NfcAdapter _nfcAdapter;
bool _isListening;
bool _isWriting;
bool _isFormatting;
Tag _currentTag;
/// <summary>
/// Current Android <see cref="Context"/>
/// </summary>
Context CurrentContext => CrossNFC.AppContext;
/// <summary>
/// Current Android <see cref="Activity"/>
/// </summary>
Activity CurrentActivity => CrossNFC.GetCurrentActivity(true);
/// <summary>
/// Checks if NFC Feature is available
/// </summary>
public bool IsAvailable
{
get
{
if (CurrentContext.CheckCallingOrSelfPermission(Manifest.Permission.Nfc) != Android.Content.PM.Permission.Granted)
return false;
return _nfcAdapter != null;
}
}
/// <summary>
/// Checks if NFC Feature is enabled
/// </summary>
public bool IsEnabled => IsAvailable && _nfcAdapter.IsEnabled;
/// <summary>
/// Checks if writing mode is supported
/// </summary>
public bool IsWritingTagSupported => NFCUtils.IsWritingSupported();
/// <summary>
/// NFC configuration
/// </summary>
public NfcConfiguration Configuration { get; private set; }
/// <summary>
/// Default constructor
/// </summary>
public NFCImplementation()
{
_nfcAdapter = NfcAdapter.GetDefaultAdapter(CurrentContext);
Configuration = NfcConfiguration.GetDefaultConfiguration();
}
/// <summary>
/// Update NFC configuration
/// </summary>
/// <param name="configuration"><see cref="NfcConfiguration"/></param>
public void SetConfiguration(NfcConfiguration configuration) => Configuration.Update(configuration);
/// <summary>
/// Starts tags detection
/// </summary>
public void StartListening()
{
if (_nfcAdapter == null)
return;
var intent = new Intent(CurrentActivity, CurrentActivity.GetType()).AddFlags(ActivityFlags.SingleTop);
// We don't use MonoAndroid12.0 as targetframework for easier backward compatibility:
// MonoAndroid12.0 needs JDK 11.
PendingIntentFlags pendingIntentFlags = 0;
#if NET6_0_OR_GREATER
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.S)
pendingIntentFlags = PendingIntentFlags.Mutable;
#else
if ((int)Android.OS.Build.VERSION.SdkInt >= 31) //Android.OS.BuildVersionCodes.S
pendingIntentFlags = (PendingIntentFlags)33554432; //PendingIntentFlags.Mutable
#endif
var pendingIntent = PendingIntent.GetActivity(CurrentActivity, 0, intent, pendingIntentFlags);
var ndefFilter = new IntentFilter(NfcAdapter.ActionNdefDiscovered);
ndefFilter.AddDataType("*/*");
var tagFilter = new IntentFilter(NfcAdapter.ActionTagDiscovered);
tagFilter.AddCategory(Intent.CategoryDefault);
var filters = new IntentFilter[] { ndefFilter, tagFilter };
_nfcAdapter.EnableForegroundDispatch(CurrentActivity, pendingIntent, filters, null);
_isListening = true;
OnTagListeningStatusChanged?.Invoke(_isListening);
}
/// <summary>
/// Stops tags detection
/// </summary>
public void StopListening()
{
DisablePublishing();
if (_nfcAdapter != null)
_nfcAdapter.DisableForegroundDispatch(CurrentActivity);
_isListening = false;
OnTagListeningStatusChanged?.Invoke(_isListening);
}
/// <summary>
/// Starts tag publishing (writing or formatting)
/// </summary>
/// <param name="clearMessage">Format tag</param>
public void StartPublishing(bool clearMessage = false)
{
if (!IsWritingTagSupported)
return;
_isWriting = true;
_isFormatting = clearMessage;
}
/// <summary>
/// Stops tag publishing
/// </summary>
public void StopPublishing() => DisablePublishing();
/// <summary>
/// Publish or write a message on a tag
/// </summary>
/// <param name="tagInfo">see <see cref="ITagInfo"/></param>
/// <param name="makeReadOnly">make tag read-only</param>
public void PublishMessage(ITagInfo tagInfo, bool makeReadOnly = false) => WriteOrClearMessage(tagInfo, false, makeReadOnly);
/// <summary>
/// Format tag
/// </summary>
/// <param name="tagInfo">see <see cref="ITagInfo"/></param>
public void ClearMessage(ITagInfo tagInfo) => WriteOrClearMessage(tagInfo, true);
/// <summary>
/// Write or Clear a NDEF message
/// </summary>
/// <param name="tagInfo"><see cref="ITagInfo"/></param>
/// <param name="clearMessage">Clear Message</param>
/// <param name="makeReadOnly">Make tag read-only</param>
internal void WriteOrClearMessage(ITagInfo tagInfo, bool clearMessage = false, bool makeReadOnly = false)
{
try
{
if (_currentTag == null)
throw new Exception(Configuration.Messages.NFCErrorMissingTag);
if (tagInfo == null)
throw new Exception(Configuration.Messages.NFCErrorMissingTagInfo);
var ndef = Ndef.Get(_currentTag);
if (ndef != null)
{
try
{
if (!ndef.IsWritable)
throw new Exception(Configuration.Messages.NFCErrorReadOnlyTag);
if (ndef.MaxSize < NFCUtils.GetSize(tagInfo.Records))
throw new Exception(Configuration.Messages.NFCErrorCapacityTag);
ndef.Connect();
OnTagConnected?.Invoke(null, EventArgs.Empty);
NdefMessage message = null;
if (clearMessage)
{
message = GetEmptyNdefMessage();
}
else
{
var records = new List<NdefRecord>();
for (var i = 0; i < tagInfo.Records.Length; i++)
{
var record = tagInfo.Records[i];
if (GetAndroidNdefRecord(record) is NdefRecord ndefRecord)
records.Add(ndefRecord);
}
if (records.Any())
message = new NdefMessage(records.ToArray());
}
if (message != null)
{
ndef.WriteNdefMessage(message);
if (!clearMessage && makeReadOnly)
{
if (!MakeReadOnly(ndef))
Console.WriteLine("Cannot lock tag");
}
var nTag = GetTagInfo(_currentTag, ndef.NdefMessage);
OnMessagePublished?.Invoke(nTag);
}
else
throw new Exception(Configuration.Messages.NFCErrorWrite);
}
catch (Android.Nfc.TagLostException tlex)
{
throw new Exception("Tag Lost Error: " + tlex.Message);
}
catch (Java.IO.IOException ioex)
{
throw new Exception("Tag IO Error: " + ioex.Message);
}
catch (Android.Nfc.FormatException fe)
{
throw new Exception("Tag Format Error: " + fe.Message);
}
catch (Exception ex)
{
throw new Exception("Tag Error:" + ex.Message);
}
finally
{
if (ndef.IsConnected)
ndef.Close();
_currentTag = null;
OnTagDisconnected?.Invoke(null, EventArgs.Empty);
}
}
else
throw new Exception(Configuration.Messages.NFCErrorNotCompliantTag);
}
catch (Exception ex)
{
StopPublishingAndThrowError(ex.Message);
}
}
/// <summary>
/// Handle Android OnNewIntent
/// </summary>
/// <param name="intent">Android <see cref="Intent"/></param>
internal void HandleNewIntent(Intent intent)
{
if (intent == null)
return;
if (intent.Action == NfcAdapter.ActionTagDiscovered || intent.Action == NfcAdapter.ActionNdefDiscovered)
{
_currentTag = intent.GetParcelableExtra(NfcAdapter.ExtraTag) as Tag;
if (_currentTag != null)
{
var nTag = GetTagInfo(_currentTag);
if (_isWriting)
{
// Write mode
OnTagDiscovered?.Invoke(nTag, _isFormatting);
}
else
{
// Read mode
OnMessageReceived?.Invoke(nTag);
}
}
}
}
/// <summary>
/// Handle Android OnResume
/// </summary>
internal void HandleOnResume()
{
// Android 10 fix:
// If listening mode is already enable, we restart listening when activity is resumed
if (_isListening)
StartListening();
}
#region Private
/// <summary>
/// Stops publishing and throws error
/// </summary>
/// <param name="message">message</param>
void StopPublishingAndThrowError(string message)
{
StopPublishing();
throw new Exception(message);
}
/// <summary>
/// Deactivate publishing
/// </summary>
void DisablePublishing()
{
_isWriting = false;
_isFormatting = false;
}
/// <summary>
/// Transforms an array of <see cref="NdefRecord"/> into an array of <see cref="NFCNdefRecord"/>
/// </summary>
/// <param name="records">Array of <see cref="NdefRecord"/></param>
/// <returns>Array of <see cref="NFCNdefRecord"/></returns>
NFCNdefRecord[] GetRecords(NdefRecord[] records)
{
var results = new NFCNdefRecord[records.Length];
for (var i = 0; i < records.Length; i++)
{
var ndefRecord = new NFCNdefRecord
{
TypeFormat = (NFCNdefTypeFormat)records[i].Tnf,
Uri = records[i].ToUri()?.ToString(),
MimeType = records[i].ToMimeType(),
Payload = records[i].GetPayload()
};
results.SetValue(ndefRecord, i);
}
return results;
}
/// <summary>
/// Returns informations contains in NFC Tag
/// </summary>
/// <param name="tag">Android <see cref="Tag"/></param>
/// <param name="ndefMessage">Android <see cref="NdefMessage"/></param>
/// <returns><see cref="ITagInfo"/></returns>
ITagInfo GetTagInfo(Tag tag, NdefMessage ndefMessage = null)
{
if (tag == null)
return null;
var ndef = Ndef.Get(tag);
var nTag = new TagInfo(tag.GetId(), ndef != null);
if (ndef != null)
{
nTag.Capacity = ndef.MaxSize;
nTag.IsWritable = ndef.IsWritable;
if (ndefMessage == null)
ndefMessage = ndef.CachedNdefMessage;
if (ndefMessage != null)
{
var records = ndefMessage.GetRecords();
nTag.Records = GetRecords(records);
}
}
return nTag;
}
/// <summary>
/// Transforms a <see cref="NFCNdefRecord"/> into an Android <see cref="NdefRecord"/>
/// </summary>
/// <param name="record">Object <see cref="NFCNdefRecord"/></param>
/// <returns>Android <see cref="NdefRecord"/></returns>
NdefRecord GetAndroidNdefRecord(NFCNdefRecord record)
{
if (record == null)
return null;
NdefRecord ndefRecord = null;
switch (record.TypeFormat)
{
case NFCNdefTypeFormat.WellKnown:
var languageCode = record.LanguageCode;
if (string.IsNullOrWhiteSpace(languageCode)) languageCode = Configuration.DefaultLanguageCode;
ndefRecord = NdefRecord.CreateTextRecord(languageCode.Substring(0, 2), Encoding.UTF8.GetString(record.Payload));
break;
case NFCNdefTypeFormat.Mime:
ndefRecord = NdefRecord.CreateMime(record.MimeType, record.Payload);
break;
case NFCNdefTypeFormat.Uri:
ndefRecord = NdefRecord.CreateUri(Encoding.UTF8.GetString(record.Payload));
break;
case NFCNdefTypeFormat.External:
ndefRecord = NdefRecord.CreateExternal(record.ExternalDomain, record.ExternalType, record.Payload);
break;
case NFCNdefTypeFormat.Empty:
ndefRecord = GetEmptyNdefRecord();
break;
case NFCNdefTypeFormat.Unknown:
case NFCNdefTypeFormat.Unchanged:
case NFCNdefTypeFormat.Reserved:
default:
break;
}
return ndefRecord;
}
/// <summary>
/// Returns an empty Android <see cref="NdefRecord"/>
/// </summary>
/// <returns>Android <see cref="NdefRecord"/></returns>
NdefRecord GetEmptyNdefRecord()
{
var empty = Array.Empty<byte>();
return new NdefRecord(NdefRecord.TnfEmpty, empty, empty, empty);
}
/// <summary>
/// Returns an empty Android <see cref="NdefMessage"/>
/// </summary>
/// <returns>Android <see cref="NdefMessage"/></returns>
NdefMessage GetEmptyNdefMessage()
{
var records = new NdefRecord[1];
records[0] = GetEmptyNdefRecord();
return new NdefMessage(records);
}
/// <summary>
/// Make a tag read-only
/// WARNING: This operation is permanent
/// </summary>
/// <param name="ndef"><see cref="Ndef"/></param>
/// <returns>boolean</returns>
bool MakeReadOnly(Ndef ndef)
{
if (ndef == null)
return false;
var result = false;
var newConnection = false;
if (!ndef.IsConnected)
{
newConnection = true;
ndef.Connect();
}
if (ndef.CanMakeReadOnly())
result = ndef.MakeReadOnly();
if (newConnection && ndef.IsConnected)
ndef.Close();
return result;
}
#endregion
#region NFC Status Event Listener
NfcBroadcastReceiver _nfcBroadcastReceiver;
event OnNfcStatusChangedEventHandler _onNfcStatusChangedInternal;
public event OnNfcStatusChangedEventHandler OnNfcStatusChanged
{
add
{
var wasRunning = _onNfcStatusChangedInternal != null;
_onNfcStatusChangedInternal += value;
if (!wasRunning && _onNfcStatusChangedInternal != null)
{
RegisterListener();
}
}
remove
{
var wasRunning = _onNfcStatusChangedInternal != null;
_onNfcStatusChangedInternal -= value;
if (wasRunning && _onNfcStatusChangedInternal == null)
{
UnRegisterListener();
}
}
}
/// <summary>
/// Register NFC Broadcast Receiver
/// </summary>
void RegisterListener()
{
_nfcBroadcastReceiver = new NfcBroadcastReceiver(OnNfcStatusChange);
CurrentContext?.RegisterReceiver(_nfcBroadcastReceiver, new IntentFilter(NfcAdapter.ActionAdapterStateChanged));
}
/// <summary>
/// Unregister NFC Broadcast Receiver
/// </summary>
void UnRegisterListener()
{
if (_nfcBroadcastReceiver == null)
return;
try
{
CurrentContext?.UnregisterReceiver(_nfcBroadcastReceiver);
}
catch (Java.Lang.IllegalArgumentException ex)
{
throw new Exception("NFC Broadcast Receiver Error: " + ex.Message);
}
_nfcBroadcastReceiver.Dispose();
_nfcBroadcastReceiver = null;
}
/// <summary>
/// Called when NFC status has changed
/// </summary>
void OnNfcStatusChange() => _onNfcStatusChangedInternal?.Invoke(IsEnabled);
/// <summary>
/// Broadcast Receiver to check NFC feature availability
/// </summary>
[BroadcastReceiver(Enabled = true, Exported = false, Label = "NFC Status Broadcast Receiver")]
class NfcBroadcastReceiver : BroadcastReceiver
{
Action _onChanged;
public NfcBroadcastReceiver() { }
public NfcBroadcastReceiver(Action onChanged)
{
_onChanged = onChanged;
}
public override async void OnReceive(Context context, Intent intent)
{
if (intent.Action == NfcAdapter.ActionAdapterStateChanged)
{
var state = intent.GetIntExtra(NfcAdapter.ExtraAdapterState, default);
if (state == NfcAdapter.StateOff || state == NfcAdapter.StateOn)
{
// await 1500ms to ensure that the status updates
await Task.Delay(1500);
_onChanged?.Invoke();
}
}
}
}
#endregion
}
}
+107
View File
@@ -0,0 +1,107 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.44">
<PropertyGroup>
<!--Work around so the conditions work below-->
<TargetFrameworks>netstandard1.0;netstandard2.0;Xamarin.iOS10;MonoAndroid10.0;net8.0;net8.0-android;net8.0-ios</TargetFrameworks>
<AssemblyName>Plugin.NFC</AssemblyName>
<RootNamespace>Plugin.NFC</RootNamespace>
<PackageId>Plugin.NFC</PackageId>
<Product>$(AssemblyName) ($(TargetFramework))</Product>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<AssemblyFileVersion>1.0.0.0</AssemblyFileVersion>
<Version>1.0.0.0</Version>
<PackageVersion>1.0.0.0</PackageVersion>
<PackOnBuild>true</PackOnBuild>
<NeutralLanguage>en</NeutralLanguage>
<DefineConstants>$(DefineConstants);</DefineConstants>
<NoWarn>CS0067</NoWarn>
<UseFullSemVerForNuGet>false</UseFullSemVerForNuGet>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<LangVersion>latest</LangVersion>
<DebugType>portable</DebugType>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/franckbour/Plugin.NFC</PackageProjectUrl>
<RepositoryUrl>https://github.com/franckbour/Plugin.NFC</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageReleaseNotes>https://github.com/franckbour/Plugin.NFC/blob/master/CHANGELOG.md</PackageReleaseNotes>
<PackageIconUrl>https://github.com/franckbour/Plugin.NFC/raw/master/art/nfc128.png</PackageIconUrl>
<PackageIcon>icon.png</PackageIcon>
<PackageTags>maui, xamarin, ios, android, xamarin.forms, plugin, NFC</PackageTags>
<Title>NFC Plugin for Xamarin</Title>
<Summary>A Cross-Platform plugin to easily read and write NFC tags.</Summary>
<Description>Cross-Platform NFC (Near Field Communication) plugin to easily read and write NFC tags in your application.</Description>
<Owners>Franck Bour</Owners>
<Authors>Franck Bour</Authors>
<Copyright>Copyright 2022</Copyright>
<!-- When built in 2019 will remove extra references on pack for iOS in System.Drawing.Common -->
<DisableExtraReferences>false</DisableExtraReferences>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!-- sourcelink: Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<!-- sourcelink: Embed source files that are not tracked by the source control manager in the PDB -->
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<!-- sourcelink: Include PDB in the built .nupkg -->
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
</PropertyGroup>
<PropertyGroup>
<_NET8 Condition=" $(TargetFramework.Contains('net8')) ">true</_NET8>
<_DROID Condition=" $(TargetFramework.ToLowerInvariant().Contains('droid')) ">true</_DROID>
<_IOS Condition=" $(TargetFramework.ToLowerInvariant().Contains('ios')) ">true</_IOS>
<_MOBILE Condition=" '$(_DROID)' == 'true' OR '$(_IOS)' == 'true' ">true</_MOBILE>
<DefineConstants Condition=" '$(_DROID)' == 'true' ">$(DefineConstants);__ANDROID__;__MOBILE__;</DefineConstants>
<DefineConstants Condition=" '$(_IOS)' == 'true' ">$(DefineConstants);__IOS__;__MOBILE__;</DefineConstants>
<DefineConstants Condition=" '$(_NET8)' == 'true' ">$(DefineConstants);__NET8__;</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(_NET8)' == 'true' AND '$(_MOBILE)' == 'true' ">
<SupportedOSPlatformVersion Condition="'$(_IOS)' == 'true'">11.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="'$(_DROID)' == 'true'">21.0</SupportedOSPlatformVersion>
</PropertyGroup>
<!-- Deterministic Builds (Azure Pipelines) -->
<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<ItemGroup Condition=" '$(Configuration)' == 'Release' ">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\art\nfc128.png" PackagePath="icon.png" Pack="true" />
<Compile Include="**\*.shared.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) OR '$(TargetFramework)' == 'net8.0' ">
</ItemGroup>
<ItemGroup Condition=" '$(_DROID)' == 'true' ">
<Compile Include="**\*.android.cs" />
</ItemGroup>
<ItemGroup Condition=" '$(_IOS)' == 'true' ">
<Compile Include="**\*.iOS.cs" />
</ItemGroup>
</Project>
+73
View File
@@ -0,0 +1,73 @@
using System;
namespace Plugin.NFC
{
/// <summary>
/// Cross NFC
/// </summary>
public static partial class CrossNFC
{
static Lazy<INFC> _implementation = new Lazy<INFC>(() => CreateNFC(), System.Threading.LazyThreadSafetyMode.PublicationOnly);
/// <summary>
/// Gets if the plugin is supported on the current platform.
/// </summary>
public static bool IsSupported => _implementation.Value != null;
/// <summary>
/// Legacy Mode (Supporting Mifare Classic on iOS)
/// </summary>
static bool _legacy = false;
public static bool Legacy
{
get
{
return _legacy;
}
set
{
_legacy = value;
_implementation = new Lazy<INFC>(() => CreateNFC(), System.Threading.LazyThreadSafetyMode.PublicationOnly);
}
}
/// <summary>
/// Current plugin implementation to use
/// </summary>
public static INFC Current
{
get
{
INFC ret = _implementation.Value;
if (ret == null)
{
throw NotImplementedInReferenceAssembly();
}
return ret;
}
}
static INFC CreateNFC()
{
#if NETSTANDARD || !__MOBILE__
return null;
#elif __IOS__
ObjCRuntime.Class.ThrowOnInitFailure = false;
if (NFCUtils.IsWritingSupported() && !Legacy)
return new NFCImplementation();
return new NFCImplementation_Before_iOS13();
#else
#pragma warning disable IDE0022 // Use expression body for methods
return new NFCImplementation();
#pragma warning restore IDE0022 // Use expression body for methods
#endif
}
internal static Exception NotImplementedInReferenceAssembly() =>
new NotImplementedException("This functionality is not implemented in the portable version of this assembly. You should reference the NuGet package from your main application project in order to reference the platform-specific implementation.");
}
}
+118
View File
@@ -0,0 +1,118 @@
using System;
namespace Plugin.NFC
{
#region Event delegates
public delegate void NdefMessageReceivedEventHandler(ITagInfo tagInfo);
public delegate void NdefMessagePublishedEventHandler(ITagInfo tagInfo);
public delegate void TagDiscoveredEventHandler(ITagInfo tagInfo, bool format);
public delegate void OnNfcStatusChangedEventHandler(bool isEnabled);
public delegate void TagListeningStatusChangedEventHandler(bool isListening);
#endregion
/// <summary>
/// Main interface for NFC
/// </summary>
public interface INFC
{
/// <summary>
/// Checks if NFC Feature is available
/// </summary>
bool IsAvailable { get; }
/// <summary>
/// Checks if NFC Feature is enabled
/// </summary>
bool IsEnabled { get; }
/// <summary>
/// Checks if writing mode is supported
/// </summary>
bool IsWritingTagSupported { get; }
/// <summary>
/// NFC Configuration
/// </summary>
NfcConfiguration Configuration { get; }
/// <summary>
/// Set Nfc configuration
/// </summary>
/// <param name="configuration"><see cref="NfcConfiguration"/></param>
void SetConfiguration(NfcConfiguration configuration);
/// <summary>
/// Starts tags detection
/// </summary>
void StartListening();
/// <summary>
/// Stops tags detection
/// </summary>
void StopListening();
/// <summary>
/// Starts tag publishing (writing or formatting)
/// </summary>
/// <param name="clearMessage">Format tag</param>
void StartPublishing(bool clearMessage = false);
/// <summary>
/// Stops tag publishing
/// </summary>
void StopPublishing();
/// <summary>
/// Publish or write a message on a tag
/// </summary>
/// <param name="tagInfo">see <see cref="ITagInfo"/></param>
/// <param name="makeReadOnly">make tag read-only</param>
void PublishMessage(ITagInfo tagInfo, bool makeReadOnly = false);
/// <summary>
/// Format tag
/// </summary>
/// <param name="tagInfo">see <see cref="ITagInfo"/></param>
void ClearMessage(ITagInfo tagInfo);
/// <summary>
/// Event raised when tag is connected
/// </summary>
event EventHandler OnTagConnected;
/// <summary>
/// Event raised when tag is disconnected
/// </summary>
event EventHandler OnTagDisconnected;
/// <summary>
/// Event raised when ndef message is received
/// </summary>
event NdefMessageReceivedEventHandler OnMessageReceived;
/// <summary>
/// Event raised when a tag is discovered (Editing)
/// </summary>
event TagDiscoveredEventHandler OnTagDiscovered;
/// <summary>
/// Event raised when ndef message has been published
/// </summary>
event NdefMessagePublishedEventHandler OnMessagePublished;
/// <summary>
/// Event raised when iOS NFC reading session is cancelled
/// </summary>
event EventHandler OniOSReadingSessionCancelled;
/// <summary>
/// Event raised when NFC status changes
/// </summary>
event OnNfcStatusChangedEventHandler OnNfcStatusChanged;
/// <summary>
/// Event raised when NFC listener status changes
/// </summary>
event TagListeningStatusChangedEventHandler OnTagListeningStatusChanged;
}
}
+104
View File
@@ -0,0 +1,104 @@
namespace Plugin.NFC
{
/// <summary>
/// Interface for ITagInfo
/// </summary>
public interface ITagInfo
{
/// <summary>
/// Tag Raw Identifier
/// </summary>
byte[] Identifier { get; }
/// <summary>
/// Tag Serial Number
/// </summary>
string SerialNumber { get; }
/// <summary>
/// Writable tag
/// </summary>
bool IsWritable { get; set; }
/// <summary>
/// Empty tag
/// </summary>
bool IsEmpty { get; }
/// <summary>
/// Supported tag
/// </summary>
bool IsSupported { get; }
/// <summary>
/// Capacity of tag in bytes
/// </summary>
int Capacity { get; set; }
/// <summary>
/// Array of <see cref="NFCNdefRecord"/> of tag
/// </summary>
NFCNdefRecord[] Records { get; set; }
}
/// <summary>
/// Class describing the information containing within a NFC tag
/// </summary>
public class NFCNdefRecord
{
/// <summary>
/// NDEF Type
/// </summary>
public NFCNdefTypeFormat TypeFormat { get; set; }
/// <summary>
/// MimeType used for <see cref="NFCNdefTypeFormat.Mime"/> type
/// </summary>
public string MimeType { get; set; } = "text/plain";
/// <summary>
/// External domain used for <see cref="NFCNdefTypeFormat.External"/> type
/// </summary>
public string ExternalDomain { get; set; }
/// <summary>
/// External type used for <see cref="NFCNdefTypeFormat.External"/> type
/// </summary>
public string ExternalType { get; set; }
/// <summary>
/// Payload
/// </summary>
public byte[] Payload { get; set; }
/// <summary>
/// Uri
/// </summary>
public string Uri { get; set; }
/// <summary>
/// String formatted payload
/// </summary>
public string Message => NFCUtils.GetMessage(TypeFormat, Payload, Uri);
/// <summary>
/// Two letters ISO 639-1 Language Code (ex: en, fr, de...)
/// </summary>
public string LanguageCode { get; set; }
}
/// <summary>
/// Enumeration of NDEF type
/// </summary>
public enum NFCNdefTypeFormat
{
Empty = 0x00,
WellKnown = 0x01,
Mime = 0x02,
Uri = 0x03,
External = 0x04,
Unknown = 0x05,
Unchanged = 0x06,
Reserved = 0x07
}
}
+113
View File
@@ -0,0 +1,113 @@
using System.Linq;
using System.Text;
#if __IOS__
using UIKit;
#endif
namespace Plugin.NFC
{
/// <summary>
/// NFC tools
/// </summary>
public static class NFCUtils
{
/// <summary>
/// Returns the content size of an array of <see cref="NFCNdefRecord"/>
/// </summary>
/// <param name="records">array of <see cref="NFCNdefRecord"/></param>
/// <returns>Content size</returns>
internal static int GetSize(NFCNdefRecord[] records)
{
var size = 0;
if (records != null && records.Length > 0)
{
for (var i = 0; i < records.Length; i++)
{
if (records[i] != null)
size += records[i].Payload.Length;
}
}
return size;
}
/// <summary>
/// Returns the string formatted payload
/// </summary>
/// <param name="type">type of <see cref="NFCNdefTypeFormat"/></param>
/// <param name="payload">record payload</param>
/// <param name="uri">record uri</param>
/// <returns>String formatted payload</returns>
internal static string GetMessage(NFCNdefTypeFormat type, byte[] payload, string uri)
{
string message;
if (!string.IsNullOrWhiteSpace(uri))
message = uri;
else
{
if (type == NFCNdefTypeFormat.WellKnown)
{
// NDEF_WELLKNOWN Text record
var status = payload[0];
var enc = status & 0x80;
var languageCodeLength = status & 0x3F;
if (enc == 0)
message = Encoding.UTF8.GetString(payload, languageCodeLength + 1, payload.Length - languageCodeLength - 1);
else
message = Encoding.Unicode.GetString(payload, languageCodeLength + 1, payload.Length - languageCodeLength - 1);
}
else
{
// Other NDEF types
message = Encoding.UTF8.GetString(payload, 0, payload.Length);
}
}
return message;
}
/// <summary>
/// Transforms a string message into an array of bytes
/// </summary>
/// <param name="text">text message</param>
/// <returns>Array of bytes</returns>
public static byte[] EncodeToByteArray(string text) => Encoding.UTF8.GetBytes(text);
/// <summary>
/// Returns the string formatted payload
/// </summary>
/// <param name="record">Object <see cref="NFCNdefRecord"/></param>
/// <returns>String formatted payload</returns>
public static string GetMessage(NFCNdefRecord record)
{
if (record == null)
return string.Empty;
return GetMessage(record.TypeFormat, record.Payload, record.Uri);
}
/// <summary>
/// Convert bytes array into hexadecimal string
/// </summary>
/// <param name="bytes">Bytes Array</param>
/// <param name="separator">Separator</param>
/// <returns>Hexadecimal string</returns>
public static string ByteArrayToHexString(byte[] bytes, string separator = null)
{
return bytes == null ? string.Empty : string.Join(separator ?? string.Empty, bytes.Select(b => b.ToString("X2")));
}
/// <summary>
/// Checks if writing tags is supported
/// </summary>
/// <returns>boolean</returns>
public static bool IsWritingSupported()
{
#if __IOS__
var splitted = UIDevice.CurrentDevice.SystemVersion?.Split('.');
if (splitted != null && splitted.Length > 0 && int.TryParse(splitted[0], out var majorVersion))
return majorVersion >= 13;
return false;
#else
return true;
#endif
}
}
}
@@ -0,0 +1,282 @@
namespace Plugin.NFC
{
/// <summary>
/// NFC Configuration class
/// </summary>
public class NfcConfiguration
{
/// <summary>
/// List of user defined messages
/// </summary>
public UserDefinedMessages Messages { get; set; }
/// <summary>
/// Sets ISO 639-1 Language Code for all ndef records (default is "en")
/// </summary>
public string DefaultLanguageCode { get; set; }
/// <summary>
/// Update Nfc Configuration with a new configuration object
/// </summary>
/// <param name="newCfg"><see cref="NfcConfiguration"/></param>
public void Update(NfcConfiguration newCfg)
{
if (newCfg == null || newCfg.Messages == null)
return;
Messages = newCfg.Messages;
DefaultLanguageCode = newCfg.DefaultLanguageCode;
}
/// <summary>
/// Get the default Nfc configuration
/// </summary>
/// <returns>Default <see cref="NfcConfiguration"/></returns>
public static NfcConfiguration GetDefaultConfiguration()
=> new NfcConfiguration { Messages = new UserDefinedMessages(), DefaultLanguageCode = "en" };
}
/// <summary>
/// User defined UI messages
/// </summary>
public class UserDefinedMessages
{
string _nfcSessionInvalidated = "Session Invalidated";
string _nfcSessionInvalidatedButton = "OK";
string _nfcWritingNotSupported = "Writing NFC Tag is not supported on this device";
string _nfcDialogAlertMessage = "Please hold your phone near a NFC tag";
string _nfcErrorRead = "Read error. Please try again";
string _nfcErrorEmptyTag = "Tag is empty";
string _nfcErrorReadOnlyTag = "Tag is not writable";
string _nfcErrorCapacityTag = "Tag's capacity is too low";
string _nfcErrorMissingTag = "Tag is missing";
string _nfcErrorMissingTagInfo = "No Tag Informations: nothing to write";
string _nfcErrorNotSupportedTag = "Tag is not supported";
string _nfcErrorNotCompliantTag = "Tag is not NDEF compliant";
string _nfcErrorWrite = "Nothing to write";
string _nfcSuccessRead = "Read Operation Successful";
string _nfcSuccessWrite = "Write Operation Successful";
string _nfcSuccessClear = "Clear Operation Successful";
string _nfcSessionTimeout = "session timeout";
/// <summary>
/// Session timeout
/// </summary>
public string NFCSessionTimeout
{
get => _nfcSessionTimeout;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcSessionTimeout = value;
}
}
/// <summary>
/// Session invalidated
/// </summary>
public string NFCSessionInvalidatedButton
{
get => _nfcSessionInvalidatedButton;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcSessionInvalidatedButton = value;
}
}
/// <summary>
/// Session invalidated
/// </summary>
public string NFCSessionInvalidated
{
get => _nfcSessionInvalidated;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcSessionInvalidated = value;
}
}
/// <summary>
/// Writing feature not supported
/// </summary>
public string NFCWritingNotSupported
{
get => _nfcWritingNotSupported;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcWritingNotSupported = value;
}
}
/// <summary>
/// [iOS] NFC Scan dialog alert message
/// </summary>
public string NFCDialogAlertMessage
{
get => _nfcDialogAlertMessage;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcDialogAlertMessage = value;
}
}
/// <summary>
/// Read operation error
/// </summary>
public string NFCErrorRead
{
get => _nfcErrorRead;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcErrorRead = value;
}
}
/// <summary>
/// Write operation error
/// </summary>
public string NFCErrorWrite
{
get => _nfcErrorWrite;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcErrorWrite = value;
}
}
/// <summary>
/// Empty tag error
/// </summary>
public string NFCErrorEmptyTag
{
get => _nfcErrorEmptyTag;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcErrorEmptyTag = value;
}
}
/// <summary>
/// Read-only tag error
/// </summary>
public string NFCErrorReadOnlyTag
{
get => _nfcErrorReadOnlyTag;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcErrorReadOnlyTag = value;
}
}
/// <summary>
/// Tag capacity error
/// </summary>
public string NFCErrorCapacityTag
{
get => _nfcErrorCapacityTag;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcErrorCapacityTag = value;
}
}
/// <summary>
/// Missing tag error
/// </summary>
public string NFCErrorMissingTag
{
get => _nfcErrorMissingTag;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcErrorMissingTag = value;
}
}
/// <summary>
/// Missing tag info error
/// </summary>
public string NFCErrorMissingTagInfo
{
get => _nfcErrorMissingTagInfo;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcErrorMissingTagInfo = value;
}
}
/// <summary>
/// Not supported tag error
/// </summary>
public string NFCErrorNotSupportedTag
{
get => _nfcErrorNotSupportedTag;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcErrorNotSupportedTag = value;
}
}
/// <summary>
/// Not NDEF compliant tag error
/// </summary>
public string NFCErrorNotCompliantTag
{
get => _nfcErrorNotCompliantTag;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcErrorNotCompliantTag = value;
}
}
/// <summary>
/// [iOS] Successful read operation message
/// </summary>
public string NFCSuccessRead
{
get => _nfcSuccessRead;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcSuccessRead = value;
}
}
/// <summary>
/// [iOS] Successful write operation message
/// </summary>
public string NFCSuccessWrite
{
get => _nfcSuccessWrite;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcSuccessWrite = value;
}
}
/// <summary>
/// [iOS] Successful clear operation message
/// </summary>
public string NFCSuccessClear
{
get => _nfcSuccessClear;
set
{
if (!string.IsNullOrWhiteSpace(value))
_nfcSuccessClear = value;
}
}
}
}
+62
View File
@@ -0,0 +1,62 @@
namespace Plugin.NFC
{
/// <summary>
/// Default implementation of <see cref="ITagInfo"/>
/// </summary>
public class TagInfo : ITagInfo
{
public byte[] Identifier { get; }
/// <summary>
/// Tag Serial Number
/// </summary>
public string SerialNumber { get; }
/// <summary>
/// Writable tag
/// </summary>
public bool IsWritable { get; set; }
/// <summary>
/// Capacity of tag in bytes
/// </summary>
public int Capacity { get; set; }
/// <summary>
/// Array of <see cref="NFCNdefRecord"/> of tag
/// </summary>
public NFCNdefRecord[] Records { get; set; }
/// <summary>
/// Empty tag
/// </summary>
public bool IsEmpty => Records == null || Records.Length == 0 || Records[0] == null ||Records[0].TypeFormat == NFCNdefTypeFormat.Empty;
/// <summary>
///
/// </summary>
public bool IsSupported { get; private set; }
/// <summary>
/// Default constructor
/// </summary>
public TagInfo()
{
IsSupported = true;
}
/// <summary>
/// Custom contructor
/// </summary>
/// <param name="identifier">Tag Identifier</param>
/// <param name="isNdef">Is Ndef tag</param>
public TagInfo(byte[] identifier, bool isNdef = true)
{
Identifier = identifier;
SerialNumber = NFCUtils.ByteArrayToHexString(identifier);
IsSupported = isNdef;
}
public override string ToString() => $"TagInfo: identifier: {Identifier}, SerialNumber:{SerialNumber}, Capacity:{Capacity} bytes, IsSupported:{IsSupported}, IsEmpty:{IsEmpty}, IsWritable:{IsWritable}";
}
}
File diff suppressed because it is too large Load Diff