Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions samples/CommunityToolkit.Maui.Sample/AppShell.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ public partial class AppShell : Shell
CreateViewModelMapping<AvatarViewShadowsPage, AvatarViewShadowsViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<AvatarViewShapesPage, AvatarViewShapesViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<AvatarViewSizesPage, AvatarViewSizesViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<PlatformSpecificBarcodeScanningPage, PlatformSpecificBarcodeScanningViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<SharedBarcodeScanningPage, SharedBarcodeScanningViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<BasicMapsPage, BasicMapsViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<CameraViewPage, CameraViewViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<DrawingViewPage, DrawingViewViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.5.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.6" />

<!-- Required for 2 separate examples:
1. Windows implementation of the PlatformBarcodeScanningScenario, in this implementation the PackageReference is only required for Windows
2. Implementation of the SharedBarcodeScanningScenario.
-->
<PackageReference Include="ZXing.Net" Version="0.16.10" />
</ItemGroup>

<PropertyGroup>
Expand All @@ -88,6 +94,11 @@
<ProjectReference Include="..\..\src\CommunityToolkit.Maui.Maps\CommunityToolkit.Maui.Maps.csproj" />
</ItemGroup>

<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
<!-- Required NuGet Packages -->
<PackageReference Include="Xamarin.Google.MLKit.BarcodeScanning" Version="117.3.0.3" />
</ItemGroup>

<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))=='windows' and $(Configuration) == 'Release'">
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
Expand Down
2 changes: 2 additions & 0 deletions samples/CommunityToolkit.Maui.Sample/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ static void RegisterViewsAndViewModels(in IServiceCollection services)

// Add Views Pages + ViewModels
services.AddTransientWithShellRoute<BasicMapsPage, BasicMapsViewModel>();
services.AddTransientWithShellRoute<PlatformSpecificBarcodeScanningPage, PlatformSpecificBarcodeScanningViewModel>();
services.AddTransientWithShellRoute<SharedBarcodeScanningPage, SharedBarcodeScanningViewModel>();
services.AddTransientWithShellRoute<CameraViewPage, CameraViewViewModel>();
services.AddTransientWithShellRoute<DrawingViewPage, DrawingViewViewModel>();
services.AddTransientWithShellRoute<ExpanderPage, ExpanderViewModel>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8" ?>
<pages:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:CommunityToolkit.Maui.Sample.Pages"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:viewModels="clr-namespace:CommunityToolkit.Maui.Sample.ViewModels.Views"
xmlns:sample="clr-namespace:CommunityToolkit.Maui.Sample"
Title="Platform Specific Barcode Scanning"
x:Class="CommunityToolkit.Maui.Sample.Pages.Views.PlatformSpecificBarcodeScanningPage"
x:TypeArguments="viewModels:PlatformSpecificBarcodeScanningViewModel"
x:DataType="viewModels:PlatformSpecificBarcodeScanningViewModel">

<Grid RowDefinitions="200,*,Auto,Auto" ColumnDefinitions="3*,*">
<Label Text="{Binding DetectedCode}" />

<toolkit:CameraView
x:Name="Camera"
Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Grid.RowSpan="3">
<toolkit:CameraView.Scenarios>
<sample:PlatformBarcodeScanningScenario Command="{Binding BindingContext.CodeDetectedCommand, Source={x:Reference Camera}}" />
</toolkit:CameraView.Scenarios>
</toolkit:CameraView>
</Grid>
</pages:BasePage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using CommunityToolkit.Maui.Sample.ViewModels.Views;

namespace CommunityToolkit.Maui.Sample.Pages.Views;

public partial class PlatformSpecificBarcodeScanningPage : BasePage<PlatformSpecificBarcodeScanningViewModel>
{
public PlatformSpecificBarcodeScanningPage(PlatformSpecificBarcodeScanningViewModel viewModel) : base(viewModel)
{
InitializeComponent();
}

protected override async void OnAppearing()
{
base.OnAppearing();

var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(3));
await BindingContext.RefreshCamerasCommand.ExecuteAsync(cancellationTokenSource.Token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8" ?>
<pages:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:CommunityToolkit.Maui.Sample.Pages"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:viewModels="clr-namespace:CommunityToolkit.Maui.Sample.ViewModels.Views"
xmlns:sample="clr-namespace:CommunityToolkit.Maui.Sample"
Title="Shared Barcode Scanning"
x:Class="CommunityToolkit.Maui.Sample.Pages.Views.SharedBarcodeScanningPage"
x:TypeArguments="viewModels:SharedBarcodeScanningViewModel"
x:DataType="viewModels:SharedBarcodeScanningViewModel">

<Grid RowDefinitions="200,*,Auto,Auto" ColumnDefinitions="3*,*">
<Label Text="{Binding DetectedCode}" />

<toolkit:CameraView
x:Name="Camera"
Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Grid.RowSpan="3">
<toolkit:CameraView.Scenarios>
<sample:SharedBarcodeScanningScenario Command="{Binding BindingContext.CodeDetectedCommand, Source={x:Reference Camera}}" />
</toolkit:CameraView.Scenarios>
</toolkit:CameraView>
</Grid>
</pages:BasePage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using CommunityToolkit.Maui.Sample.ViewModels.Views;

namespace CommunityToolkit.Maui.Sample.Pages.Views;

public partial class SharedBarcodeScanningPage : BasePage<SharedBarcodeScanningViewModel>
{
public SharedBarcodeScanningPage(SharedBarcodeScanningViewModel viewModel) : base(viewModel)
{
InitializeComponent();
}

protected override async void OnAppearing()
{
base.OnAppearing();

var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(3));
await BindingContext.RefreshCamerasCommand.ExecuteAsync(cancellationTokenSource.Token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Windows.Input;
using CommunityToolkit.Maui.Core;

namespace CommunityToolkit.Maui.Sample;

/// <summary>
/// A <see cref="PlatformCameraScenario"/> implementation that provides barcode scanning.
/// </summary>
public partial class PlatformBarcodeScanningScenario : PlatformCameraScenario
{
/// <summary>
/// Backing BindableProperty for the <see cref="Command"/> property.
/// </summary>
public static readonly BindableProperty CommandProperty =
BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(PlatformBarcodeScanningScenario));

/// <summary>
/// The Command that should be executed when a barcode is detected.
/// </summary>
public ICommand? Command
{
get => (ICommand?)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System.Diagnostics;
using System.Windows.Input;
using Android.Gms.Tasks;
using Android.Runtime;
using AndroidX.Camera.Core;
using Xamarin.Google.MLKit.Vision.Barcode.Common;
using Xamarin.Google.MLKit.Vision.BarCode;
using Xamarin.Google.MLKit.Vision.Common;

using Scanner = Xamarin.Google.MLKit.Vision.BarCode.BarcodeScanning;

namespace CommunityToolkit.Maui.Sample;

public class BarcodeAnalyzer(Func<ICommand?> commandProvider) : Java.Lang.Object, ImageAnalysis.IAnalyzer
{
IBarcodeScanner? barcodeScanner;
readonly Lock resultsLock = new();

internal void UpdateSymbologies()
{
barcodeScanner?.Dispose();
barcodeScanner = Scanner.GetClient(new BarcodeScannerOptions.Builder()
.SetBarcodeFormats(Barcode.FormatAllFormats)
.Build());
}

public void Analyze(IImageProxy proxy)
{
try
{
ArgumentNullException.ThrowIfNull(proxy?.Image);
ArgumentNullException.ThrowIfNull(barcodeScanner);

using var inputImage = InputImage.FromMediaImage(proxy.Image, 0);
using var task = barcodeScanner.Process(inputImage);
var result = TasksClass.Await(task);

lock (resultsLock)
{
ProcessResults(result);
}

result?.Dispose();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
try
{
proxy?.Close();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
Comment on lines +45 to +59
}

void ProcessResults(Java.Lang.Object? result)
{
if (result is not JavaList javaList)
{
return;
}

if (commandProvider.Invoke() is not Command command)
{
return;
}

foreach (Barcode barcode in javaList)
{
if (barcode is null)
{
continue;
}

if (string.IsNullOrEmpty(barcode.DisplayValue) && string.IsNullOrEmpty(barcode.RawValue))
{
continue;
}

if (command.CanExecute(barcode.RawValue) is true)
{
command.Execute(barcode.RawValue);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using AndroidX.Camera.Core;

namespace CommunityToolkit.Maui.Sample;

/// <summary>
/// Android based implementation of <see cref="PlatformBarcodeScanningScenario"/>.
/// </summary>
public partial class PlatformBarcodeScanningScenario
{
readonly Lazy<UseCase>? lazyUseCase = null;

/// <summary>
/// Initializes a new instance of the <see cref="PlatformBarcodeScanningScenario"/> class.
/// </summary>
public PlatformBarcodeScanningScenario()
{
lazyUseCase = new Lazy<UseCase>(() =>
{
var imageAnalysis =
new ImageAnalysis.Builder()
.SetBackpressureStrategy(ImageAnalysis.StrategyKeepOnlyLatest)
.Build();

imageAnalysis.SetAnalyzer(Android.Runtime.AndroidEnvironment.MainThreadExecutor, new BarcodeAnalyzer(() => Command));

Check failure on line 24 in samples/CommunityToolkit.Maui.Sample/Platforms/Android/PlatformBarcodeScanningScenario.cs

View workflow job for this annotation

GitHub Actions / Build Sample App using Latest .NET SDK (windows-latest)

The type or namespace name 'Runtime' does not exist in the namespace 'CommunityToolkit.Maui.Android' (are you missing an assembly reference?)

Check failure on line 24 in samples/CommunityToolkit.Maui.Sample/Platforms/Android/PlatformBarcodeScanningScenario.cs

View workflow job for this annotation

GitHub Actions / Build Sample App using Latest .NET SDK (windows-latest)

The type or namespace name 'Runtime' does not exist in the namespace 'CommunityToolkit.Maui.Android' (are you missing an assembly reference?)

Check failure on line 24 in samples/CommunityToolkit.Maui.Sample/Platforms/Android/PlatformBarcodeScanningScenario.cs

View workflow job for this annotation

GitHub Actions / Build Sample App using Latest .NET SDK (macos-26)

The type or namespace name 'Runtime' does not exist in the namespace 'CommunityToolkit.Maui.Android' (are you missing an assembly reference?)

Check failure on line 24 in samples/CommunityToolkit.Maui.Sample/Platforms/Android/PlatformBarcodeScanningScenario.cs

View workflow job for this annotation

GitHub Actions / Build Sample App using Latest .NET SDK (macos-26)

The type or namespace name 'Runtime' does not exist in the namespace 'CommunityToolkit.Maui.Android' (are you missing an assembly reference?)

return imageAnalysis;
});
}

/// <inheritdoc/>
public override UseCase UseCase => lazyUseCase?.Value!;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System.Windows.Input;
using AVFoundation;
using CoreFoundation;

namespace CommunityToolkit.Maui.Sample;

/// <summary>
/// Apple based implementation of <see cref="PlatformBarcodeScanningScenario"/>.
/// </summary>
public partial class PlatformBarcodeScanningScenario
{
readonly Lazy<AVCaptureMetadataOutput> lazyOutput;

/// <summary>
/// Initializes a new instance of the <see cref="PlatformBarcodeScanningScenario"/> class.
/// </summary>
public PlatformBarcodeScanningScenario()
{
lazyOutput = new Lazy<AVCaptureMetadataOutput>(() =>
{
var output = new AVCaptureMetadataOutput();

output.SetDelegate(new BarcodeDetectionDelegate(() => Command), DispatchQueue.MainQueue);

return output;
});
}

/// <inheritdoc/>
public override AVCaptureOutput Output => lazyOutput.Value;

/// <inheritdoc/>
public override void OnAttached()
{
// Must apply this here once the output has been attached to the capture session.
lazyOutput.Value.MetadataObjectTypes = lazyOutput.Value.AvailableMetadataObjectTypes;
}
}

sealed class BarcodeDetectionDelegate(Func<ICommand?> commandProvider) : AVCaptureMetadataOutputObjectsDelegate
{
public override void DidOutputMetadataObjects(
AVCaptureMetadataOutput captureOutput,
AVMetadataObject[] metadataObjects,
AVCaptureConnection connection)
{
foreach (var metadataObject in metadataObjects)
{
if (metadataObject is AVMetadataMachineReadableCodeObject readableObject)
{
var code = readableObject.StringValue;

Console.WriteLine($"Metadata object {code} at {string.Join(",", readableObject.Corners?? [])}");

var invokeCommand = commandProvider.Invoke();

if (invokeCommand?.CanExecute(readableObject.StringValue) is true)
{
invokeCommand.Execute(readableObject.StringValue);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Windows.Input;
using CommunityToolkit.Maui.Core;

namespace CommunityToolkit.Maui.Sample;

/// <summary>
///
/// </summary>
public partial class PlatformBarcodeScanningScenario : PlatformCameraScenario
{
public ICommand? Command { get; set; }
}
Comment thread
bijington marked this conversation as resolved.
Loading
Loading