Release v1.0.3: Enhanced NDEF reading and UX improvements

Major Improvements:
- Implement exclusive card access with retry logic and exponential backoff
- Fix Type 4 NDEF length parsing (now correctly reads NTAG424 and similar chips)
- Add enhanced card type detection (Type 2 vs Type 4)
- Implement chunked reading for large NDEF messages
- Add proper TLV parsing for Type 2 tags

Bug Fixes:
- Fix WPF window lifecycle issue (visual tree error on reopen)
- Fix NDEF length parsing incorrectly detecting extended format
- Correct data offset for Type 4 tag reading

New Features:
- Multi-line log selection and copy to clipboard
- Context menu with Copy Selected Lines, Copy All, Select All
- Runtime version roll-forward support (.NET 8.0.x compatibility)

Technical Details:
- Type 4 tags now use correct 2-byte NLEN field per NFC Forum spec
- Removed incorrect 3-byte extended length detection
- Window now hides instead of closing for proper tray app behavior
- Connection attempts exclusive access first, falls back to shared mode
- Status timeout increased from 0ms to 1000ms for better card detection
This commit is contained in:
2025-11-18 22:20:13 -08:00
parent 308966da36
commit a1d03fec2a
6 changed files with 585 additions and 31 deletions

View File

@@ -1,7 +1,11 @@
using System.ComponentModel;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using NfcActions.Services;
using NfcActions.ViewModels;
namespace NfcActions;
@@ -39,4 +43,84 @@ public partial class MainWindow : Window
// Silently fail if browser can't be opened
}
}
private void CopySelectedLogs_Click(object sender, RoutedEventArgs e)
{
try
{
var selectedItems = LogListBox.SelectedItems.Cast<LogEntry>().ToList();
if (selectedItems.Count == 0)
{
MessageBox.Show("No log entries selected. Please select one or more lines first.",
"No Selection",
MessageBoxButton.OK,
MessageBoxImage.Information);
return;
}
var logText = new StringBuilder();
foreach (var entry in selectedItems)
{
logText.AppendLine(entry.FormattedMessage);
}
Clipboard.SetText(logText.ToString());
MessageBox.Show($"Copied {selectedItems.Count} log {(selectedItems.Count == 1 ? "entry" : "entries")} to clipboard.",
"Copied",
MessageBoxButton.OK,
MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Failed to copy to clipboard: {ex.Message}",
"Error",
MessageBoxButton.OK,
MessageBoxImage.Error);
}
}
private void CopyAllLogs_Click(object sender, RoutedEventArgs e)
{
try
{
if (DataContext is MainViewModel viewModel)
{
var allEntries = viewModel.LogEntries.ToList();
if (allEntries.Count == 0)
{
MessageBox.Show("No log entries available.",
"Empty Log",
MessageBoxButton.OK,
MessageBoxImage.Information);
return;
}
var logText = new StringBuilder();
foreach (var entry in allEntries)
{
logText.AppendLine(entry.FormattedMessage);
}
Clipboard.SetText(logText.ToString());
MessageBox.Show($"Copied all {allEntries.Count} log entries to clipboard.",
"Copied",
MessageBoxButton.OK,
MessageBoxImage.Information);
}
}
catch (Exception ex)
{
MessageBox.Show($"Failed to copy to clipboard: {ex.Message}",
"Error",
MessageBoxButton.OK,
MessageBoxImage.Error);
}
}
private void SelectAllLogs_Click(object sender, RoutedEventArgs e)
{
LogListBox.SelectAll();
}
}