Skip to content

Commit

Permalink
Merge pull request #28 from thomasgalliker/extend-filepicker-sample-app
Browse files Browse the repository at this point in the history
Merge extend-filepicker-sample-app into develop
  • Loading branch information
thomasgalliker authored Sep 13, 2024
2 parents b34ea7a + f0d4642 commit 8ed8c0e
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 51 deletions.
67 changes: 56 additions & 11 deletions CameraScanner.Maui/Controls/BarcodeResultOverlay.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using System.Windows.Input;
using CameraScanner.Maui.Utils;
using Microsoft.Maui.Graphics.Text;

namespace CameraScanner.Maui.Controls
{
public partial class BarcodeResultOverlay : GraphicsView
{
private static readonly TimeSpan ClearResultOverlayDelay = TimeSpan.FromMilliseconds(500);

private readonly TaskDelayer taskDelayer;

public BarcodeResultOverlay()
Expand All @@ -16,6 +15,18 @@ public BarcodeResultOverlay()
this.Drawable = this.BarcodeDrawable;
}

protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
//UpdateDrawable(
// this,
// this.BarcodeResults,
// this.StrokeSize,
// this.StrokeColor,
// this.TextColor,
// this.InvalidateDrawableAfter);
}

public static readonly BindableProperty BarcodeDrawableProperty = BindableProperty.Create(
nameof(BarcodeDrawable),
typeof(IBarcodeDrawable),
Expand Down Expand Up @@ -61,7 +72,8 @@ private static void OnBarcodeResultsPropertyChanged(BindableObject bindable, obj
barcodeResults,
barcodeResultOverlay.StrokeSize,
barcodeResultOverlay.StrokeColor,
barcodeResultOverlay.TextColor);
barcodeResultOverlay.TextColor,
barcodeResultOverlay.InvalidateDrawableAfter);
}
}

Expand Down Expand Up @@ -89,7 +101,8 @@ private static void OnStrokeSizePropertyChanged(BindableObject bindable, object
barcodeResultOverlay.BarcodeResults,
strokeSize,
barcodeResultOverlay.StrokeColor,
barcodeResultOverlay.TextColor);
barcodeResultOverlay.TextColor,
barcodeResultOverlay.InvalidateDrawableAfter);
}
}

Expand Down Expand Up @@ -117,7 +130,8 @@ private static void OnStrokeColorPropertyChanged(BindableObject bindable, object
barcodeResultOverlay.BarcodeResults,
barcodeResultOverlay.StrokeSize,
strokeColor,
barcodeResultOverlay.TextColor);
barcodeResultOverlay.TextColor,
barcodeResultOverlay.InvalidateDrawableAfter);
}
}

Expand Down Expand Up @@ -145,7 +159,8 @@ private static void OnTextColorPropertyChanged(BindableObject bindable, object o
barcodeResultOverlay.BarcodeResults,
barcodeResultOverlay.StrokeSize,
barcodeResultOverlay.StrokeColor,
textColor);
textColor,
barcodeResultOverlay.InvalidateDrawableAfter);
}
}

Expand All @@ -155,17 +170,47 @@ public Color TextColor
set => this.SetValue(TextColorProperty, value);
}

public static readonly BindableProperty InvalidateDrawableAfterProperty = BindableProperty.Create(
nameof(InvalidateDrawableAfter),
typeof(TimeSpan?),
typeof(BarcodeResultOverlay),
TimeSpan.FromMilliseconds(500),
propertyChanged: OnInvalidateDrawableAfterPropertyChanged);


private static void OnInvalidateDrawableAfterPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var barcodeResultOverlay = (BarcodeResultOverlay)bindable;

UpdateDrawable(
barcodeResultOverlay,
barcodeResultOverlay.BarcodeResults,
barcodeResultOverlay.StrokeSize,
barcodeResultOverlay.StrokeColor,
barcodeResultOverlay.TextColor,
newValue as TimeSpan?);
}

public TimeSpan? InvalidateDrawableAfter
{
get => (TimeSpan?)this.GetValue(InvalidateDrawableAfterProperty);
set => this.SetValue(InvalidateDrawableAfterProperty, value);
}

private static void UpdateDrawable(BarcodeResultOverlay barcodeResultOverlay,
BarcodeResult[] barcodeResults, float strokeSize, Color strokeColor, Color textColor)
BarcodeResult[] barcodeResults, float strokeSize, Color strokeColor, Color textColor, TimeSpan? invalidateDrawableAfter)
{
barcodeResultOverlay.BarcodeDrawable.Update(barcodeResults, strokeSize, strokeColor, textColor);
barcodeResultOverlay.Invalidate();

barcodeResultOverlay.taskDelayer.RunWithDelay(ClearResultOverlayDelay, () =>
if (invalidateDrawableAfter is TimeSpan delay)
{
barcodeResultOverlay.BarcodeDrawable.Reset();
MainThread.BeginInvokeOnMainThread(barcodeResultOverlay.Invalidate);
});
barcodeResultOverlay.taskDelayer.RunWithDelay(delay, () =>
{
barcodeResultOverlay.BarcodeDrawable.Reset();
MainThread.BeginInvokeOnMainThread(barcodeResultOverlay.Invalidate);
});
}
}

private void TapGestureRecognizer_OnTapped(object sender, TappedEventArgs e)
Expand Down
11 changes: 9 additions & 2 deletions CameraScanner.Maui/Platforms/Android/Services/BarcodeScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,15 @@ internal static void ProcessBarcodeResult(Java.Lang.Object inputResults, HashSet
}
else
{
previewRect = RectF.Zero;
cornerPoints = [];
previewRect = rectF.AsRectangleF();
cornerPoints = barcode.GetCornerPoints()
.Select(p =>
{
var pointF = new global::Android.Graphics.PointF(p);
return pointF;
})
.Select(p => new Point(p.X, p.Y))
.ToArray();
}


Expand Down
2 changes: 1 addition & 1 deletion Samples/CameraDemoApp/CameraDemoApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<MauiVersion>8.0.82</MauiVersion>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Nullable>disable</Nullable>

<!-- Display name -->
<ApplicationTitle>CameraDemoApp</ApplicationTitle>
Expand Down
1 change: 1 addition & 0 deletions Samples/CameraDemoApp/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public static MauiApp CreateMauiApp()
builder.Services.AddSingleton<INavigationService, MauiNavigationService>();
builder.Services.AddSingleton<IDialogService, DialogService>();
builder.Services.AddSingleton<ILauncher>(_ => Launcher.Default);
builder.Services.AddSingleton<IMediaPicker>(_ => MediaPicker.Default);

// Register pages and view models
builder.Services.AddTransient<MainPage>();
Expand Down
2 changes: 2 additions & 0 deletions Samples/CameraDemoApp/Resources/Styles/Converters.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@
FalseValue="Pause"
TrueValue="Resume" />

<c:StringIsNotNullOrEmptyConverter x:Key="StringIsNotNullOrEmptyConverter" />

</ResourceDictionary>
41 changes: 28 additions & 13 deletions Samples/CameraDemoApp/ViewModels/FilePickerViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,30 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.Logging;
using Microsoft.Maui.Graphics.Platform;

namespace CameraDemoApp.ViewModels
{
public class FilePickerViewModel : ObservableObject
{
private readonly ILogger logger;
private readonly IMediaPicker mediaPicker;
private readonly IBarcodeScanner barcodeScanner;

private PlatformImage image;
private ImageSource image;
private IAsyncRelayCommand pickPhotoCommand;
private BarcodeResult[] barcodeResults;

public FilePickerViewModel(
ILogger<FilePickerViewModel> logger,
IMediaPicker mediaPicker,
IBarcodeScanner barcodeScanner)
{
this.logger = logger;
this.mediaPicker = mediaPicker;
this.barcodeScanner = barcodeScanner;
}

public PlatformImage Image
public ImageSource Image
{
get => this.image;
private set => this.SetProperty(ref this.image, value);
Expand All @@ -32,7 +34,26 @@ public PlatformImage Image
public BarcodeResult[] BarcodeResults
{
get => this.barcodeResults;
private set => this.SetProperty(ref this.barcodeResults, value);
private set
{
if (this.SetProperty(ref this.barcodeResults, value))
{
this.OnPropertyChanged(nameof(this.BarcodeResultsCountText));
}
}
}

public string BarcodeResultsCountText
{
get
{
if (this.BarcodeResults is not BarcodeResult[] barcodeResults)
{
return null;
}

return string.Format("Number of barcodes found: {0}", barcodeResults.Length);
}
}

public IAsyncRelayCommand PickPhotoCommand
Expand All @@ -45,25 +66,19 @@ private async Task PickPhotoAsync()
try
{
var options = new MediaPickerOptions { Title = "Test picker" };
var fileResult = await MediaPicker.PickPhotoAsync(options);
var fileResult = await this.mediaPicker.PickPhotoAsync(options);
if (fileResult != null)
{
// save the file into local storage
//var localFilePath = Path.Combine(FileSystem.CacheDirectory, fileResult.FileName);

//using var sourceStream = await fileResult.OpenReadAsync();
//using var localFileStream = File.OpenWrite(localFilePath);

//await sourceStream.CopyToAsync(localFileStream);

var barcodeResults = await this.barcodeScanner.ScanFromImageAsync(fileResult);
this.Image = ImageSource.FromFile(fileResult.FullPath);
this.BarcodeResults = barcodeResults.ToArray();
}
}
catch (Exception ex)
{
this.logger.LogError(ex, "PickPhotoAsync failed with exception");
this.BarcodeResults = [];
this.Image = null;
}
}
}
Expand Down
71 changes: 49 additions & 22 deletions Samples/CameraDemoApp/Views/FilePickerPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,60 @@

<Grid
Padding="16"
RowDefinitions="*, Auto, Auto"
RowSpacing="16">
RowDefinitions="*, Auto"
RowSpacing="8">

<Image
Grid.Row="0"
Source="{Binding Image}" />
<Grid Grid.Row="0">
<Image
Grid.Row="0"
Source="{Binding Image}"
BackgroundColor="{StaticResource Gray100}"
SizeChanged="Image_SizeChanged"
HorizontalOptions="Fill"
VerticalOptions="Start" />

<!-- BarcodeResultOverlay does not (yet) properly work with photo picker -->
<!--<c:BarcodeResultOverlay
Grid.Row="0"
BarcodeResults="{Binding BarcodeResults}"
InvalidateDrawableAfter="{x:Null}"
SizeChanged="BarcodeResultOverlay_SizeChanged"
StrokeColor="Red"
StrokeSize="4">
<c:BarcodeResultOverlay.BarcodeDrawable>
<c:BoundingBoxBarcodeDrawable />
</c:BarcodeResultOverlay.BarcodeDrawable>
</c:BarcodeResultOverlay>-->
</Grid>

<VerticalStackLayout
Grid.Row="1"
BindableLayout.ItemTemplate="{StaticResource BarcodeResultItemTemplate}"
BindableLayout.ItemsSource="{Binding BarcodeResults}" />
Spacing="8">

<VerticalStackLayout
Grid.Row="2"
VerticalOptions="Start">

<Grid
RowDefinitions="Auto"
RowSpacing="8"
VerticalOptions="Fill">

<Button
Grid.Row="0"
Command="{Binding PickPhotoCommand}"
Text="Pick Photo"
HorizontalOptions="Fill" />
</Grid>
<ScrollView MaximumHeightRequest="200">
<VerticalStackLayout
BindableLayout.ItemTemplate="{StaticResource BarcodeResultItemTemplate}"
BindableLayout.ItemsSource="{Binding BarcodeResults}"
Spacing="4" />
</ScrollView>

<Label
FontAttributes="Bold"
IsVisible="{Binding BarcodeResultsCountText, Converter={StaticResource StringIsNotNullOrEmptyConverter}}"
Text="{Binding BarcodeResultsCountText}" />

<VerticalStackLayout VerticalOptions="Start">
<Grid
RowDefinitions="Auto"
RowSpacing="8"
VerticalOptions="Fill">
<Button
Grid.Row="0"
Command="{Binding PickPhotoCommand}"
Text="Pick Photo"
HorizontalOptions="Fill" />
</Grid>
</VerticalStackLayout>
</VerticalStackLayout>
</Grid>
</ContentPage>
14 changes: 14 additions & 0 deletions Samples/CameraDemoApp/Views/FilePickerPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Diagnostics;

namespace CameraDemoApp.Views;

public partial class FilePickerPage : ContentPage
Expand All @@ -6,4 +8,16 @@ public FilePickerPage()
{
this.InitializeComponent();
}

private void Image_SizeChanged(object sender, EventArgs e)
{
var visualElement = (VisualElement)sender;
Debug.WriteLine($"Image_SizeChanged: w:{visualElement.Width} x h:{visualElement.Height}");
}

private void BarcodeResultOverlay_SizeChanged(object sender, EventArgs e)
{
var visualElement = (VisualElement)sender;
Debug.WriteLine($"BarcodeResultOverlay_SizeChanged: w:{visualElement.Width} x h:{visualElement.Height}");
}
}
Loading

0 comments on commit 8ed8c0e

Please sign in to comment.