diff --git a/OSDP-Bench.sln.DotSettings b/OSDP-Bench.sln.DotSettings index a7fc0a9..ec3bac6 100644 --- a/OSDP-Bench.sln.DotSettings +++ b/OSDP-Bench.sln.DotSettings @@ -1,4 +1,5 @@  CRC + .XX True True \ No newline at end of file diff --git a/README.md b/README.md index 50852ee..cab7b5a 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,7 @@ A professional tool for configuring and troubleshooting OSDP devices. [![.NET](https://img.shields.io/badge/.NET-8.0-blue)](https://dotnet.microsoft.com/) -[![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](LICENSE) -[![Platform](https://img.shields.io/badge/Platform-Windows-lightgrey)](https://docs.microsoft.com/en-us/windows/) +[![License](https://img.shields.io/badge/License-Eclipse%202.0-green.svg)](LICENSE) ## About @@ -21,6 +20,15 @@ Core functionality is under an open source license to help increase the adoption - **Multi-language Support** - Available in multiple languages - **Cross-platform** - Built on .NET 8.0 for modern compatibility +## Get OSDP Bench + +### Download the App + +OSDP Bench is available for purchase on multiple platforms: + +[![Microsoft Store](https://get.microsoft.com/images/en-us%20dark.svg)](ms-windows-store://pdp/?productid=9N3W7QR3R5S7&cid=&mode=mini) +[![Google Play](src/Assets/google-play.svg)](https://play.google.com/store/apps/details?id=com.z_bitco.com.osdpbenchmobile) + ## Getting Started ### Prerequisites @@ -29,7 +37,7 @@ Core functionality is under an open source license to help increase the adoption - Windows 10/11 (for WinUI version) - Serial port access for device communication -### Installation +### Building from Source 1. Clone the repository: ```bash @@ -84,9 +92,27 @@ For documentation contributions: 3. Update this README to include the new file 4. Follow the existing documentation style and structure +### Release Process + +To create a new release: + +1. Ensure you're on the `develop` branch with all changes committed +2. Run the release script: + ```powershell + ./ci/release.ps1 + ``` +3. The script will: + - Verify no uncommitted changes exist + - Check that develop is ahead of main + - Display changes to be released + - Merge develop into main (with confirmation) + - Push to trigger automated CI/CD pipeline + +The CI pipeline will automatically handle version bumping and release creation. + ## License -This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details. +This project is licensed under the Eclipse Public License 2.0 - see the [LICENSE](LICENSE) file for details. ## Contact diff --git a/ci/azure-pipeline-resource-check.yml b/ci/azure-pipeline-resource-check.yml new file mode 100644 index 0000000..f5a3063 --- /dev/null +++ b/ci/azure-pipeline-resource-check.yml @@ -0,0 +1,65 @@ +# Azure DevOps Pipeline for Resource Usage Analysis +# This pipeline runs the PowerShell resource usage checker + +trigger: + branches: + include: + - main + - develop + paths: + include: + - 'src/Core/Resources/*' + - 'src/**/*.cs' + - 'src/**/*.xaml' + +pr: + branches: + include: + - main + - develop + paths: + include: + - 'src/Core/Resources/*' + - 'src/**/*.cs' + - 'src/**/*.xaml' + +pool: + vmImage: 'windows-latest' + +variables: + buildConfiguration: 'Release' + +steps: +- checkout: self + displayName: 'Checkout source code' + +- task: PowerShell@2 + displayName: 'Check Resource Usage' + inputs: + filePath: 'check_resource_usage.ps1' + arguments: '-Verbose' + pwsh: true + workingDirectory: '$(Build.SourcesDirectory)' + continueOnError: true + +- task: PowerShell@2 + displayName: 'Resource Check Summary' + inputs: + targetType: 'inline' + script: | + Write-Host "##[section]Resource Usage Analysis Complete" + Write-Host "##[command]Review the output above for any unused resources or missing definitions" + Write-Host "##[command]Consider cleaning up unused resources to maintain code quality" + pwsh: true + condition: always() + +# Optional: Fail the build if critical issues are found +# Uncomment the following task if you want the pipeline to fail on resource issues +# - task: PowerShell@2 +# displayName: 'Fail on Critical Resource Issues' +# inputs: +# filePath: 'check_resource_usage.ps1' +# arguments: '-Verbose' +# pwsh: true +# workingDirectory: '$(Build.SourcesDirectory)' +# condition: always() \ No newline at end of file diff --git a/ci/check_resource_usage.ps1 b/ci/check_resource_usage.ps1 new file mode 100644 index 0000000..da5a27b --- /dev/null +++ b/ci/check_resource_usage.ps1 @@ -0,0 +1,239 @@ +# OSDP-Bench Resource Usage Checker (With Progress Indicator) + +Write-Host "=== OSDP-Bench Resource Usage Analysis ===" -ForegroundColor Cyan +Write-Host + +$ResourceFile = "src/Core/Resources/Resources.resx" + +# Phase 1: Extract resource keys +Write-Host "Phase 1: Extracting resource keys from Resources.resx..." -ForegroundColor Yellow + +if (-not (Test-Path $ResourceFile)) { + Write-Host "Error: Resources.resx not found at $ResourceFile" -ForegroundColor Red + exit 1 +} + +[xml]$resourceXml = Get-Content $ResourceFile +$resourceKeys = $resourceXml.root.data | Where-Object { $_.name } | Select-Object -ExpandProperty name | Sort-Object + +Write-Host "Found $($resourceKeys.Count) resource keys" -ForegroundColor Green +Write-Host + +# Phase 2: Check usage with progress +Write-Host "Phase 2: Checking usage of each resource key..." -ForegroundColor Yellow + +$usedKeys = @() +$unusedKeys = @() +$total = $resourceKeys.Count +$current = 0 + +# Check if ripgrep is available for faster processing +$rgAvailable = $null -ne (Get-Command "rg" -ErrorAction SilentlyContinue) +if ($rgAvailable) { + Write-Host "Using ripgrep for fast searching..." -ForegroundColor Gray +} else { + Write-Host "Using PowerShell Select-String (slower fallback)..." -ForegroundColor Gray +} + +foreach ($key in $resourceKeys) { + $current++ + $percentComplete = [math]::Round(($current / $total) * 100, 1) + + # Update progress bar + Write-Progress -Activity "Checking Resource Usage" -Status "Processing $key ($current of $total)" -PercentComplete $percentComplete + + $found = $false + + if ($rgAvailable) { + # Use ripgrep patterns - simplified for speed + try { + & rg -q $key "src" "test" --glob="*.cs" --glob="*.xaml" 2>$null + if ($LASTEXITCODE -eq 0) { + $found = $true + } + } + catch { + # Continue if rg fails + } + } + else { + # Fallback to Select-String + $files = Get-ChildItem -Path "src", "test" -Recurse -Include "*.cs", "*.xaml" -ErrorAction SilentlyContinue + + if ($files | Select-String -Pattern $key -Quiet) { + $found = $true + } + } + + if ($found) { + $usedKeys += $key + Write-Host "✓ $key" -ForegroundColor Green + } + else { + $unusedKeys += $key + Write-Host "✗ $key" -ForegroundColor Red + } +} + +# Clear progress bar +Write-Progress -Activity "Checking Resource Usage" -Completed + +Write-Host +Write-Host "=== SUMMARY ===" -ForegroundColor Cyan +Write-Host "Used resource keys: $($usedKeys.Count)" -ForegroundColor Green +Write-Host "Unused resource keys: $($unusedKeys.Count)" -ForegroundColor Red + +if ($unusedKeys.Count -gt 0) { + Write-Host + Write-Host "=== UNUSED RESOURCE KEYS ===" -ForegroundColor Yellow + + foreach ($key in $unusedKeys) { + $dataNode = $resourceXml.root.data | Where-Object { $_.name -eq $key } + $value = if ($dataNode.value) { $dataNode.value } else { "" } + $comment = if ($dataNode.comment) { $dataNode.comment } else { "" } + + Write-Host $key -ForegroundColor Red + Write-Host " Value: `"$value`"" + if ($comment) { + Write-Host " Comment: $comment" + } + Write-Host + } + + Write-Host "Consider removing these unused resource strings to clean up the codebase." -ForegroundColor Yellow +} +else { + Write-Host "All resource strings are being used! 🎉" -ForegroundColor Green +} + +Write-Host + +# Phase 3: Check for missing resource definitions +Write-Host "Phase 3: Checking for resource lookups without definitions..." -ForegroundColor Yellow + +$missingKeys = @() +$missingDetails = @() + +# Get all source files +$allFiles = Get-ChildItem -Path "src", "test" -Recurse -Include "*.cs", "*.xaml" -ErrorAction SilentlyContinue + +Write-Host "Found $($allFiles.Count) source files to scan..." -ForegroundColor Gray +Write-Progress -Activity "Phase 3: Scanning for Missing Definitions" -Status "Initializing scan..." -PercentComplete 0 + +$fileCount = 0 +$totalFiles = $allFiles.Count + +foreach ($file in $allFiles) { + $fileCount++ + $percentComplete = [math]::Round(($fileCount / $totalFiles) * 100, 1) + + Write-Progress -Activity "Phase 3: Scanning for Missing Definitions" -Status "Scanning $($file.Name) ($fileCount of $totalFiles) - $($percentComplete)%" -PercentComplete $percentComplete + + # Show periodic status updates + if ($fileCount % 10 -eq 0 -or $fileCount -eq $totalFiles) { + Write-Host " Processed $fileCount of $totalFiles files..." -ForegroundColor Gray + } + + try { + $content = Get-Content $file.FullName -Raw -ErrorAction SilentlyContinue + if (-not $content) { continue } + + # Pattern 1: {markup:Localize KeyName} + $matches = [regex]::Matches($content, '\{markup:Localize\s+([^}]+)\}') + foreach ($match in $matches) { + $key = $match.Groups[1].Value.Trim() + if ($key -notin $resourceKeys -and $key -notin $missingKeys) { + $missingKeys += $key + $missingDetails += @{ + Key = $key + File = $file.Name + Pattern = "{markup:Localize $key}" + } + } + } + + # Pattern 2: GetString("KeyName") + $matches = [regex]::Matches($content, 'GetString\s*\(\s*"([^"]+)"\s*\)') + foreach ($match in $matches) { + $key = $match.Groups[1].Value + if ($key -notin $resourceKeys -and $key -notin $missingKeys) { + $missingKeys += $key + $missingDetails += @{ + Key = $key + File = $file.Name + Pattern = "GetString(`"$key`")" + } + } + } + + # Pattern 3: Resources.KeyName (exclude common properties) + $matches = [regex]::Matches($content, 'Resources\.([A-Za-z_][A-Za-z0-9_]*)') + foreach ($match in $matches) { + $key = $match.Groups[1].Value + if ($key -notin @("ResourceManager", "Culture", "GetString", "Resources") -and + $key -notin $resourceKeys -and $key -notin $missingKeys) { + $missingKeys += $key + $missingDetails += @{ + Key = $key + File = $file.Name + Pattern = "Resources.$key" + } + } + } + } + catch { + # Skip files that can't be processed + continue + } +} + +# Clear progress bar +Write-Progress -Activity "Phase 3: Scanning for Missing Definitions" -Completed +Write-Host "Completed scanning $totalFiles files for missing definitions." -ForegroundColor Gray + +# Filter out common false positives +$actualMissingKeys = $missingKeys | Where-Object { + $_ -notin @("AssemblyAssociatedContentFileAttribute", "GetString", "Resources") -and + $_ -ne "" +} | Sort-Object -Unique + +if ($actualMissingKeys.Count -gt 0) { + Write-Host + Write-Host "=== MISSING RESOURCE DEFINITIONS ===" -ForegroundColor Yellow + Write-Host "Found $($actualMissingKeys.Count) resource lookups without definitions:" -ForegroundColor Red + Write-Host + + foreach ($key in $actualMissingKeys) { + Write-Host "Missing: $key" -ForegroundColor Red + # Show where it's referenced + $keyDetails = $missingDetails | Where-Object { $_.Key -eq $key } + foreach ($detail in $keyDetails) { + Write-Host " Used in: $($detail.File)" -ForegroundColor Blue -NoNewline + Write-Host " as $($detail.Pattern)" + } + Write-Host + } + + Write-Host "These resource keys are referenced in code but not defined in Resources.resx" -ForegroundColor Yellow +} +else { + Write-Host "All resource lookups have corresponding definitions! ✓" -ForegroundColor Green +} + +Write-Host +Write-Host "=== FINAL SUMMARY ===" -ForegroundColor Cyan +Write-Host "Used resource keys: $($usedKeys.Count)" -ForegroundColor Green +Write-Host "Unused resource keys: $($unusedKeys.Count)" -ForegroundColor Red +Write-Host "Missing definitions: $($actualMissingKeys.Count)" -ForegroundColor Red + +if ($unusedKeys.Count -gt 0 -or $actualMissingKeys.Count -gt 0) { + Write-Host + Write-Host "⚠️ Resource issues found. Consider cleaning up unused resources and adding missing definitions." -ForegroundColor Yellow +} +else { + Write-Host + Write-Host "✅ All resource strings are properly managed!" -ForegroundColor Green +} + +Write-Host +Write-Host "Analysis complete." -ForegroundColor Cyan \ No newline at end of file diff --git a/ci/release.ps1 b/ci/release.ps1 new file mode 100644 index 0000000..3122ff6 --- /dev/null +++ b/ci/release.ps1 @@ -0,0 +1,77 @@ +#!/usr/bin/env pwsh + +# OSDP-Bench Release Script +# Merges develop into main to trigger CI version bump and release + +Write-Host "OSDP-Bench Release Process" -ForegroundColor Cyan +Write-Host "==========================" -ForegroundColor Cyan +Write-Host "" + +# Ensure we have latest changes +Write-Host "Fetching latest changes..." -ForegroundColor Yellow +git fetch --all + +# Check if there are uncommitted changes +$uncommittedChanges = git status -s +if ($uncommittedChanges) { + Write-Host "Error: You have uncommitted changes. Please commit or stash them before releasing." -ForegroundColor Red + exit 1 +} + +# Ensure we're on develop branch +$currentBranch = git rev-parse --abbrev-ref HEAD +if ($currentBranch -ne "develop") { + Write-Host "Error: You must be on the develop branch to release. Currently on: $currentBranch" -ForegroundColor Red + exit 1 +} + +# Pull latest develop +Write-Host "Updating develop branch..." -ForegroundColor Yellow +git pull origin develop + +# Check if develop is ahead of main +$aheadCount = git rev-list --count origin/main..origin/develop +if ($aheadCount -eq 0) { + Write-Host "Error: develop branch is not ahead of main. Nothing to release." -ForegroundColor Red + exit 1 +} + +Write-Host "" +Write-Host "Changes to be released:" -ForegroundColor Green +git log --oneline --no-merges origin/main..origin/develop + +Write-Host "" +$confirm = Read-Host "Do you want to proceed with the release? (y/n)" +if ($confirm -ne "y") { + Write-Host "Release cancelled." -ForegroundColor Yellow + exit 0 +} + +# Checkout main +Write-Host "Checking out main branch..." -ForegroundColor Yellow +git checkout main + +# Pull latest main +Write-Host "Updating main branch..." -ForegroundColor Yellow +git pull origin main + +# Merge develop +Write-Host "Merging develop into main..." -ForegroundColor Yellow +git merge --no-ff develop -m "Release: Merge develop into main for automated release" + +# Push to remote +Write-Host "Pushing to remote..." -ForegroundColor Yellow +git push origin main + +# Switch back to develop +Write-Host "Switching back to develop branch..." -ForegroundColor Yellow +git checkout develop + +Write-Host "" +Write-Host "Release process completed successfully!" -ForegroundColor Green +Write-Host "The CI pipeline will automatically:" -ForegroundColor Green +Write-Host "1. Run tests" -ForegroundColor Green +Write-Host "2. Bump version number" -ForegroundColor Green +Write-Host "3. Create a tagged release" -ForegroundColor Green +Write-Host "" +Write-Host "You can monitor the release progress in Azure DevOps." -ForegroundColor Green \ No newline at end of file diff --git a/ci/release.sh b/ci/release.sh deleted file mode 100755 index 7255a61..0000000 --- a/ci/release.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/bash - -# OSDP-Bench Release Script -# Merges develop into main to trigger CI version bump and release - -echo "OSDP-Bench Release Process" -echo "==========================" -echo "" - -# Ensure we have latest changes -echo "Fetching latest changes..." -git fetch --all - -# Check if there are uncommitted changes -if [[ -n $(git status -s) ]]; then - echo "Error: You have uncommitted changes. Please commit or stash them before releasing." - exit 1 -fi - -# Ensure we're on develop branch -CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) -if [[ "$CURRENT_BRANCH" != "develop" ]]; then - echo "Error: You must be on the develop branch to release. Currently on: $CURRENT_BRANCH" - exit 1 -fi - -# Pull latest develop -echo "Updating develop branch..." -git pull origin develop - -# Check if develop is ahead of main -AHEAD_COUNT=$(git rev-list --count origin/main..origin/develop) -if [[ "$AHEAD_COUNT" -eq 0 ]]; then - echo "Error: develop branch is not ahead of main. Nothing to release." - exit 1 -fi - -echo "" -echo "Changes to be released:" -git log --oneline --no-merges origin/main..origin/develop - -echo "" -read -p "Do you want to proceed with the release? (y/n) " CONFIRM -if [[ "$CONFIRM" != "y" ]]; then - echo "Release cancelled." - exit 0 -fi - -# Checkout main -echo "Checking out main branch..." -git checkout main - -# Pull latest main -echo "Updating main branch..." -git pull origin main - -# Merge develop -echo "Merging develop into main..." -git merge --no-ff develop -m "Release: Merge develop into main for automated release" - -# Push to remote -echo "Pushing to remote..." -git push origin main - -# Switch back to develop -echo "Switching back to develop branch..." -git checkout develop - -echo "" -echo "Release process completed successfully!" -echo "The CI pipeline will automatically:" -echo "1. Run tests" -echo "2. Bump version number" -echo "3. Create a tagged release" -echo "" -echo "You can monitor the release progress in Azure DevOps." \ No newline at end of file diff --git a/docs/CLAUDE.md b/docs/CLAUDE.md index b66db58..bb9b0ab 100644 --- a/docs/CLAUDE.md +++ b/docs/CLAUDE.md @@ -14,6 +14,32 @@ - Run specific test: `dotnet test test/Core.Tests/Core.Tests.csproj --filter "Name=ConnectViewModel_InitializedAvailableBaudRates"` - Run with code coverage: `dotnet test test/Core.Tests/Core.Tests.csproj --collect:"XPlat Code Coverage"` +## Resource Management +- Check resource usage: `pwsh ci/check_resource_usage_progress.ps1` +- The script analyzes unused resource strings and missing definitions across all language files +- Shows progress indicators for both resource usage checking and missing definition scanning +- Provides detailed reports with resource values and comments for cleanup decisions +- Can be integrated into Azure DevOps pipelines using `ci/azure-pipeline-resource-check.yml` + +## Release Process +- Create a release: `pwsh ci/release.ps1` +- The script automates the release workflow: + - Validates working directory state (no uncommitted changes) + - Ensures you're on the develop branch + - Fetches latest changes from remote + - Verifies develop is ahead of main + - Shows commits to be released + - Merges develop into main with no-fast-forward + - Pushes to trigger CI/CD pipeline +- The Azure DevOps pipeline will automatically: + - Run tests + - Bump version number + - Create a tagged release +- Requirements: + - Must be on develop branch + - No uncommitted changes + - Develop must be ahead of main + ## Code Style Guidelines - Use C# 8.0+ features with async/await patterns for asynchronous operations - Follow the MVVM design pattern for view models with ObservableObject and RelayCommand @@ -31,9 +57,11 @@ ## UI Style Guidelines - **Always use standard styles** - Apply predefined styles from the design system instead of inline properties - **Use design tokens for spacing** - Reference `{StaticResource Margin.Card}` instead of hardcoding values -- **Apply semantic colors** - Use `{StaticResource Brush.Error}` instead of hardcoded colors like "Red" +- **Apply theme-aware semantic colors** - Use `{DynamicResource SemanticSuccessBrush}` for automatic light/dark theme support - **Follow the style hierarchy** - Check ComponentStyles.xaml and LayoutTemplates.xaml before creating custom styles - **Update existing code** - When modifying files, replace inline styling with standard styles - **Create reusable patterns** - If you find yourself repeating XAML structures, consider adding a new style or template +- **Use WrapPanel for responsive layouts** - When controls should be horizontal on wide screens but wrap to vertical on narrow screens, use WrapPanel instead of fixed Grid layouts +- **Prefer dynamic resources for colors** - Use `{DynamicResource}` instead of `{StaticResource}` for colors to ensure theme compatibility For detailed UI styling guidelines and examples, see: `src/UI/Windows/Styles/StyleGuide.md` diff --git a/docs/SYSTEM_LANGUAGE_CHECK.md b/docs/SYSTEM_LANGUAGE_CHECK.md new file mode 100644 index 0000000..1de7226 --- /dev/null +++ b/docs/SYSTEM_LANGUAGE_CHECK.md @@ -0,0 +1,149 @@ +# System Language Matching + +This document describes the system language matching functionality added to OSDP-Bench. + +## Overview + +The localization service now provides methods to check if the application's current language matches the system language. This can be useful for: +- Notifying users when their app language differs from the system +- Providing an option to sync with system language +- Analytics to understand user language preferences + +## New API Methods + +### `IsSystemLanguageMatch()` +Checks if the current application language matches the system language. + +```csharp +bool isMatch = _localizationService.IsSystemLanguageMatch(); +``` + +Returns `true` if: +- The system language is not supported by the application (no mismatch to report) +- The culture names are exactly the same (e.g., "en-US" == "en-US") +- The base languages match (e.g., "en-US" matches "en-GB") + +### `GetSystemCulture()` +Gets the current system UI culture. + +```csharp +CultureInfo systemCulture = _localizationService.GetSystemCulture(); +``` + +### `IsCultureSupported(CultureInfo culture)` +Checks if a specific culture is supported by the application. + +```csharp +bool isSupported = _localizationService.IsCultureSupported(systemCulture); +``` + +Returns `true` if the culture or its base language is supported by the application. + +## Usage Examples + +### Example 1: Display System Language Mismatch Indicator +```csharp +public class LanguageStatusViewModel : ObservableObject +{ + private readonly ILocalizationService _localizationService; + + public bool IsSystemLanguageMismatch => !_localizationService.IsSystemLanguageMatch(); + + public string SystemLanguageName => _localizationService.GetSystemCulture().NativeName; + + public LanguageStatusViewModel(ILocalizationService localizationService) + { + _localizationService = localizationService; + _localizationService.CultureChanged += (s, e) => OnPropertyChanged(nameof(IsSystemLanguageMismatch)); + } +} +``` + +### Example 2: Prompt User on Language Mismatch +```csharp +public async Task CheckLanguageOnStartupAsync() +{ + if (!_localizationService.IsSystemLanguageMatch()) + { + var systemCulture = _localizationService.GetSystemCulture(); + var result = await _dialogService.ShowMessageAsync( + $"Your system language is {systemCulture.NativeName}. " + + "Would you like to switch the application to match?", + "Language Mismatch", + DialogButtons.YesNo); + + if (result == DialogResult.Yes) + { + _localizationService.ChangeCulture(systemCulture); + } + } +} +``` + +### Example 3: Add "Use System Language" Option +```csharp +public class LanguageSelectionViewModel : ObservableObject +{ + [RelayCommand] + private void UseSystemLanguage() + { + var systemCulture = _localizationService.GetSystemCulture(); + _localizationService.ChangeCulture(systemCulture); + } + + public bool IsUsingSystemLanguage => _localizationService.IsSystemLanguageMatch(); +} +``` + +## Implementation Notes + +1. **System Culture Detection**: Uses `CultureInfo.InstalledUICulture` which represents the OS display language +2. **Flexible Matching**: Considers both exact matches and base language matches +3. **Real-time Updates**: The `IsSystemLanguageMatch()` method always checks against the current system state + +## UI Integration + +The system language prompt has been integrated into the application startup process. Here's how it works: + +### Automatic Detection +- When the application starts, it automatically checks if the system language matches the current app language +- The check only runs for users who have previously set a language preference (not first-time users) +- If the system language is not supported by the app, no prompt is shown + +### User Experience +1. **Language Mismatch Detected**: A dialog appears with the title "System Language Detected" +2. **Informative Message**: Shows "Your system language is [Language Name]. Would you like to switch the application to match your system language?" +3. **User Choice**: User can choose "OK" to switch or "Cancel" to keep current language +4. **Automatic Switching**: If user chooses "OK", the app immediately switches to the best matching supported language + +### Implementation Details +- **Service**: `LanguageMismatchService` handles all the logic +- **Timing**: Check runs 500ms after startup to ensure UI is fully loaded +- **Language Matching**: Uses exact match first, then falls back to base language match (e.g., "en-GB" → "en-US") +- **Persistence**: Language choice is automatically saved to user settings + +## Testing + +To test the system language prompt: + +### Test Case 1: Language Mismatch +1. Set Windows display language to Spanish (Settings > Time & Language > Language) +2. Set OSDP-Bench to English in the Info page +3. Close and relaunch the application +4. You should see the system language prompt asking to switch to Spanish + +### Test Case 2: No Prompt for Unsupported Language +1. Set Windows display language to Italian (not supported by the app) +2. Set OSDP-Bench to English +3. Close and relaunch the application +4. No prompt should appear (Italian is not supported) + +### Test Case 3: First-Time User +1. Delete the settings file (`%APPDATA%\OSDPBench\settings.json`) +2. Launch the application +3. No prompt should appear (first-time users get system language by default) + +### Test Case 4: Languages Already Match +1. Set both Windows and OSDP-Bench to the same language +2. Relaunch the application +3. No prompt should appear (languages already match) \ No newline at end of file diff --git a/src/Assets/google-play.svg b/src/Assets/google-play.svg new file mode 100644 index 0000000..223ebd2 --- /dev/null +++ b/src/Assets/google-play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Core/Actions/FileTransferAction.cs b/src/Core/Actions/FileTransferAction.cs index 9ef4750..24439f9 100644 --- a/src/Core/Actions/FileTransferAction.cs +++ b/src/Core/Actions/FileTransferAction.cs @@ -20,7 +20,7 @@ public async Task PerformAction(ControlPanel panel, Guid connectionId, b { if (parameter is not FileTransferParameters transferParams) { - throw new ArgumentException("Invalid parameter type for file transfer", nameof(parameter)); + throw new ArgumentException(@"Invalid parameter type for file transfer", nameof(parameter)); } if (string.IsNullOrEmpty(transferParams.FilePath)) diff --git a/src/Core/Actions/ResetCypressDeviceAction.cs b/src/Core/Actions/ResetCypressDeviceAction.cs index 7ace2f4..5ef4be5 100644 --- a/src/Core/Actions/ResetCypressDeviceAction.cs +++ b/src/Core/Actions/ResetCypressDeviceAction.cs @@ -21,7 +21,7 @@ public async Task PerformAction(ControlPanel panel, Guid connectionId, b await panel.Shutdown(); var connectionService = parameter as IOsdpConnection ?? - throw new ArgumentException("Invalid type", nameof(parameter)); + throw new ArgumentException(@"Invalid type", nameof(parameter)); connectionId = panel.StartConnection(connectionService, TimeSpan.Zero); diff --git a/src/Core/Actions/SetCommunicationAction.cs b/src/Core/Actions/SetCommunicationAction.cs index 26bcc8e..408e0a1 100644 --- a/src/Core/Actions/SetCommunicationAction.cs +++ b/src/Core/Actions/SetCommunicationAction.cs @@ -20,7 +20,7 @@ public async Task PerformAction(ControlPanel panel, Guid connectionId, b object? parameter) { var communicationParameters = parameter as CommunicationParameters ?? - throw new ArgumentException("Invalid type", nameof(parameter)); + throw new ArgumentException(@"Invalid type", nameof(parameter)); var result = await panel.CommunicationConfiguration(connectionId, address, new CommunicationConfiguration(communicationParameters.Address, diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index f21bfbe..8400539 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -25,7 +25,7 @@ - + diff --git a/src/Core/Models/UserSettings.cs b/src/Core/Models/UserSettings.cs index a096732..e6fd223 100644 --- a/src/Core/Models/UserSettings.cs +++ b/src/Core/Models/UserSettings.cs @@ -13,15 +13,20 @@ public class UserSettings /// /// Gets or sets the window width /// - public double WindowWidth { get; set; } = 800; + public double WindowWidth { get; init; } = 800; /// /// Gets or sets the window height /// - public double WindowHeight { get; set; } = 600; + public double WindowHeight { get; init; } = 600; /// /// Gets or sets whether the window is maximized /// - public bool IsMaximized { get; set; } = false; + public bool IsMaximized { get; init; } + + /// + /// Gets or sets whether to skip language mismatch checking + /// + public bool SkipLanguageMismatchCheck { get; set; } } \ No newline at end of file diff --git a/src/Core/Resources/Resources.de.resx b/src/Core/Resources/Resources.de.resx index 5776725..269a102 100644 --- a/src/Core/Resources/Resources.de.resx +++ b/src/Core/Resources/Resources.de.resx @@ -122,45 +122,21 @@ Entfernt Device connection status when not connected - - Entdeckend - Device connection status during discovery process - Fehler Device connection status when an error occurred - - USB-Gerät eingesteckt - Message shown when a USB device is detected - - - USB-Gerät entfernt - Message shown when a USB device is disconnected - - - Verbindung fehlgeschlagen - Generic connection failure message - - - Gerät nicht gefunden - Error when device cannot be found during discovery - - - Ungültige Adresse - Error when the entered address is invalid - S/N - Prefix for device serial number display - - Gerät, das an der Adresse verbunden ist {0} mit einer Baudrate von {1} - Format string for displaying connection details. {0} = address, {1} = baud rate + + Adresse {0} bei {1} + Brief connection status format. {0} = address, {1} = baud rate @@ -314,10 +290,6 @@ Art Column header for type in monitoring grid - - Details - Column header for details in monitoring grid - Erweitern Button text to expand row details @@ -472,10 +444,6 @@ USB-Gerät getrennt Message when USB device is disconnected - - USB-Anschlüsse geändert - Message when USB ports have changed - Verbinden @@ -557,8 +525,24 @@ Sprache auswählen Tooltip for language selection - - Sprache erfolgreich geändert - Confirmation message when language is changed + + Systemsprache erkannt + Title for system language mismatch dialog + + + Ihre Systemsprache ist {0}. Möchten Sie die Anwendung an Ihre Systemsprache anpassen? + Message asking user if they want to switch to system language. {0} is the system language name. + + + Frag mich nicht noch einmal + Checkbox label to prevent future language mismatch prompts + + + Ja + Common Yes button text + + + Nein + Common No button text \ No newline at end of file diff --git a/src/Core/Resources/Resources.es.resx b/src/Core/Resources/Resources.es.resx index f53280d..5c400fe 100644 --- a/src/Core/Resources/Resources.es.resx +++ b/src/Core/Resources/Resources.es.resx @@ -122,45 +122,21 @@ Desconectar Device connection status when not connected - - Descubriendo - Device connection status during discovery process - Error Device connection status when an error occurred - - Dispositivo USB conectado - Message shown when a USB device is detected - - - Dispositivo USB extraído - Message shown when a USB device is disconnected - - - Error de conexión - Generic connection failure message - - - Dispositivo no encontrado - Error when device cannot be found during discovery - - - Dirección no válida - Error when the entered address is invalid - S/N - Prefix for device serial number display - - Dispositivo conectado a la dirección {0} funcionando a una velocidad de transmisión de {1} - Format string for displaying connection details. {0} = address, {1} = baud rate + + Dirección {0} en {1} + Brief connection status format. {0} = address, {1} = baud rate @@ -314,10 +290,6 @@ Tipo Column header for type in monitoring grid - - Detalles - Column header for details in monitoring grid - Expandir Button text to expand row details @@ -472,10 +444,6 @@ Dispositivo USB desconectado Message when USB device is disconnected - - Puertos USB cambiados - Message when USB ports have changed - Conectar @@ -557,8 +525,24 @@ Seleccionar idioma Tooltip for language selection - - El lenguaje cambió con éxito - Confirmation message when language is changed + + Idioma del sistema detectado + Title for system language mismatch dialog + + + El idioma de su sistema es {0}. ¿Le gustaría cambiar la aplicación para que coincida con el idioma de su sistema? + Message asking user if they want to switch to system language. {0} is the system language name. + + + No me vuelvas a preguntar + Checkbox label to prevent future language mismatch prompts + + + + Common Yes button text + + + No + Common No button text \ No newline at end of file diff --git a/src/Core/Resources/Resources.fr.resx b/src/Core/Resources/Resources.fr.resx index fbc0ba0..b2acdf9 100644 --- a/src/Core/Resources/Resources.fr.resx +++ b/src/Core/Resources/Resources.fr.resx @@ -122,45 +122,21 @@ Coupé Device connection status when not connected - - Découvrir - Device connection status during discovery process - Erreur Device connection status when an error occurred - - Périphérique USB inséré - Message shown when a USB device is detected - - - Périphérique USB supprimé - Message shown when a USB device is disconnected - - - Échec de la connexion - Generic connection failure message - - - Appareil introuvable - Error when device cannot be found during discovery - - - Adresse invalide - Error when the entered address is invalid - N° de série - Prefix for device serial number display - - Appareil connecté à l’adresse {0} fonctionnant à une vitesse de transmission de {1} - Format string for displaying connection details. {0} = address, {1} = baud rate + + Adresse {0} à {1} + Brief connection status format. {0} = address, {1} = baud rate @@ -314,10 +290,6 @@ Type Column header for type in monitoring grid - - Détails - Column header for details in monitoring grid - Développer Button text to expand row details @@ -472,10 +444,6 @@ Périphérique USB déconnecté Message when USB device is disconnected - - Ports USB modifiés - Message when USB ports have changed - Relier @@ -557,8 +525,24 @@ Sélectionner une langue Tooltip for language selection - - Changement de langue réussi - Confirmation message when language is changed + + Langue du système détectée + Title for system language mismatch dialog + + + La langue de votre système est {0}. Souhaitez-vous changer l’application pour qu’elle corresponde à la langue de votre système ? + Message asking user if they want to switch to system language. {0} is the system language name. + + + Ne me demandez plus + Checkbox label to prevent future language mismatch prompts + + + Oui + Common Yes button text + + + Non + Common No button text \ No newline at end of file diff --git a/src/Core/Resources/Resources.ja.resx b/src/Core/Resources/Resources.ja.resx index f4a2f6d..e79ab41 100644 --- a/src/Core/Resources/Resources.ja.resx +++ b/src/Core/Resources/Resources.ja.resx @@ -122,45 +122,21 @@ 途切れ途切れ Device connection status when not connected - - 発見 - Device connection status during discovery process - エラー Device connection status when an error occurred - - USBデバイスが挿入されています - Message shown when a USB device is detected - - - USBデバイスを取り外しました - Message shown when a USB device is disconnected - - - 接続に失敗しました - Generic connection failure message - - - デバイスが見つかりません - Error when device cannot be found during discovery - - - 無効なアドレス - Error when the entered address is invalid - S/N - Prefix for device serial number display - - アドレスで接続されたデバイス {0} ボーレート {1} - Format string for displaying connection details. {0} = address, {1} = baud rate + + 住所 {0} で {1} + Brief connection status format. {0} = address, {1} = baud rate @@ -314,10 +290,6 @@ 種類 Column header for type in monitoring grid - - 細部 - Column header for details in monitoring grid - 膨らむ Button text to expand row details @@ -472,10 +444,6 @@ USBデバイスが切断されました Message when USB device is disconnected - - USBポートの変更 - Message when USB ports have changed - 繋ぐ @@ -557,8 +525,24 @@ 言語の選択 Tooltip for language selection - - 言語が正常に変更されました - Confirmation message when language is changed + + システム言語が検出されました + Title for system language mismatch dialog + + + システム言語は次のとおりです。 {0}.システム言語に合わせてアプリケーションを切り替えますか? + Message asking user if they want to switch to system language. {0} is the system language name. + + + 二度と聞かないで + Checkbox label to prevent future language mismatch prompts + + + はい + Common Yes button text + + + いいえ + Common No button text \ No newline at end of file diff --git a/src/Core/Resources/Resources.resx b/src/Core/Resources/Resources.resx index 38473df..cf8218c 100644 --- a/src/Core/Resources/Resources.resx +++ b/src/Core/Resources/Resources.resx @@ -123,38 +123,14 @@ Disconnected Device connection status when not connected - - Discovering - Device connection status during discovery process - Error Device connection status when an error occurred - - USB device inserted - Message shown when a USB device is detected - - - USB device removed - Message shown when a USB device is disconnected - - - Connection failed - Generic connection failure message - - - Device not found - Error when device cannot be found during discovery - - - Invalid address - Error when the entered address is invalid - @@ -163,9 +139,9 @@ - - Device connected at address {0} running at a baud rate of {1} - Format string for displaying connection details. {0} = address, {1} = baud rate + + Address {0} at {1} + Brief connection status format. {0} = address, {1} = baud rate @@ -323,10 +299,6 @@ Type Column header for type in monitoring grid - - Details - Column header for details in monitoring grid - Expand Button text to expand row details @@ -487,10 +459,6 @@ USB device disconnected Message when USB device is disconnected - - USB ports changed - Message when USB ports have changed - @@ -577,8 +545,24 @@ Select Language Tooltip for language selection - - Language changed successfully - Confirmation message when language is changed + + System Language Detected + Title for system language mismatch dialog + + + Your system language is {0}. Would you like to switch the application to match your system language? + Message asking user if they want to switch to system language. {0} is the system language name. + + + Don't ask me again + Checkbox label to prevent future language mismatch prompts + + + Yes + Common Yes button text + + + No + Common No button text \ No newline at end of file diff --git a/src/Core/Resources/Resources.zh.resx b/src/Core/Resources/Resources.zh.resx index b7df891..01d694c 100644 --- a/src/Core/Resources/Resources.zh.resx +++ b/src/Core/Resources/Resources.zh.resx @@ -122,45 +122,21 @@ 断开 Device connection status when not connected - - 发现 - Device connection status during discovery process - 错误 Device connection status when an error occurred - - 已插入 USB 设备 - Message shown when a USB device is detected - - - 已删除 USB 设备 - Message shown when a USB device is disconnected - - - 连接失败 - Generic connection failure message - - - 未找到设备 - Error when device cannot be found during discovery - - - 地址无效 - Error when the entered address is invalid - 序列号 - Prefix for device serial number display - - 设备连接地址 {0} 以 {1} - Format string for displaying connection details. {0} = address, {1} = baud rate + + 地址 {0} 在 {1} + Brief connection status format. {0} = address, {1} = baud rate @@ -314,10 +290,6 @@ 类型 Column header for type in monitoring grid - - - Column header for details in monitoring grid - 扩大 Button text to expand row details @@ -472,10 +444,6 @@ USB 设备已断开连接 Message when USB device is disconnected - - USB 端口已更改 - Message when USB ports have changed - 连接 @@ -557,8 +525,24 @@ 选择语言 Tooltip for language selection - - 语言更改成功 - Confirmation message when language is changed + + 检测到系统语言 + Title for system language mismatch dialog + + + 您的系统语言是 {0}.是否要切换应用程序以匹配您的系统语言? + Message asking user if they want to switch to system language. {0} is the system language name. + + + 不要再问我了 + Checkbox label to prevent future language mismatch prompts + + + 是的 + Common Yes button text + + + + Common No button text \ No newline at end of file diff --git a/src/Core/Services/DeviceManagementService.cs b/src/Core/Services/DeviceManagementService.cs index 46fe062..75f9821 100644 --- a/src/Core/Services/DeviceManagementService.cs +++ b/src/Core/Services/DeviceManagementService.cs @@ -222,20 +222,7 @@ public async Task Shutdown() await _panel.Shutdown(); - try - { - await WaitUntilDeviceIsOffline(); - } - catch (TimeoutException ex) - { - // Log or handle the timeout exception - Console.WriteLine($"Warning: {ex.Message}"); - } - catch (Exception ex) - { - // Log unexpected exceptions during shutdown - Console.WriteLine($"Error during device shutdown: {ex.Message}"); - } + await WaitUntilDeviceIsOffline(); } private async Task WaitUntilDeviceIsOffline() diff --git a/src/Core/Services/DeviceStateService.cs b/src/Core/Services/DeviceStateService.cs index 1aaaab5..7eb215c 100644 --- a/src/Core/Services/DeviceStateService.cs +++ b/src/Core/Services/DeviceStateService.cs @@ -22,22 +22,22 @@ public DeviceStateService(IDeviceManagementService deviceManagementService) throw new ArgumentNullException(nameof(deviceManagementService)); // Forward events from the device management service - _deviceManagementService.ConnectionStatusChange += (sender, args) => + _deviceManagementService.ConnectionStatusChange += (_, args) => ConnectionStatusChange?.Invoke(this, args); - _deviceManagementService.KeypadReadReceived += (sender, args) => + _deviceManagementService.KeypadReadReceived += (_, args) => KeypadReadReceived?.Invoke(this, args); - _deviceManagementService.CardReadReceived += (sender, args) => + _deviceManagementService.CardReadReceived += (_, args) => CardReadReceived?.Invoke(this, args); - _deviceManagementService.TraceEntryReceived += (sender, args) => + _deviceManagementService.TraceEntryReceived += (_, args) => TraceEntryReceived?.Invoke(this, args); - _deviceManagementService.NakReplyReceived += (sender, args) => + _deviceManagementService.NakReplyReceived += (_, args) => NakReplyReceived?.Invoke(this, args); - _deviceManagementService.DeviceLookupsChanged += (sender, args) => + _deviceManagementService.DeviceLookupsChanged += (_, args) => DeviceLookupsChanged?.Invoke(this, args); } diff --git a/src/Core/Services/ILanguageMismatchService.cs b/src/Core/Services/ILanguageMismatchService.cs new file mode 100644 index 0000000..b3a47d5 --- /dev/null +++ b/src/Core/Services/ILanguageMismatchService.cs @@ -0,0 +1,19 @@ +namespace OSDPBench.Core.Services; + +/// +/// Service for handling system language mismatch detection and user prompts +/// +public interface ILanguageMismatchService +{ + /// + /// Checks for system language mismatch and prompts the user if needed + /// + Task CheckAndPromptForLanguageMismatchAsync(); + + /// + /// Shows the language mismatch dialog with custom UI + /// + /// The name of the system language + /// A tuple containing (userWantsToSwitch, dontAskAgain) + Task<(bool userWantsToSwitch, bool dontAskAgain)> ShowLanguageMismatchDialogAsync(string systemLanguageName); +} \ No newline at end of file diff --git a/src/Core/Services/ILocalizationService.cs b/src/Core/Services/ILocalizationService.cs index 5daffba..31cc258 100644 --- a/src/Core/Services/ILocalizationService.cs +++ b/src/Core/Services/ILocalizationService.cs @@ -55,4 +55,23 @@ public interface ILocalizationService /// The culture to get the display name for /// The localized display name string GetCultureDisplayName(CultureInfo culture); + + /// + /// Checks if the current application language matches the system language + /// + /// True if the languages match, false otherwise + bool IsSystemLanguageMatch(); + + /// + /// Gets the current system language + /// + /// The system's current UI culture + CultureInfo GetSystemCulture(); + + /// + /// Checks if a specific culture is supported by the application + /// + /// The culture to check + /// True if the culture or its base language is supported + bool IsCultureSupported(CultureInfo culture); } \ No newline at end of file diff --git a/src/Core/Services/IUsbDeviceMonitorService.cs b/src/Core/Services/IUsbDeviceMonitorService.cs index 3d93ccd..9a199bf 100644 --- a/src/Core/Services/IUsbDeviceMonitorService.cs +++ b/src/Core/Services/IUsbDeviceMonitorService.cs @@ -1,5 +1,3 @@ -using System; - namespace OSDPBench.Core.Services; /// diff --git a/src/Core/Services/LanguageMismatchService.cs b/src/Core/Services/LanguageMismatchService.cs new file mode 100644 index 0000000..c94630d --- /dev/null +++ b/src/Core/Services/LanguageMismatchService.cs @@ -0,0 +1,115 @@ +using System.Globalization; + +namespace OSDPBench.Core.Services; + +/// +/// Service for handling system language mismatch detection and user prompts +/// +public class LanguageMismatchService : ILanguageMismatchService +{ + private readonly ILocalizationService _localizationService; + private readonly IDialogService _dialogService; + private readonly IUserSettingsService _userSettingsService; + + /// + /// Initializes a new instance of the LanguageMismatchService + /// + /// The localization service + /// The dialog service + /// The user settings service + public LanguageMismatchService( + ILocalizationService localizationService, + IDialogService dialogService, + IUserSettingsService userSettingsService) + { + _localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService)); + _dialogService = dialogService ?? throw new ArgumentNullException(nameof(dialogService)); + _userSettingsService = userSettingsService ?? throw new ArgumentNullException(nameof(userSettingsService)); + } + + /// + public async Task CheckAndPromptForLanguageMismatchAsync() + { + // Only check if this is not the user's first time setting a language + // (i.e., they have a saved preference) + if (string.IsNullOrEmpty(_userSettingsService.Settings.PreferredCulture)) + { + // First time user - don't prompt, just use system language + return; + } + + // Check if user has disabled language mismatch checking + if (_userSettingsService.Settings.SkipLanguageMismatchCheck) + { + // User has opted out of language mismatch checks + return; + } + + // Check if there's a mismatch + if (_localizationService.IsSystemLanguageMatch()) + { + // Languages match or system language not supported - no action needed + return; + } + + var systemCulture = _localizationService.GetSystemCulture(); + var systemLanguageName = systemCulture.NativeName; + + // Show custom dialog + var (userWantsToSwitch, dontAskAgain) = await ShowLanguageMismatchDialogAsync(systemLanguageName); + + // Save the "don't ask again" preference + if (dontAskAgain) + { + await _userSettingsService.UpdateSettingsAsync(settings => + settings.SkipLanguageMismatchCheck = true); + } + + if (userWantsToSwitch) + { + // Find the best supported culture that matches the system language + var supportedCulture = FindBestMatchingCulture(systemCulture); + if (supportedCulture != null) + { + _localizationService.ChangeCulture(supportedCulture); + } + } + } + + /// + public async Task<(bool userWantsToSwitch, bool dontAskAgain)> ShowLanguageMismatchDialogAsync(string systemLanguageName) + { + // This method will be implemented in the Windows-specific service + // For now, fall back to the simple dialog + var title = _localizationService.GetString("Language_SystemMismatchTitle"); + var message = _localizationService.GetString("Language_SystemMismatchMessage", systemLanguageName); + + var userWantsToSwitch = await _dialogService.ShowConfirmationDialog( + title, + message, + MessageIcon.Information); + + return (userWantsToSwitch, false); // No "don't ask again" option in fallback + } + + /// + /// Finds the best matching supported culture for the given system culture + /// + /// The system culture to match + /// The best matching supported culture, or null if none found + private CultureInfo? FindBestMatchingCulture(CultureInfo systemCulture) + { + var supportedCultures = _localizationService.SupportedCultures; + + // First try exact match + var exactMatch = supportedCultures.FirstOrDefault(c => c.Name == systemCulture.Name); + if (exactMatch != null) + return exactMatch; + + // Then try base language match + var baseLanguageMatch = supportedCultures.FirstOrDefault(c => + c.TwoLetterISOLanguageName == systemCulture.TwoLetterISOLanguageName); + + return baseLanguageMatch; + } +} \ No newline at end of file diff --git a/src/Core/Services/LocalizationService.cs b/src/Core/Services/LocalizationService.cs index aad0743..088c570 100644 --- a/src/Core/Services/LocalizationService.cs +++ b/src/Core/Services/LocalizationService.cs @@ -1,6 +1,4 @@ using System.Globalization; -using System.Resources; -using OSDPBench.Core.Resources; namespace OSDPBench.Core.Services; @@ -9,7 +7,6 @@ namespace OSDPBench.Core.Services; /// public class LocalizationService : ILocalizationService { - private readonly ResourceManager _resourceManager; private readonly IUserSettingsService? _userSettingsService; private CultureInfo _currentCulture; @@ -19,7 +16,6 @@ public class LocalizationService : ILocalizationService /// Optional user settings service for persistence public LocalizationService(IUserSettingsService? userSettingsService) { - _resourceManager = new ResourceManager("OSDPBench.Core.Resources.Resources", typeof(LocalizationService).Assembly); _userSettingsService = userSettingsService; // Initialize culture from settings or system default @@ -72,7 +68,7 @@ public CultureInfo CurrentCulture /// public string GetString(string key) { - return OSDPBench.Core.Resources.Resources.GetString(key); + return Resources.Resources.GetString(key); } /// @@ -101,7 +97,7 @@ public void ChangeCulture(CultureInfo culture) CultureInfo.CurrentCulture = culture; // Update the Resources class culture (this will trigger PropertyChanged) - OSDPBench.Core.Resources.Resources.ChangeCulture(culture); + Resources.Resources.ChangeCulture(culture); // Save preference to settings if (_userSettingsService != null) @@ -138,4 +134,40 @@ public string GetCultureDisplayName(CultureInfo culture) // Return the native name of the culture return culture.NativeName; } + + /// + public bool IsSystemLanguageMatch() + { + var systemCulture = GetSystemCulture(); + + // If system language is not supported, consider it a match (no need to prompt) + if (!IsCultureSupported(systemCulture)) + return true; + + // Check if cultures are exactly the same + if (_currentCulture.Name == systemCulture.Name) + return true; + + // Check if the base languages match (e.g., "en-US" matches "en-GB") + if (_currentCulture.TwoLetterISOLanguageName == systemCulture.TwoLetterISOLanguageName) + return true; + + return false; + } + + /// + public CultureInfo GetSystemCulture() + { + // Get the current system UI culture + // This represents the OS display language + return CultureInfo.InstalledUICulture; + } + + /// + public bool IsCultureSupported(CultureInfo culture) + { + return SupportedCultures.Any(c => + c.Name == culture.Name || + c.TwoLetterISOLanguageName == culture.TwoLetterISOLanguageName); + } } \ No newline at end of file diff --git a/src/Core/ViewModels/Dialogs/LanguageMismatchDialogViewModel.cs b/src/Core/ViewModels/Dialogs/LanguageMismatchDialogViewModel.cs new file mode 100644 index 0000000..765f2cc --- /dev/null +++ b/src/Core/ViewModels/Dialogs/LanguageMismatchDialogViewModel.cs @@ -0,0 +1,41 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; + +namespace OSDPBench.Core.ViewModels.Dialogs; + +/// +/// ViewModel for the language mismatch dialog +/// +public partial class LanguageMismatchDialogViewModel : ObservableObject +{ + private readonly Action _closeCallback; + + [ObservableProperty] + private string _message; + + [ObservableProperty] + private bool _dontAskAgain; + + /// + /// Initializes a new instance of the LanguageMismatchDialogViewModel + /// + /// The message to display + /// Callback when dialog closes (userChoice, dontAskAgain) + public LanguageMismatchDialogViewModel(string message, Action closeCallback) + { + _message = message; + _closeCallback = closeCallback ?? throw new ArgumentNullException(nameof(closeCallback)); + } + + [RelayCommand] + private void Yes() + { + _closeCallback(true, DontAskAgain); + } + + [RelayCommand] + private void No() + { + _closeCallback(false, DontAskAgain); + } +} \ No newline at end of file diff --git a/src/Core/ViewModels/LanguageSelectionViewModel.cs b/src/Core/ViewModels/LanguageSelectionViewModel.cs index 9ca5548..816eba6 100644 --- a/src/Core/ViewModels/LanguageSelectionViewModel.cs +++ b/src/Core/ViewModels/LanguageSelectionViewModel.cs @@ -3,7 +3,6 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using OSDPBench.Core.Services; -using OSDPBench.Core.Resources; namespace OSDPBench.Core.ViewModels; diff --git a/src/Core/ViewModels/Pages/ConnectViewModel.cs b/src/Core/ViewModels/Pages/ConnectViewModel.cs index 1ee8cdb..16d3435 100644 --- a/src/Core/ViewModels/Pages/ConnectViewModel.cs +++ b/src/Core/ViewModels/Pages/ConnectViewModel.cs @@ -5,7 +5,6 @@ using OSDP.Net.Tracing; using OSDPBench.Core.Models; using OSDPBench.Core.Services; -using OSDPBench.Core.Resources; namespace OSDPBench.Core.ViewModels.Pages; @@ -57,13 +56,13 @@ public ConnectViewModel(IDialogService dialogService, IDeviceManagementService d _usbDeviceMonitorService.StartMonitoring(); } - // Perform initial port scan + // Perform an initial port scan Task.Run(async () => await InitializeSerialPorts()); } private void OnDeviceManagementServiceOnTraceEntryReceived(object? sender, TraceEntry traceEntry) { - // Update activity indicators based on raw trace entry direction (works for encrypted packets too) + // Update activity indicators based on a raw trace entry direction (works for encrypted packets too) UpdateActivityIndicators(traceEntry.Direction); PacketTraceEntry? packetTraceEntry = BuildPacketTraceEntry(traceEntry); @@ -102,23 +101,23 @@ private void DeviceManagementServiceOnConnectionStatusChange(object? sender, Con { if (connectionStatus == ConnectionStatus.Connected) { - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_Connected"); + StatusText = Resources.Resources.GetString("Status_Connected"); NakText = string.Empty; StatusLevel = StatusLevel.Connected; } else if (StatusLevel == StatusLevel.Discovered) { - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_AttemptingToConnect"); + StatusText = Resources.Resources.GetString("Status_AttemptingToConnect"); StatusLevel = StatusLevel.Connecting; } else if (connectionStatus == ConnectionStatus.InvalidSecurityKey) { - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_InvalidSecurityKey"); + StatusText = Resources.Resources.GetString("Status_InvalidSecurityKey"); StatusLevel = StatusLevel.Error; } else { - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_Disconnected"); + StatusText = Resources.Resources.GetString("Status_Disconnected"); StatusLevel = StatusLevel.Disconnected; } } @@ -188,7 +187,7 @@ private async Task InitializeSerialPorts() } catch (Exception ex) { - Console.WriteLine(OSDPBench.Core.Resources.Resources.GetString("Error_InitializingSerialPorts").Replace("{0}", ex.Message)); + Console.WriteLine(Resources.Resources.GetString("Error_InitializingSerialPorts").Replace("{0}", ex.Message)); StatusLevel = StatusLevel.NotReady; _initializationComplete.SetException(ex); } @@ -230,27 +229,27 @@ private void UpdateDiscoveryStatus(DiscoveryResult current) switch (current.Status) { case DiscoveryStatus.Started: - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_AttemptingToDiscover"); + StatusText = Resources.Resources.GetString("Status_AttemptingToDiscover"); break; case DiscoveryStatus.LookingForDeviceOnConnection: - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_AttemptingToDiscoverAtBaudRate").Replace("{0}", current.Connection.BaudRate.ToString()); + StatusText = Resources.Resources.GetString("Status_AttemptingToDiscoverAtBaudRate").Replace("{0}", current.Connection.BaudRate.ToString()); break; case DiscoveryStatus.ConnectionWithDeviceFound: - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_FoundDeviceAtBaudRate").Replace("{0}", current.Connection.BaudRate.ToString()); + StatusText = Resources.Resources.GetString("Status_FoundDeviceAtBaudRate").Replace("{0}", current.Connection.BaudRate.ToString()); break; case DiscoveryStatus.LookingForDeviceAtAddress: - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_AttemptingToDetermineDevice").Replace("{0}", current.Connection.BaudRate.ToString()).Replace("{1}", current.Address.ToString()); + StatusText = Resources.Resources.GetString("Status_AttemptingToDetermineDevice").Replace("{0}", current.Connection.BaudRate.ToString()).Replace("{1}", current.Address.ToString()); break; case DiscoveryStatus.DeviceIdentified: - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_AttemptingToIdentifyDevice").Replace("{0}", current.Connection.BaudRate.ToString()).Replace("{1}", current.Address.ToString()); + StatusText = Resources.Resources.GetString("Status_AttemptingToIdentifyDevice").Replace("{0}", current.Connection.BaudRate.ToString()).Replace("{1}", current.Address.ToString()); break; case DiscoveryStatus.CapabilitiesDiscovered: - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_AttemptingToGetCapabilities").Replace("{0}", current.Connection.BaudRate.ToString()).Replace("{1}", current.Address.ToString()); + StatusText = Resources.Resources.GetString("Status_AttemptingToGetCapabilities").Replace("{0}", current.Connection.BaudRate.ToString()).Replace("{1}", current.Address.ToString()); break; case DiscoveryStatus.Succeeded: @@ -258,18 +257,18 @@ private void UpdateDiscoveryStatus(DiscoveryResult current) break; case DiscoveryStatus.DeviceNotFound: - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_FailedToConnect"); + StatusText = Resources.Resources.GetString("Status_FailedToConnect"); StatusLevel = StatusLevel.Error; break; case DiscoveryStatus.Error: - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_ErrorWhileDiscovering"); + StatusText = Resources.Resources.GetString("Status_ErrorWhileDiscovering"); StatusLevel = StatusLevel.Error; break; case DiscoveryStatus.Cancelled: StatusLevel = StatusLevel.Error; - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_CancelledDiscovery"); + StatusText = Resources.Resources.GetString("Status_CancelledDiscovery"); break; default: @@ -279,7 +278,7 @@ private void UpdateDiscoveryStatus(DiscoveryResult current) private void HandleSuccessfulDiscovery(DiscoveryResult result) { - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_SuccessfullyDiscovered").Replace("{0}", result.Connection.BaudRate.ToString()).Replace("{1}", result.Address.ToString()); + StatusText = Resources.Resources.GetString("Status_SuccessfullyDiscovered").Replace("{0}", result.Connection.BaudRate.ToString()).Replace("{1}", result.Address.ToString()); StatusLevel = StatusLevel.Discovered; if (result.Connection is ISerialPortConnectionService service) @@ -298,7 +297,7 @@ private async Task ConnectDevice() string serialPortName = SelectedSerialPort?.Name ?? string.Empty; StatusLevel = StatusLevel.ConnectingManually; - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_AttemptingToConnectManually"); + StatusText = Resources.Resources.GetString("Status_AttemptingToConnectManually"); byte[]? securityKey = await GetSecurityKey(); if (securityKey == null && !UseDefaultKey) return; @@ -317,8 +316,8 @@ private async Task ConnectDevice() catch (Exception exception) { await _dialogService.ShowMessageDialog( - OSDPBench.Core.Resources.Resources.GetString("Dialog_Connect_Title"), - OSDPBench.Core.Resources.Resources.GetString("Dialog_InvalidSecurityKeyMessage").Replace("{0}", exception.Message), + Resources.Resources.GetString("Dialog_Connect_Title"), + Resources.Resources.GetString("Dialog_InvalidSecurityKeyMessage").Replace("{0}", exception.Message), MessageIcon.Error); return null; } @@ -343,7 +342,7 @@ await _deviceManagementService.Connect( private async Task DisconnectDevice() { await _deviceManagementService.Shutdown(); - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_Disconnected"); + StatusText = Resources.Resources.GetString("Status_Disconnected"); StatusLevel = StatusLevel.Disconnected; NakText = string.Empty; _lastPacketEntry = null; @@ -351,7 +350,7 @@ private async Task DisconnectDevice() LastRxActiveTime = DateTime.MinValue; } - private async void OnUsbDeviceChanged(object? sender, UsbDeviceChangedEventArgs e) + private async void OnUsbDeviceChanged(object? sender, UsbDeviceChangedEventArgs eventArgs) { try { @@ -367,20 +366,13 @@ private async void OnUsbDeviceChanged(object? sender, UsbDeviceChangedEventArgs AvailableSerialPorts.Add(port); } - // Handle port selection based on change type if (AvailableSerialPorts.Count > 0) { // Try to reselect the previously selected port if it still exists var previousPort = AvailableSerialPorts.FirstOrDefault(p => p.Name == currentlySelectedPort); - if (previousPort != null) - { - SelectedSerialPort = previousPort; - } - else - { - // Select the first available port - SelectedSerialPort = AvailableSerialPorts.First(); - } + SelectedSerialPort = previousPort ?? + // Select the first available port + AvailableSerialPorts.First(); if (StatusLevel == StatusLevel.NotReady) { @@ -395,28 +387,28 @@ private async void OnUsbDeviceChanged(object? sender, UsbDeviceChangedEventArgs StatusLevel = StatusLevel.NotReady; } } - - // Show notification based on change type - if (e.ChangeType == UsbDeviceChangeType.Connected) - { - UsbStatusText = OSDPBench.Core.Resources.Resources.GetString("USB_DeviceConnected"); - } - else if (e.ChangeType == UsbDeviceChangeType.Disconnected) + + switch (eventArgs.ChangeType) { - UsbStatusText = OSDPBench.Core.Resources.Resources.GetString("USB_DeviceDisconnected"); - - // If we were connected and the device was removed, update status - if (StatusLevel == StatusLevel.Connected && !e.AvailablePorts.Contains(_deviceManagementService.PortName ?? "")) + // Show notification based on the change type + case UsbDeviceChangeType.Connected: + UsbStatusText = Resources.Resources.GetString("USB_DeviceConnected"); + break; + case UsbDeviceChangeType.Disconnected: { - await _deviceManagementService.Shutdown(); - StatusLevel = StatusLevel.Disconnected; - StatusText = OSDPBench.Core.Resources.Resources.GetString("Status_DeviceDisconnectedUSBRemoved"); + UsbStatusText = Resources.Resources.GetString("USB_DeviceDisconnected"); + + // If we were connected and the device was removed, update status + if (StatusLevel == StatusLevel.Connected && !eventArgs.AvailablePorts.Contains(_deviceManagementService.PortName ?? "")) + { + await _deviceManagementService.Shutdown(); + StatusLevel = StatusLevel.Disconnected; + StatusText = Resources.Resources.GetString("Status_DeviceDisconnectedUSBRemoved"); + } + + break; } } - else - { - UsbStatusText = OSDPBench.Core.Resources.Resources.GetString("USB_PortsChanged"); - } // Clear USB status after 3 seconds _usbStatusTimer?.Dispose(); @@ -424,7 +416,7 @@ private async void OnUsbDeviceChanged(object? sender, UsbDeviceChangedEventArgs } catch (Exception ex) { - Console.WriteLine(OSDPBench.Core.Resources.Resources.GetString("Error_HandlingUSBDeviceChange").Replace("{0}", ex.Message)); + Console.WriteLine(Resources.Resources.GetString("Error_HandlingUSBDeviceChange").Replace("{0}", ex.Message)); } } diff --git a/src/Core/ViewModels/Pages/ManageViewModel.cs b/src/Core/ViewModels/Pages/ManageViewModel.cs index 3148582..79b230f 100644 --- a/src/Core/ViewModels/Pages/ManageViewModel.cs +++ b/src/Core/ViewModels/Pages/ManageViewModel.cs @@ -5,7 +5,6 @@ using OSDPBench.Core.Actions; using OSDPBench.Core.Models; using OSDPBench.Core.Services; -using OSDPBench.Core.Resources; namespace OSDPBench.Core.ViewModels.Pages; @@ -52,7 +51,7 @@ private async Task ExecuteDeviceAction() { if (SelectedDeviceAction == null) return; - await ExceptionHelper.ExecuteSafelyAsync(_dialogService, OSDPBench.Core.Resources.Resources.GetString("Dialog_PerformingAction_Title"), async () => + await ExceptionHelper.ExecuteSafelyAsync(_dialogService, Resources.Resources.GetString("Dialog_PerformingAction_Title"), async () => { if (SelectedDeviceAction is ResetCypressDeviceAction) { @@ -72,7 +71,7 @@ await ExceptionHelper.ExecuteSafelyAsync(_dialogService, OSDPBench.Core.Resource { return await ExceptionHelper.ExecuteSafelyAsync( _dialogService, - OSDPBench.Core.Resources.Resources.GetString("Dialog_PerformingAction_Title"), + Resources.Resources.GetString("Dialog_PerformingAction_Title"), async () => await _deviceManagementService.ExecuteDeviceAction(SelectedDeviceAction!, DeviceActionParameter), null); } @@ -87,13 +86,13 @@ private async Task HandleSetCommunicationAction(object result) if (!parametersChanged) { - await _dialogService.ShowMessageDialog(OSDPBench.Core.Resources.Resources.GetString("Dialog_UpdateCommunications_Title"), - OSDPBench.Core.Resources.Resources.GetString("Dialog_UpdateCommunications_NoChange"), MessageIcon.Warning); + await _dialogService.ShowMessageDialog(Resources.Resources.GetString("Dialog_UpdateCommunications_Title"), + Resources.Resources.GetString("Dialog_UpdateCommunications_NoChange"), MessageIcon.Warning); return; } - await _dialogService.ShowMessageDialog(OSDPBench.Core.Resources.Resources.GetString("Dialog_UpdateCommunications_Title"), - OSDPBench.Core.Resources.Resources.GetString("Dialog_UpdateCommunications_Success"), MessageIcon.Information); + await _dialogService.ShowMessageDialog(Resources.Resources.GetString("Dialog_UpdateCommunications_Title"), + Resources.Resources.GetString("Dialog_UpdateCommunications_Success"), MessageIcon.Information); if (_deviceManagementService.PortName != null) { await _deviceManagementService.Reconnect(_serialPortConnectionService.GetConnection( @@ -109,7 +108,7 @@ private async Task HandleResetCypressDeviceAction() if (!IdentityLookup.CanSendResetCommand) { await _dialogService.ShowMessageDialog( - OSDPBench.Core.Resources.Resources.GetString("Dialog_ResetDevice_Title"), + Resources.Resources.GetString("Dialog_ResetDevice_Title"), IdentityLookup.ResetInstructions, MessageIcon.Information); return; @@ -118,8 +117,8 @@ await _dialogService.ShowMessageDialog( await _deviceManagementService.Shutdown(); bool userConfirmed = await _dialogService.ShowConfirmationDialog( - OSDPBench.Core.Resources.Resources.GetString("Dialog_ResetDevice_Title"), - OSDPBench.Core.Resources.Resources.GetString("Dialog_ResetDevice_Confirmation"), + Resources.Resources.GetString("Dialog_ResetDevice_Title"), + Resources.Resources.GetString("Dialog_ResetDevice_Confirmation"), MessageIcon.Warning); if (!userConfirmed) @@ -134,7 +133,7 @@ await _deviceManagementService.Reconnect(_serialPortConnectionService.GetConnect return; } - bool success = await ExceptionHelper.ExecuteSafelyAsync(_dialogService, OSDPBench.Core.Resources.Resources.GetString("Dialog_ResetDevice_Title"), async () => + bool success = await ExceptionHelper.ExecuteSafelyAsync(_dialogService, Resources.Resources.GetString("Dialog_ResetDevice_Title"), async () => { if (_deviceManagementService.PortName != null) { @@ -149,15 +148,15 @@ await _deviceManagementService.ExecuteDeviceAction( if (success) { await _dialogService.ShowMessageDialog( - OSDPBench.Core.Resources.Resources.GetString("Dialog_ResetDevice_Title"), - OSDPBench.Core.Resources.Resources.GetString("Dialog_ResetDevice_Success"), + Resources.Resources.GetString("Dialog_ResetDevice_Title"), + Resources.Resources.GetString("Dialog_ResetDevice_Success"), MessageIcon.Information); } else { await _dialogService.ShowMessageDialog( - OSDPBench.Core.Resources.Resources.GetString("Dialog_ResetDevice_Title"), - OSDPBench.Core.Resources.Resources.GetString("Dialog_ResetDevice_Failed"), + Resources.Resources.GetString("Dialog_ResetDevice_Title"), + Resources.Resources.GetString("Dialog_ResetDevice_Failed"), MessageIcon.Error); } } diff --git a/src/Core/ViewModels/Windows/MainWindowViewModel.cs b/src/Core/ViewModels/Windows/MainWindowViewModel.cs index e701e51..d747feb 100644 --- a/src/Core/ViewModels/Windows/MainWindowViewModel.cs +++ b/src/Core/ViewModels/Windows/MainWindowViewModel.cs @@ -6,7 +6,7 @@ namespace OSDPBench.Core.ViewModels.Windows; /// /// Represents the view model for the main window of the application. /// -public partial class MainWindowViewModel : ObservableObject +public class MainWindowViewModel : ObservableObject { /// /// Gets the language selection view model diff --git a/src/UI/UsbSerialForAndroid/.gitattributes b/src/UI/UsbSerialForAndroid/.gitattributes deleted file mode 100644 index 1ff0c42..0000000 --- a/src/UI/UsbSerialForAndroid/.gitattributes +++ /dev/null @@ -1,63 +0,0 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain diff --git a/src/UI/UsbSerialForAndroid/.gitignore b/src/UI/UsbSerialForAndroid/.gitignore deleted file mode 100644 index 84a4cfa..0000000 --- a/src/UI/UsbSerialForAndroid/.gitignore +++ /dev/null @@ -1,248 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -[Xx]64/ -[Xx]86/ -[Bb]uild/ -bld/ -[Bb]in/ -[Oo]bj/ - -# Visual Studio 2015 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# 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 - -# DNX -project.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# 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 - -# 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 - -# TODO: Un-comment the next line if you do not want to checkin -# your web deploy settings because they may include unencrypted -# passwords -#*.pubxml -*.publishproj - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Microsoft Azure ApplicationInsights config file -ApplicationInsights.config - -# Windows Store app package directory -AppPackages/ -BundleArtifacts/ - -# 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/ -[Ss]tyle[Cc]op.* -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.pfx -*.publishsettings -node_modules/ -orleans.codegen.cs - -# 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 - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# LightSwitch generated files -GeneratedArtifacts/ -ModelManifest.xml - -# Paket dependency manager -.paket/paket.exe - -# FAKE - F# Make -.fake/ - -#Zip files -*.zip \ No newline at end of file diff --git a/src/UI/UsbSerialForAndroid/LICENSE.txt b/src/UI/UsbSerialForAndroid/LICENSE.txt deleted file mode 100644 index c5e6b6b..0000000 --- a/src/UI/UsbSerialForAndroid/LICENSE.txt +++ /dev/null @@ -1,22 +0,0 @@ -MIT License - -Copyright (c) 2011-2017 Google Inc. -Copyright (c) 2017 Tyler Technologies - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/src/UI/UsbSerialForAndroid/README.md b/src/UI/UsbSerialForAndroid/README.md deleted file mode 100644 index 1780d36..0000000 --- a/src/UI/UsbSerialForAndroid/README.md +++ /dev/null @@ -1,107 +0,0 @@ -# UsbSerialForAndroid - -[![Build Status](https://chrismillercode.visualstudio.com/UsbSerialForAndroid/_apis/build/status/anotherlab.UsbSerialForAndroid?branchName=master)](https://chrismillercode.visualstudio.com/UsbSerialForAndroid/_build/latest?definitionId=1&branchName=master) - -This is a driver library to allow your Xamarin Android app to communicate with many common USB serial hardware. It uses the [Android USB Host API](http://developer.android.com/guide/topics/connectivity/usb/host.html) -available on Android 3.1+. - -No root access, ADK, or special kernel drivers are required; all drivers are implemented in -c#. You get a raw serial port with `Read()`, `Write()`, and other basic -functions for use with your own protocols. The appropriate driver is picked based on the device's Vendor ID and Product ID. - -This is a Xamarin C# port of Mike Wakerly's Java [usb-serial-for-android](https://github.com/mik3y/usb-serial-for-android) library. It followed that library very closely when it was ported. The main changes were to make the method names follow C# standard naming conventions. Some Java specific data types were replaced with .NET types and the reflection code is .NET specific. Code examples written for the Java version of the library should translate more or less faithfully to C#. - -It also includes code derived from a portion of LusoVU's [XamarinUsbSerial](https://bitbucket.org/lusovu/xamarinusbserial) library. XamarinUsbSerial was a C# wrapper for the Java usb-serial-for-android. It used an older version of the usb-serial-for-android .jar file. Only the the C# code was used, the Java library is not referenced. - -The default branch has been renamed from master to main. if you have a local clone, you can run the following commands to update the name of the default branch - -``` -git branch -m master main -git fetch origin -git branch -u origin/main main -git remote set-head origin -a -``` - -## Structure - -This solution contains two projects. - -* UsbSerialForAndroid - A port of the Java library usb-serial-for-android -* UsbSerialExampleApp - A Xamarin version of the example app that comes with usb-serial-for-android - -## Getting Started -**1.** Reference the library to your project - -**2.** Copy the [device_filter.axml](https://github.com/anotherlab/UsbSerialForAndroid/blob/master/UsbSerialExampleApp/Resources/xml/device_filter.xml) from the example app to your Resources/xml folder. Make sure that the Build Action is set to AndroidResource - -**3.** Add the following attribute to the main activity to enable the USB Host -```C# -[assembly: UsesFeature("android.hardware.usb.host")] -``` - -**4.** Add the following IntentFilter to the main activity to receive USB device attached notifications -```C# -[IntentFilter(new[] { UsbManager.ActionUsbDeviceAttached })] -``` - -**5.** Add the MetaData attribute to associate the device_filter with the USB attached event to only see the devices that we are looking for -```C# -[MetaData(UsbManager.ActionUsbDeviceAttached, Resource = "@xml/device_filter")] -``` - -**6.** Refer to [MainActivity.cs](https://github.com/anotherlab/UsbSerialForAndroid/blob/master/UsbSerialExampleApp/MainActivity.cs) in the example app to see how connect to a serial device and read data from it. - -## Working with unrecognized devices -The UsbSerialForAndroid has been compiled with the Vendor ID/Product ID pairs for many common serial devices. If you have a device that is not defined by the library, but will work with one of the drivers, you can manually add the VID/PID pair. - -UsbSerialProber is a class to help you find and instantiate compatible -UsbSerialDrivers from the tree of connected UsbDevices. Normally, you will use -the default prober returned by ``UsbSerialProber.getDefaultProber()``, which -uses the built-in list of well-known VIDs and PIDs that are supported by our -drivers. - -To use your own set of rules, create and use a custom prober: - -```C# -// Probe for our custom CDC devices, which use VID 0x1234 -// and PIDS 0x0001 and 0x0002. -var table = UsbSerialProber.DefaultProbeTable; -table.AddProduct(0x1b4f, 0x0008, typeof(CdcAcmSerialDriver)); // IOIO OTG - -table.AddProduct(0x09D8, 0x0420, typeof(CdcAcmSerialDriver)); // Elatec TWN4 - -var prober = new UsbSerialProber(table); -List drivers = prober.FindAllDrivers(usbManager); -// ... -``` - -Of course, nothing requires you to use UsbSerialProber at all: you can -instantiate driver classes directly if you know what you're doing; just supply -a compatible UsbDevice. - - -## Compatible Devices - -* *Serial chips:* FT232R, CDC/ACM (eg Arduino Uno) and possibly others. - See [CompatibleSerialDevices](https://github.com/mik3y/usb-serial-for-android/wiki/Compatible-Serial-Devices). -* *Android phones and tablets:* Nexus 7, Motorola Xoom, and many others. - See [CompatibleAndroidDevices](https://github.com/mik3y/usb-serial-for-android/wiki/Compatible-Android-Devices). - -## Additional information - -This is a port of the usb-serial-for-android library and code examples written for it can be adapted to C# without much effort. - -For common problems, see the -[Troubleshooting](https://github.com/mik3y/usb-serial-for-android/wiki/Troubleshooting) -wiki page for usb-serial-for-android library. - -For other help and discussion, please join the usb-serial-for-android Google Group, -[usb-serial-for-android](https://groups.google.com/forum/?fromgroups#!forum/usb-serial-for-android). - -Pull Requests are welcome, but please include what hardware was used for testing. I do not have the hardware or the bandwidth to test the various chipsets supported by the library. - -## Author, License, and Copyright - -This library is licensed under LGPL Version 2.1. Please see LICENSE.txt for the complete license. - -Copyright 2017, Tyler Technologies. All Rights Reserved. Portions of this library are based on the [usb-serial-for-android](https://github.com/mik3y/usb-serial-for-android) and [XamarinUsbSerial](https://bitbucket.org/lusovu/xamarinusbserial) libraries. Their rights remain intact. diff --git a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Assets/AboutAssets.txt b/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Assets/AboutAssets.txt deleted file mode 100644 index ee39886..0000000 --- a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Assets/AboutAssets.txt +++ /dev/null @@ -1,19 +0,0 @@ -Any raw assets you want to be deployed with your application can be placed in -this directory (and child directories) and given a Build Action of "AndroidAsset". - -These files will be deployed with you package and will be accessible using Android's -AssetManager, like this: - -public class ReadAsset : Activity -{ - protected override void OnCreate (Bundle bundle) - { - base.OnCreate (bundle); - - InputStream input = Assets.Open ("my_asset.txt"); - } -} - -Additionally, some Android functions will automatically load asset files: - -Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); \ No newline at end of file diff --git a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/GettingStarted.Xamarin b/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/GettingStarted.Xamarin deleted file mode 100644 index e9d4f6a..0000000 --- a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/GettingStarted.Xamarin +++ /dev/null @@ -1,4 +0,0 @@ - - GS\Android\CS\AndroidApp\GettingStarted.html - false - \ No newline at end of file diff --git a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/MainActivity.cs b/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/MainActivity.cs deleted file mode 100644 index 3c6f148..0000000 --- a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/MainActivity.cs +++ /dev/null @@ -1,222 +0,0 @@ -/* Copyright 2017 Tyler Technologies Inc. - * - * Project home page: https://github.com/anotherlab/xamarin-usb-serial-for-android - * Portions of this library are based on usb-serial-for-android (https://github.com/mik3y/usb-serial-for-android). - * Portions of this library are based on Xamarin USB Serial for Android (https://bitbucket.org/lusovu/xamarinusbserial). - */ - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Android.App; -using Android.Content; -using Android.Hardware.Usb; -using Android.Runtime; -using Android.Views; -using Android.Widget; -using Android.OS; -using Android.Util; -using Hoho.Android.UsbSerial.Driver; -using Hoho.Android.UsbSerial.Extensions; -using Hoho.Android.UsbSerial.Util; - - -[assembly: UsesFeature("android.hardware.usb.host")] - - -namespace UsbSerialExampleApp -{ - [Activity(Label = "UsbSerialExampleApp", MainLauncher = true, Icon = "@drawable/icon")] - [IntentFilter(new[] { UsbManager.ActionUsbDeviceAttached })] - [MetaData(UsbManager.ActionUsbDeviceAttached, Resource = "@xml/device_filter")] - public class MainActivity : Activity - { - static readonly string TAG = typeof(MainActivity).Name; - const string ACTION_USB_PERMISSION = "com.hoho.android.usbserial.examples.USB_PERMISSION"; - - UsbManager usbManager; - ListView listView; - TextView progressBarTitle; - ProgressBar progressBar; - - UsbSerialPortAdapter adapter; - BroadcastReceiver detachedReceiver; - UsbSerialPort selectedPort; - - - protected override void OnCreate(Bundle bundle) - { - base.OnCreate(bundle); - - // Set our view from the "main" layout resource - SetContentView(Resource.Layout.Main); - - usbManager = GetSystemService(Context.UsbService) as UsbManager; - listView = FindViewById(Resource.Id.deviceList); - progressBar = FindViewById(Resource.Id.progressBar); - progressBarTitle = FindViewById(Resource.Id.progressBarTitle); - } - - protected override async void OnResume() - { - base.OnResume(); - - adapter = new UsbSerialPortAdapter(this); - listView.Adapter = adapter; - - listView.ItemClick += async (sender, e) => { - await OnItemClick(sender, e); - }; - - await PopulateListAsync(); - - //register the broadcast receivers - detachedReceiver = new UsbDeviceDetachedReceiver(this); - RegisterReceiver(detachedReceiver, new IntentFilter(UsbManager.ActionUsbDeviceDetached)); - } - protected override void OnPause() - { - base.OnPause(); - - // unregister the broadcast receivers - var temp = detachedReceiver; // copy reference for thread safety - if (temp != null) - UnregisterReceiver(temp); - } - internal static Task> FindAllDriversAsync(UsbManager usbManager) - { - // using the default probe table - // return UsbSerialProber.DefaultProber.FindAllDriversAsync (usbManager); - - // adding a custom driver to the default probe table - var table = UsbSerialProber.DefaultProbeTable; - table.AddProduct(0x1b4f, 0x0008, typeof(CdcAcmSerialDriver)); // IOIO OTG - - table.AddProduct(0x09D8, 0x0420, typeof(CdcAcmSerialDriver)); // Elatec TWN4 - - var prober = new UsbSerialProber(table); - return prober.FindAllDriversAsync(usbManager); - } - - async Task OnItemClick(object sender, AdapterView.ItemClickEventArgs e) - { - Log.Info(TAG, "Pressed item " + e.Position); - if (e.Position >= adapter.Count) - { - Log.Info(TAG, "Illegal position."); - return; - } - - // request user permission to connect to device - // NOTE: no request is shown to user if permission already granted - selectedPort = adapter.GetItem(e.Position); - var permissionGranted = await usbManager.RequestPermissionAsync(selectedPort.Driver.Device, this); - if (permissionGranted) - { - // start the SerialConsoleActivity for this device - var newIntent = new Intent(this, typeof(SerialConsoleActivity)); - newIntent.PutExtra(SerialConsoleActivity.EXTRA_TAG, new UsbSerialPortInfo(selectedPort)); - StartActivity(newIntent); - } - } - - async Task PopulateListAsync() - { - ShowProgressBar(); - - Log.Info(TAG, "Refreshing device list ..."); - - var drivers = await FindAllDriversAsync(usbManager); - - adapter.Clear(); - foreach (var driver in drivers) - { - var ports = driver.Ports; - Log.Info(TAG, string.Format("+ {0}: {1} port{2}", driver, ports.Count, ports.Count == 1 ? string.Empty : "s")); - foreach (var port in ports) - adapter.Add(port); - } - - adapter.NotifyDataSetChanged(); - progressBarTitle.Text = string.Format("{0} device{1} found", adapter.Count, adapter.Count == 1 ? string.Empty : "s"); - HideProgressBar(); - Log.Info(TAG, "Done refreshing, " + adapter.Count + " entries found."); - } - - void ShowProgressBar() - { - progressBar.Visibility = ViewStates.Visible; - progressBarTitle.Text = GetString(Resource.String.refreshing); - } - - void HideProgressBar() - { - progressBar.Visibility = ViewStates.Invisible; - } - - - #region UsbSerialPortAdapter implementation - - class UsbSerialPortAdapter : ArrayAdapter - { - public UsbSerialPortAdapter(Context context) - : base(context, global::Android.Resource.Layout.SimpleExpandableListItem2) - { - } - - public override View GetView(int position, View convertView, ViewGroup parent) - { - var row = convertView; - if (row == null) - { - var inflater = Context.GetSystemService(Context.LayoutInflaterService) as LayoutInflater; - row = inflater.Inflate(global::Android.Resource.Layout.SimpleListItem2, null); - } - - var port = this.GetItem(position); - var driver = port.GetDriver(); - var device = driver.GetDevice(); - - var title = string.Format("Vendor {0} Product {1}", - HexDump.ToHexString((short)device.VendorId), - HexDump.ToHexString((short)device.ProductId)); - row.FindViewById(global::Android.Resource.Id.Text1).Text = title; - - var subtitle = device.Class.SimpleName; - row.FindViewById(global::Android.Resource.Id.Text2).Text = subtitle; - - return row; - } - } - - #endregion - - #region UsbDeviceDetachedReceiver implementation - - class UsbDeviceDetachedReceiver - : BroadcastReceiver - { - readonly string TAG = typeof(UsbDeviceDetachedReceiver).Name; - readonly MainActivity activity; - - public UsbDeviceDetachedReceiver(MainActivity activity) - { - this.activity = activity; - } - - public async override void OnReceive(Context context, Intent intent) - { - var device = intent.GetParcelableExtra(UsbManager.ExtraDevice) as UsbDevice; - - Log.Info(TAG, "USB device detached: " + device.DeviceName); - - await activity.PopulateListAsync(); - } - } - - #endregion - - - } -} - diff --git a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Properties/AndroidManifest.xml b/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Properties/AndroidManifest.xml deleted file mode 100644 index 2fc30a2..0000000 --- a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Properties/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Properties/AssemblyInfo.cs b/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Properties/AssemblyInfo.cs deleted file mode 100644 index ba38bad..0000000 --- a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Android.App; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("UsbSerialExampleApp")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("UsbSerialExampleApp")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/AboutResources.txt b/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/AboutResources.txt deleted file mode 100644 index c2bca97..0000000 --- a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/AboutResources.txt +++ /dev/null @@ -1,44 +0,0 @@ -Images, layout descriptions, binary blobs and string dictionaries can be included -in your application as resource files. Various Android APIs are designed to -operate on the resource IDs instead of dealing with images, strings or binary blobs -directly. - -For example, a sample Android app that contains a user interface layout (main.axml), -an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) -would keep its resources in the "Resources" directory of the application: - -Resources/ - drawable/ - icon.png - - layout/ - main.axml - - values/ - strings.xml - -In order to get the build system to recognize Android resources, set the build action to -"AndroidResource". The native Android APIs do not operate directly with filenames, but -instead operate on resource IDs. When you compile an Android application that uses resources, -the build system will package the resources for distribution and generate a class called "R" -(this is an Android convention) that contains the tokens for each one of the resources -included. For example, for the above Resources layout, this is what the R class would expose: - -public class R { - public class drawable { - public const int icon = 0x123; - } - - public class layout { - public const int main = 0x456; - } - - public class strings { - public const int first_string = 0xabc; - public const int second_string = 0xbcd; - } -} - -You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main -to reference the layout/main.axml file, or R.strings.first_string to reference the first -string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/Resource.Designer.cs b/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/Resource.Designer.cs deleted file mode 100644 index 696074e..0000000 --- a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/Resource.Designer.cs +++ /dev/null @@ -1,169 +0,0 @@ -#pragma warning disable 1591 -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -[assembly: global::Android.Runtime.ResourceDesignerAttribute("UsbSerialExampleApp.Resource", IsApplication=true)] - -namespace UsbSerialExampleApp -{ - - - [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] - public partial class Resource - { - - static Resource() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - public static void UpdateIdValues() - { - global::Hoho.Android.UsbSerial.Resource.String.ApplicationName = global::UsbSerialExampleApp.Resource.String.ApplicationName; - global::Hoho.Android.UsbSerial.Resource.String.Hello = global::UsbSerialExampleApp.Resource.String.Hello; - } - - public partial class Attribute - { - - static Attribute() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Attribute() - { - } - } - - public partial class Drawable - { - - // aapt resource value: 0x7f020000 - public const int Icon = 2130837504; - - static Drawable() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Drawable() - { - } - } - - public partial class Id - { - - // aapt resource value: 0x7f060008 - public const int consoleText = 2131099656; - - // aapt resource value: 0x7f060007 - public const int demoScroller = 2131099655; - - // aapt resource value: 0x7f060001 - public const int demoTitle = 2131099649; - - // aapt resource value: 0x7f060004 - public const int deviceList = 2131099652; - - // aapt resource value: 0x7f060002 - public const int progressBar = 2131099650; - - // aapt resource value: 0x7f060000 - public const int progressBarTitle = 2131099648; - - // aapt resource value: 0x7f060003 - public const int separator = 2131099651; - - // aapt resource value: 0x7f060005 - public const int sleepButton = 2131099653; - - // aapt resource value: 0x7f060006 - public const int wakeupButton = 2131099654; - - static Id() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Id() - { - } - } - - public partial class Layout - { - - // aapt resource value: 0x7f030000 - public const int Main = 2130903040; - - // aapt resource value: 0x7f030001 - public const int serial_console = 2130903041; - - static Layout() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Layout() - { - } - } - - public partial class String - { - - // aapt resource value: 0x7f050001 - public const int ApplicationName = 2131034113; - - // aapt resource value: 0x7f050000 - public const int Hello = 2131034112; - - // aapt resource value: 0x7f050002 - public const int app_name = 2131034114; - - // aapt resource value: 0x7f050003 - public const int refreshing = 2131034115; - - // aapt resource value: 0x7f050004 - public const int sleep = 2131034116; - - // aapt resource value: 0x7f050005 - public const int wakeup = 2131034117; - - static String() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private String() - { - } - } - - public partial class Xml - { - - // aapt resource value: 0x7f040000 - public const int device_filter = 2130968576; - - static Xml() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Xml() - { - } - } - } -} -#pragma warning restore 1591 diff --git a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/drawable/Icon.png b/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/drawable/Icon.png deleted file mode 100644 index 8074c4c..0000000 Binary files a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/drawable/Icon.png and /dev/null differ diff --git a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/layout/Main.axml b/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/layout/Main.axml deleted file mode 100644 index 683e784..0000000 --- a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/layout/Main.axml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/layout/serial_console.axml b/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/layout/serial_console.axml deleted file mode 100644 index f97b0de..0000000 --- a/src/UI/UsbSerialForAndroid/UsbSerialExampleApp/Resources/layout/serial_console.axml +++ /dev/null @@ -1,44 +0,0 @@ - - - -