Anonymous
Xamarin.Forms NFC: автоматический запуск приложения и чтение данных тегов на Android и iOS
Сообщение
Anonymous » 04 ноя 2024, 18:15
Я создаю приложение Xamarin.Forms, в котором при помещении карты NFC в телефон приложение должно запускаться автоматически и немедленно считывать все данные тега (по крайней мере идентификатор и полезные данные) за один цикл.На некоторых устройствах Android приложение запускается должным образом, но OnNewIntent в MainActivity не запускается. И оно также запускает мое приложение без чтения каких-либо данных. Почему-то метка не обнаруживается.
На других телефонах Android приложение вообще не запускается при помещении карты NFC в устройство.
Я использую следующий код, адаптированный из этого проекта, но в него внесены существенные изменения.
Мой
первый вопрос: как я могу обеспечить надежный запуск приложения и чтение данных на устройствах Android? Нужны ли какие-либо изменения, чтобы обеспечить согласованность срабатывания OnNewIntent?
Мой
второй вопрос касается достижения такого же поведения на iOS. Можно ли запустить приложение и прочитать данные NFC за один цикл на iOS с помощью Xamarin.Forms, учитывая ограничения iOS NFC?
NativeNFCAdapterService
Код: Выделить всё
using System;
using System.Linq;
using System.Threading.Tasks;
using NfcAdapter = Android.Nfc.NfcAdapter;
using Android.Content;
using Android.Nfc.Tech;
using Android.Nfc;
using Android.OS;
using NFCTestApp.Interfaces;
using NFCTestApp.Droid.Services;
using Xamarin.Essentials;
using Xamarin.Forms;
using NFCTestApp.Droid.Enums;
using System.IO;
[assembly: Dependency(typeof(NativeNFCAdapterService))]
namespace NFCTestApp.Droid.Services
{
class NativeNFCAdapterService : INfcAdapter
{
private readonly MainActivity mainActivity = (MainActivity)Platform.CurrentActivity;
private Lazy lazynfcAdapter = new Lazy(() => NfcAdapter.GetDefaultAdapter(Platform.CurrentActivity));
private NfcAdapter NfcAdapter => lazynfcAdapter.Value;
private PendingIntent pendingIntent;
private IntentFilter[] writeTagFilters;
private string[][] techList;
private ReaderCallback readerCallback;
public event Action TagDiscovered;
public event Action AllDataRead;
private NfcStatus NfcStatus => NfcAdapter == null ?
NfcStatus.Unavailable : NfcAdapter.IsEnabled ?
NfcStatus.Enabled : NfcStatus.Disabled;
public static Tag DetectedTag { get; set; }
public NativeNFCAdapterService()
{
Platform.ActivityStateChanged += Platform_ActivityStateChanged;
}
private void Platform_ActivityStateChanged(object sender, ActivityStateChangedEventArgs e)
{
switch (e.State)
{
case ActivityState.Resumed:
EnableForegroundDispatch();
break;
case ActivityState.Paused:
DisableForegroundDispatch();
break;
}
}
public void ConfigureNfcAdapter()
{
IntentFilter tagdiscovered = new IntentFilter(NfcAdapter.ActionTagDiscovered);
IntentFilter ndefDiscovered = new IntentFilter(NfcAdapter.ActionNdefDiscovered);
IntentFilter techDiscovered = new IntentFilter(NfcAdapter.ActionTechDiscovered);
tagdiscovered.AddCategory(Intent.CategoryDefault);
ndefDiscovered.AddCategory(Intent.CategoryDefault);
techDiscovered.AddCategory(Intent.CategoryDefault);
var intent = new Intent(mainActivity, mainActivity.Class).AddFlags(ActivityFlags.SingleTop);
pendingIntent = PendingIntent.GetActivity(mainActivity, 0, intent, PendingIntentFlags.Immutable);
techList = new string[][]
{
new string[] { nameof(NfcA) },
new string[] { nameof(NfcB) },
new string[] { nameof(NfcF) },
new string[] { nameof(NfcV) },
new string[] { nameof(IsoDep) },
new string[] { nameof(NdefFormatable) },
new string[] { nameof(MifareClassic) },
new string[] { nameof(MifareUltralight) },
};
readerCallback = new ReaderCallback();
readerCallback.OnTagDiscoveredEvent += HandleTagDiscovered;
writeTagFilters = new IntentFilter[] { tagdiscovered, ndefDiscovered, techDiscovered };
}
public void DisableForegroundDispatch()
{
NfcAdapter?.DisableForegroundDispatch(Platform.CurrentActivity);
NfcAdapter?.DisableReaderMode(Platform.CurrentActivity);
}
public void EnableForegroundDispatch()
{
if (pendingIntent == null || writeTagFilters == null || techList == null) { return; }
NfcAdapter?.EnableForegroundDispatch(Platform.CurrentActivity, pendingIntent, writeTagFilters, techList);
NfcAdapter?.EnableReaderMode(Platform.CurrentActivity, readerCallback, NfcReaderFlags.NfcA, null);
Task.Run(async () => await ReadAllTagInfoAsync());
}
public void UnconfigureNfcAdapter()
{
Platform.ActivityStateChanged -= Platform_ActivityStateChanged;
}
public void HandleTagDiscovered(string tagId)
{
TagDiscovered?.Invoke(tagId);
}
public async Task SendAsync(byte[] bytes)
{
Ndef ndef = null;
try
{
if (DetectedTag == null)
DetectedTag = await GetDetectedTag();
ndef = Ndef.Get(DetectedTag);
if (ndef == null) return;
if (!ndef.IsWritable)
{
await Application.Current.MainPage.DisplayAlert("Error", "Tag is readonly", "Ok");
return;
}
if (!ndef.IsConnected)
{
await ndef.ConnectAsync();
}
await WriteToTag(ndef, bytes);
}
catch (IOException)
{
await Application.Current.MainPage.DisplayAlert("Error", "Transmission error - possibly due to movement.", "Ok");
}
catch (Exception)
{
await Application.Current.MainPage.DisplayAlert("Error", "Request error", "Ok");
}
finally
{
if (ndef?.IsConnected == true) ndef.Close();
ndef = null;
DetectedTag = null;
}
}
private async Task GetDetectedTag()
{
mainActivity.NfcTag = new TaskCompletionSource();
readerCallback.NFCTag = new TaskCompletionSource();
var tagDetectionTask = await Task.WhenAny(mainActivity.NfcTag.Task, readerCallback.NFCTag.Task);
return await tagDetectionTask;
}
private async Task WriteToTag(Ndef ndef, byte[] chunkedBytes)
{
var ndefRecord = new NdefRecord(NdefRecord.TnfWellKnown, NdefRecord.RtdText?.ToArray(), Array.Empty(), chunkedBytes);
NdefMessage message = new NdefMessage(new[] { ndefRecord });
ndef.WriteNdefMessage(message);
await Application.Current.MainPage.DisplayAlert("NFC", "Write Successful", "Ok");
}
public async Task ReadAllTagInfoAsync()
{
if (DetectedTag == null)
{
DetectedTag = await GetDetectedTag();
}
var info = new System.Text.StringBuilder();
info.AppendLine("Tech List:");
foreach (var tech in DetectedTag.GetTechList())
{
info.AppendLine($"- {tech}");
}
Ndef ndef = Ndef.Get(DetectedTag);
if (ndef != null)
{
info.AppendLine("NDEF Supported: Yes");
info.AppendLine($"NDEF Type: {ndef.Type}");
info.AppendLine($"Is Writable: {ndef.IsWritable}");
info.AppendLine($"Max Size: {ndef.MaxSize} bytes");
var ndefMessage = ndef.CachedNdefMessage;
if (ndefMessage != null && ndefMessage.GetRecords().Any())
{
foreach (var ndefRecord in ndefMessage.GetRecords())
{
info.AppendLine($"Payload: {System.Text.Encoding.UTF8.GetString(ndefRecord.GetPayload())}");
}
}
}
else
{
info.AppendLine("NDEF Supported: No");
}
AllDataRead?.Invoke(info.ToString());
return info.ToString();
}
}
}
ReaderCallback.cs
Код: Выделить всё
public class ReaderCallback : Java.Lang.Object, NfcAdapter.IReaderCallback
{
public TaskCompletionSource NFCTag { get; set; }
public event Action OnTagDiscoveredEvent;
public void OnTagDiscovered(Tag tag)
{
var isSuccess = NFCTag?.TrySetResult(tag);
if (NFCTag == null || !isSuccess.Value)
NativeNFCAdapterService.DetectedTag = tag;
byte[] tagIdBytes = tag.GetId();
string tagId = BitConverter.ToString(tagIdBytes).Replace("-", "");
OnTagDiscoveredEvent?.Invoke(tagId);
}
}
MainActivity.cs
Код: Выделить всё
[MetaData(NfcAdapter.ActionTechDiscovered, Resource = "@xml/nfc_tech_filter")]
[IntentFilter(new[] { NfcAdapter.ActionTechDiscovered }, Categories = new[] { Intent.CategoryDefault }, DataMimeType = "text/plain")]
[IntentFilter(new[] { NfcAdapter.ActionNdefDiscovered }, Categories = new[] { Intent.CategoryDefault }, DataMimeType = "text/plain")]
[IntentFilter(new[] { NfcAdapter.ActionTagDiscovered }, Categories = new[] { Intent.CategoryDefault }, DataMimeType = "text/plain")]
[Activity(Label = "NFCTestApp", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public TaskCompletionSource NfcTag { get; set; }
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
protected override void OnNewIntent(Intent intent)
{
System.Diagnostics.Debug.WriteLine("It is here");
base.OnNewIntent(intent);
Xamarin.Forms.Device.BeginInvokeOnMainThread(async () =>
{
await Xamarin.Forms.Application.Current.MainPage.DisplayAlert("NFC Tag Discovered", "A1", "OK");
});
}
}
AndroidManifest.xml
вот информация о карте
Подробнее здесь:
https://stackoverflow.com/questions/791 ... id-and-ios
1730733339
Anonymous
Я создаю приложение Xamarin.Forms, в котором при помещении карты NFC в телефон приложение должно запускаться автоматически и немедленно считывать все данные тега (по крайней мере идентификатор и полезные данные) за один цикл.На некоторых устройствах Android приложение запускается должным образом, но OnNewIntent в MainActivity не запускается. И оно также запускает мое приложение без чтения каких-либо данных. Почему-то метка не обнаруживается. На других телефонах Android приложение вообще не запускается при помещении карты NFC в устройство. Я использую следующий код, адаптированный из этого проекта, но в него внесены существенные изменения. Мой [b]первый[/b] вопрос: как я могу обеспечить надежный запуск приложения и чтение данных на устройствах Android? Нужны ли какие-либо изменения, чтобы обеспечить согласованность срабатывания OnNewIntent? Мой [b]второй[/b] вопрос касается достижения такого же поведения на iOS. Можно ли запустить приложение и прочитать данные NFC за один цикл на iOS с помощью Xamarin.Forms, учитывая ограничения iOS NFC? NativeNFCAdapterService [code]using System; using System.Linq; using System.Threading.Tasks; using NfcAdapter = Android.Nfc.NfcAdapter; using Android.Content; using Android.Nfc.Tech; using Android.Nfc; using Android.OS; using NFCTestApp.Interfaces; using NFCTestApp.Droid.Services; using Xamarin.Essentials; using Xamarin.Forms; using NFCTestApp.Droid.Enums; using System.IO; [assembly: Dependency(typeof(NativeNFCAdapterService))] namespace NFCTestApp.Droid.Services { class NativeNFCAdapterService : INfcAdapter { private readonly MainActivity mainActivity = (MainActivity)Platform.CurrentActivity; private Lazy lazynfcAdapter = new Lazy(() => NfcAdapter.GetDefaultAdapter(Platform.CurrentActivity)); private NfcAdapter NfcAdapter => lazynfcAdapter.Value; private PendingIntent pendingIntent; private IntentFilter[] writeTagFilters; private string[][] techList; private ReaderCallback readerCallback; public event Action TagDiscovered; public event Action AllDataRead; private NfcStatus NfcStatus => NfcAdapter == null ? NfcStatus.Unavailable : NfcAdapter.IsEnabled ? NfcStatus.Enabled : NfcStatus.Disabled; public static Tag DetectedTag { get; set; } public NativeNFCAdapterService() { Platform.ActivityStateChanged += Platform_ActivityStateChanged; } private void Platform_ActivityStateChanged(object sender, ActivityStateChangedEventArgs e) { switch (e.State) { case ActivityState.Resumed: EnableForegroundDispatch(); break; case ActivityState.Paused: DisableForegroundDispatch(); break; } } public void ConfigureNfcAdapter() { IntentFilter tagdiscovered = new IntentFilter(NfcAdapter.ActionTagDiscovered); IntentFilter ndefDiscovered = new IntentFilter(NfcAdapter.ActionNdefDiscovered); IntentFilter techDiscovered = new IntentFilter(NfcAdapter.ActionTechDiscovered); tagdiscovered.AddCategory(Intent.CategoryDefault); ndefDiscovered.AddCategory(Intent.CategoryDefault); techDiscovered.AddCategory(Intent.CategoryDefault); var intent = new Intent(mainActivity, mainActivity.Class).AddFlags(ActivityFlags.SingleTop); pendingIntent = PendingIntent.GetActivity(mainActivity, 0, intent, PendingIntentFlags.Immutable); techList = new string[][] { new string[] { nameof(NfcA) }, new string[] { nameof(NfcB) }, new string[] { nameof(NfcF) }, new string[] { nameof(NfcV) }, new string[] { nameof(IsoDep) }, new string[] { nameof(NdefFormatable) }, new string[] { nameof(MifareClassic) }, new string[] { nameof(MifareUltralight) }, }; readerCallback = new ReaderCallback(); readerCallback.OnTagDiscoveredEvent += HandleTagDiscovered; writeTagFilters = new IntentFilter[] { tagdiscovered, ndefDiscovered, techDiscovered }; } public void DisableForegroundDispatch() { NfcAdapter?.DisableForegroundDispatch(Platform.CurrentActivity); NfcAdapter?.DisableReaderMode(Platform.CurrentActivity); } public void EnableForegroundDispatch() { if (pendingIntent == null || writeTagFilters == null || techList == null) { return; } NfcAdapter?.EnableForegroundDispatch(Platform.CurrentActivity, pendingIntent, writeTagFilters, techList); NfcAdapter?.EnableReaderMode(Platform.CurrentActivity, readerCallback, NfcReaderFlags.NfcA, null); Task.Run(async () => await ReadAllTagInfoAsync()); } public void UnconfigureNfcAdapter() { Platform.ActivityStateChanged -= Platform_ActivityStateChanged; } public void HandleTagDiscovered(string tagId) { TagDiscovered?.Invoke(tagId); } public async Task SendAsync(byte[] bytes) { Ndef ndef = null; try { if (DetectedTag == null) DetectedTag = await GetDetectedTag(); ndef = Ndef.Get(DetectedTag); if (ndef == null) return; if (!ndef.IsWritable) { await Application.Current.MainPage.DisplayAlert("Error", "Tag is readonly", "Ok"); return; } if (!ndef.IsConnected) { await ndef.ConnectAsync(); } await WriteToTag(ndef, bytes); } catch (IOException) { await Application.Current.MainPage.DisplayAlert("Error", "Transmission error - possibly due to movement.", "Ok"); } catch (Exception) { await Application.Current.MainPage.DisplayAlert("Error", "Request error", "Ok"); } finally { if (ndef?.IsConnected == true) ndef.Close(); ndef = null; DetectedTag = null; } } private async Task GetDetectedTag() { mainActivity.NfcTag = new TaskCompletionSource(); readerCallback.NFCTag = new TaskCompletionSource(); var tagDetectionTask = await Task.WhenAny(mainActivity.NfcTag.Task, readerCallback.NFCTag.Task); return await tagDetectionTask; } private async Task WriteToTag(Ndef ndef, byte[] chunkedBytes) { var ndefRecord = new NdefRecord(NdefRecord.TnfWellKnown, NdefRecord.RtdText?.ToArray(), Array.Empty(), chunkedBytes); NdefMessage message = new NdefMessage(new[] { ndefRecord }); ndef.WriteNdefMessage(message); await Application.Current.MainPage.DisplayAlert("NFC", "Write Successful", "Ok"); } public async Task ReadAllTagInfoAsync() { if (DetectedTag == null) { DetectedTag = await GetDetectedTag(); } var info = new System.Text.StringBuilder(); info.AppendLine("Tech List:"); foreach (var tech in DetectedTag.GetTechList()) { info.AppendLine($"- {tech}"); } Ndef ndef = Ndef.Get(DetectedTag); if (ndef != null) { info.AppendLine("NDEF Supported: Yes"); info.AppendLine($"NDEF Type: {ndef.Type}"); info.AppendLine($"Is Writable: {ndef.IsWritable}"); info.AppendLine($"Max Size: {ndef.MaxSize} bytes"); var ndefMessage = ndef.CachedNdefMessage; if (ndefMessage != null && ndefMessage.GetRecords().Any()) { foreach (var ndefRecord in ndefMessage.GetRecords()) { info.AppendLine($"Payload: {System.Text.Encoding.UTF8.GetString(ndefRecord.GetPayload())}"); } } } else { info.AppendLine("NDEF Supported: No"); } AllDataRead?.Invoke(info.ToString()); return info.ToString(); } } } [/code] ReaderCallback.cs [code]public class ReaderCallback : Java.Lang.Object, NfcAdapter.IReaderCallback { public TaskCompletionSource NFCTag { get; set; } public event Action OnTagDiscoveredEvent; public void OnTagDiscovered(Tag tag) { var isSuccess = NFCTag?.TrySetResult(tag); if (NFCTag == null || !isSuccess.Value) NativeNFCAdapterService.DetectedTag = tag; byte[] tagIdBytes = tag.GetId(); string tagId = BitConverter.ToString(tagIdBytes).Replace("-", ""); OnTagDiscoveredEvent?.Invoke(tagId); } } [/code] MainActivity.cs [code][MetaData(NfcAdapter.ActionTechDiscovered, Resource = "@xml/nfc_tech_filter")] [IntentFilter(new[] { NfcAdapter.ActionTechDiscovered }, Categories = new[] { Intent.CategoryDefault }, DataMimeType = "text/plain")] [IntentFilter(new[] { NfcAdapter.ActionNdefDiscovered }, Categories = new[] { Intent.CategoryDefault }, DataMimeType = "text/plain")] [IntentFilter(new[] { NfcAdapter.ActionTagDiscovered }, Categories = new[] { Intent.CategoryDefault }, DataMimeType = "text/plain")] [Activity(Label = "NFCTestApp", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)] public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity { public TaskCompletionSource NfcTag { get; set; } protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); Xamarin.Essentials.Platform.Init(this, savedInstanceState); global::Xamarin.Forms.Forms.Init(this, savedInstanceState); LoadApplication(new App()); } protected override void OnNewIntent(Intent intent) { System.Diagnostics.Debug.WriteLine("It is here"); base.OnNewIntent(intent); Xamarin.Forms.Device.BeginInvokeOnMainThread(async () => { await Xamarin.Forms.Application.Current.MainPage.DisplayAlert("NFC Tag Discovered", "A1", "OK"); }); } } [/code] AndroidManifest.xml [code] [/code] вот информация о карте [img]https://i.sstatic.net/ZLnqPPpm.jpg[/img] Подробнее здесь: [url]https://stackoverflow.com/questions/79155186/xamarin-forms-nfc-automatically-launch-app-and-read-tag-data-on-android-and-ios[/url]