Skip to content

Commit

Permalink
Added MVVM properties
Browse files Browse the repository at this point in the history
  • Loading branch information
Hector Alonso committed Mar 30, 2023
1 parent 40996e4 commit 4ee2b57
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 33 deletions.
19 changes: 14 additions & 5 deletions Camera.MAUI.Test/SizedPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Camera.MAUI.Test.SizedPage"
xmlns:cv="clr-namespace:Camera.MAUI;assembly=Camera.MAUI"
Title="SizedPage" Background="white">
Title="SizedPage" Background="white" x:Name="sizedPage">
<ScrollView>
<Grid>
<VerticalStackLayout HorizontalOptions="Center" VerticalOptions="Center">
<cv:CameraView x:Name="cameraView" WidthRequest="300" HeightRequest="200"/>
<HorizontalStackLayout HorizontalOptions="Center" Margin="5">
<Label x:Name="cameraLabel" Text="Select a camera:" VerticalOptions="Center" BackgroundColor="White" TextColor="Black"/>
<Picker x:Name="cameraPicker" VerticalOptions="Center" TextColor="Black" SelectedIndexChanged="cameraPicker_SelectedIndexChanged"/>
<Picker x:Name="cameraPicker" VerticalOptions="Center" TextColor="Black" SelectedIndexChanged="CameraPicker_SelectedIndexChanged"/>
</HorizontalStackLayout>
<HorizontalStackLayout HorizontalOptions="Center">
<Label Text="Mirrored" VerticalOptions="Center" TextColor="Black"/>
Expand All @@ -20,6 +20,14 @@
<Label Text="QR Detec." VerticalOptions="Center" TextColor="Black"/>
<CheckBox CheckedChanged="CheckBox3_CheckedChanged" VerticalOptions="Center" Color="Black"/>
</HorizontalStackLayout>
<HorizontalStackLayout HorizontalOptions="Center">
<Label Text="AutoSnap freq: " VerticalOptions="Center" TextColor="Black"/>
<Entry WidthRequest="20" TextChanged="Entry_TextChanged" Keyboard="Numeric" TextColor="Black" />
<Label Text="Take Autosnap" VerticalOptions="Center" TextColor="Black"/>
<CheckBox CheckedChanged="CheckBox_CheckedChanged_1" VerticalOptions="Center" Color="Black"/>
<Label Text="As ISource" VerticalOptions="Center" TextColor="Black"/>
<CheckBox BindingContext="{x:Reference cameraView}" IsChecked="{Binding AutoSnapShotAsImageSource}" VerticalOptions="Center" Color="Black"/>
</HorizontalStackLayout>
<HorizontalStackLayout HorizontalOptions="Center" Margin="2" Spacing="2">
<Label x:Name="zoomLabel" Text="Zoom" VerticalOptions="Center" TextColor="Black"/>
<Stepper x:Name="zoomStepper" BindingContext="{x:Reference cameraView}" Minimum="{Binding MinZoomFactor}" Maximum="1.1" Increment="0.5" Value="1" ValueChanged="Stepper_ValueChanged"/>
Expand All @@ -29,11 +37,12 @@
<Button Text="Stop Camera" Clicked="OnStopClicked" />
<Button Text="Take Photo" Clicked="OnSnapClicked" />
</HorizontalStackLayout>
<Image x:Name="snapPreview"
<Label BindingContext="{x:Reference sizedPage}" Text="{Binding BarcodeText}" TextColor="Black" FontAttributes="Bold" HorizontalOptions="Center" />
<Image x:Name="snapPreview" BindingContext="{x:Reference cameraView}"
Aspect="AspectFit"
Source="dotnet_bot.png"
WidthRequest="300"
HeightRequest="200"
WidthRequest="250"
HeightRequest="100"
HorizontalOptions="Center" />
</VerticalStackLayout>
</Grid>
Expand Down
45 changes: 44 additions & 1 deletion Camera.MAUI.Test/SizedPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
using System.Diagnostics;
using ZXing.QrCode.Internal;

namespace Camera.MAUI.Test;

public partial class SizedPage : ContentPage
{
public static readonly BindableProperty StreamProperty = BindableProperty.Create(nameof(Stream), typeof(Stream), typeof(SizedPage), null, propertyChanged: SetStream);

private static void SetStream(BindableObject bindable, object oldValue, object newValue)
{
if (newValue != oldValue && newValue is Stream str)
{
var control = bindable as SizedPage;
str.Position = 0;
control.snapPreview.RemoveBinding(Image.SourceProperty);
control.snapPreview.Source = ImageSource.FromStream(() => str);
}
}
public string BarcodeText { get; set; } = "No barcode detected";
public Stream Stream {
get { return (Stream)GetValue(StreamProperty); }
set { SetValue(StreamProperty, value); }
}
public SizedPage()
{
InitializeComponent();
Expand All @@ -17,10 +35,14 @@ public SizedPage()
TryHarder = true,
TryInverted = true
};
BindingContext = cameraView;
//this.SetBinding(StreamProperty, nameof(cameraView.SnapShotStream));
}

private void CameraView_BarcodeDetected(object sender, ZXingHelper.BarcodeEventArgs args)
{
BarcodeText = "Barcode: " + args.Result[0].Text;
OnPropertyChanged(nameof(BarcodeText));
Debug.WriteLine("BarcodeText=" + args.Result[0].Text);
}

Expand Down Expand Up @@ -74,7 +96,7 @@ private void Stepper_ValueChanged(object sender, ValueChangedEventArgs e)
if (cameraView != null) cameraView.ZoomFactor = (float)e.NewValue;
}

private void cameraPicker_SelectedIndexChanged(object sender, EventArgs e)
private void CameraPicker_SelectedIndexChanged(object sender, EventArgs e)
{
if (cameraPicker.SelectedItem != null && cameraPicker.SelectedItem is CameraInfo camera)
{
Expand All @@ -87,4 +109,25 @@ private void cameraPicker_SelectedIndexChanged(object sender, EventArgs e)
zoomLabel.IsEnabled = zoomStepper.IsEnabled = true;
}
}

private void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
if (float.TryParse(e.NewTextValue, out float value))
{
cameraView.AutoSnapShotSeconds = value;
if (value <= 0)
snapPreview.RemoveBinding(Image.SourceProperty);
else
snapPreview.SetBinding(Image.SourceProperty, nameof(cameraView.SnapShot));
}
}

private void CheckBox_CheckedChanged_1(object sender, CheckedChangedEventArgs e)
{
if (e.Value && cameraView.AutoSnapShotSeconds <= 0 || !cameraView.AutoSnapShotAsImageSource)
snapPreview.SetBinding(Image.SourceProperty, nameof(cameraView.SnapShot));
else if (cameraView.AutoSnapShotSeconds <= 0)
snapPreview.RemoveBinding(Image.SourceProperty);
cameraView.TakeAutoSnapShot = e.Value;
}
}
29 changes: 21 additions & 8 deletions Camera.MAUI/Apple/MauiCameraView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public float MaxZoomFactor
private readonly DispatchQueue cameraDispacher;
private int frames = 0;
private bool initiated = false;
private bool snapping = false;

public MauiCameraView(CameraView cameraView)
{
Expand Down Expand Up @@ -263,14 +264,15 @@ public void UpdateFlashMode()
}
}
}
public ImageSource GetSnapShot(ImageFormat imageFormat)
public ImageSource GetSnapShot(ImageFormat imageFormat, bool auto = false)
{
ImageSource result = null;

if (started && lastCapture != null)
if (started && lastCapture != null && !snapping)
{
MainThread.BeginInvokeOnMainThread(() =>
MainThread.InvokeOnMainThreadAsync(() =>
{
snapping = true;
try
{
lock (lockCapture)
Expand All @@ -297,13 +299,22 @@ public ImageSource GetSnapShot(ImageFormat imageFormat)
break;
}
stream.Position = 0;
result = ImageSource.FromStream(() => stream);
if (auto)
{
if (cameraView.AutoSnapShotAsImageSource)
result = ImageSource.FromStream(() => stream);
cameraView.SnapShotStream?.Dispose();
cameraView.SnapShotStream = stream;
}
else
result = ImageSource.FromStream(() => stream);
}
}
catch
{
}
});
snapping = false;
}).Wait();
}

return result;
Expand All @@ -315,7 +326,7 @@ public bool SaveSnapShot(ImageFormat imageFormat, string SnapFilePath)
if (started && lastCapture != null)
{
if (File.Exists(SnapFilePath)) File.Delete(SnapFilePath);
MainThread.BeginInvokeOnMainThread(() =>
MainThread.InvokeOnMainThreadAsync(() =>
{
try
{
Expand Down Expand Up @@ -347,7 +358,7 @@ public bool SaveSnapShot(ImageFormat imageFormat, string SnapFilePath)
{
result = false;
}
});
}).Wait();
}
else
result = false;
Expand Down Expand Up @@ -416,7 +427,9 @@ private void ProcessImage(CIImage capture)
lastCapture?.Dispose();
lastCapture = capture;
}
if (cameraView.BarCodeDetectionEnabled && currentFrames >= cameraView.BarCodeDetectionFrameRate)
if (!snapping && cameraView != null && cameraView.AutoSnapShotSeconds > 0 && (DateTime.Now - cameraView.lastSnapshot).TotalSeconds >= cameraView.AutoSnapShotSeconds)
cameraView.RefreshSnapshot(GetSnapShot(cameraView.AutoSnapShotFormat, true));
else if (cameraView.BarCodeDetectionEnabled && currentFrames >= cameraView.BarCodeDetectionFrameRate)
ProccessQR();
}).Start();
}
Expand Down
4 changes: 2 additions & 2 deletions Camera.MAUI/Camera.MAUI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
<Authors>hjam40</Authors>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<Version>1.1.0</Version>
<PackageReleaseNotes>Added CameraInfo properties</PackageReleaseNotes>
<Version>1.2.0</Version>
<PackageReleaseNotes>Added MVVM snapshot properties</PackageReleaseNotes>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)'=='net7.0-ios'">
Expand Down
86 changes: 80 additions & 6 deletions Camera.MAUI/CameraView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ public class CameraView : View, ICameraView
public static readonly BindableProperty BarCodeDetectionFrameRateProperty = BindableProperty.Create(nameof(BarCodeDetectionFrameRate), typeof(int), typeof(CameraView), 10);
public static readonly BindableProperty BarCodeOptionsProperty = BindableProperty.Create(nameof(BarCodeOptions), typeof(BarcodeDecodeOptions), typeof(CameraView), new BarcodeDecodeOptions(), propertyChanged:BarCodeOptionsChanged);
public static readonly BindableProperty ZoomFactorProperty = BindableProperty.Create(nameof(ZoomFactor), typeof(float), typeof(CameraView), 1f);
public static readonly BindableProperty AutoSnapShotSecondsProperty = BindableProperty.Create(nameof(AutoSnapShotSeconds), typeof(float), typeof(CameraView), 0f);
public static readonly BindableProperty AutoSnapShotFormatProperty = BindableProperty.Create(nameof(AutoSnapShotFormat), typeof(ImageFormat), typeof(CameraView), ImageFormat.PNG);
public static readonly BindableProperty SnapShotProperty = BindableProperty.Create(nameof(SnapShot), typeof(ImageSource), typeof(CameraView), null, BindingMode.OneWayToSource);
public static readonly BindableProperty SnapShotStreamProperty = BindableProperty.Create(nameof(SnapShotStream), typeof(Stream), typeof(CameraView), null, BindingMode.OneWayToSource);
public static readonly BindableProperty TakeAutoSnapShotProperty = BindableProperty.Create(nameof(TakeAutoSnapShot), typeof(bool), typeof(CameraView), false, propertyChanged:TakeAutoSnapShotChanged);
public static readonly BindableProperty AutoSnapShotAsImageSourceProperty = BindableProperty.Create(nameof(AutoSnapShotAsImageSource), typeof(bool), typeof(CameraView), false);

/// <summary>
/// Flash mode for take a photo. This is a bindable property.
/// </summary>
Expand Down Expand Up @@ -124,6 +131,58 @@ public float MaxZoomFactor
return 1f;
}
}
/// <summary>
/// Sets how often the SnapShot property is updated in seconds.
/// Default 0: no snapshots are taken
/// WARNING! A low frequency directly impacts over control performance and memory usage (with AutoSnapShotAsImageSource = true)
/// </summary>
public float AutoSnapShotSeconds
{
get { return (float)GetValue(AutoSnapShotSecondsProperty); }
set { SetValue(AutoSnapShotSecondsProperty, value); }
}
/// <summary>
/// Sets the snaphost image format
/// </summary>
public ImageFormat AutoSnapShotFormat
{
get { return (ImageFormat)GetValue(AutoSnapShotFormatProperty); }
set { SetValue(AutoSnapShotFormatProperty, value); }
}
/// <summary>
/// Refreshes according to the frequency set in the AutoSnapShotSeconds property (if AutoSnapShotAsImageSource is set to true)
/// or when GetSnapShot is called or TakeAutoSnapShot is set to true
/// </summary>
public ImageSource SnapShot
{
get { return (ImageSource)GetValue(SnapShotProperty); }
private set { SetValue(SnapShotProperty, value); }
}
/// <summary>
/// Refreshes according to the frequency set in the AutoSnapShotSeconds property or when GetSnapShot is called.
/// WARNING. Each time a snapshot is made, the previous stream is disposed.
/// </summary>
public Stream SnapShotStream
{
get { return (Stream)GetValue(SnapShotStreamProperty); }
internal set { SetValue(SnapShotStreamProperty, value); }
}
/// <summary>
/// Change from false to true refresh SnapShot property
/// </summary>
public bool TakeAutoSnapShot
{
get { return (bool)GetValue(TakeAutoSnapShotProperty); }
set { SetValue(TakeAutoSnapShotProperty, value); }
}
/// <summary>
/// Change from false to true refresh SnapShot property
/// </summary>
public bool AutoSnapShotAsImageSource
{
get { return (bool)GetValue(AutoSnapShotAsImageSourceProperty); }
set { SetValue(AutoSnapShotAsImageSourceProperty, value); }
}
public delegate void BarcodeResultHandler(object sender, BarcodeEventArgs args);
/// <summary>
/// Event launched every time a code is detected in the image if "BarCodeDetectionEnabled" is set to true.
Expand All @@ -135,7 +194,7 @@ public float MaxZoomFactor
public event EventHandler CamerasLoaded;

private readonly BarcodeReaderGeneric BarcodeReader;

internal DateTime lastSnapshot = DateTime.Now;

public CameraView()
{
Expand All @@ -146,6 +205,16 @@ private void CameraView_HandlerChanged(object sender, EventArgs e)
{
if (Handler != null) CamerasLoaded?.Invoke(this, EventArgs.Empty);
}
internal void RefreshSnapshot(ImageSource img)
{
if (AutoSnapShotAsImageSource)
{
SnapShot = img;
OnPropertyChanged(nameof(SnapShot));
}
OnPropertyChanged(nameof(SnapShotStream));
lastSnapshot = DateTime.Now;
}

internal void DecodeBarcode(DecodeDataType data)
{
Expand Down Expand Up @@ -176,13 +245,19 @@ internal void DecodeBarcode(DecodeDataType data)
}
private static void CameraChanged(BindableObject bindable, object oldValue, object newValue)
{
if (newValue != null && oldValue != newValue && bindable is CameraView cameraView && newValue is CameraInfo cam)
if (newValue != null && oldValue != newValue && bindable is CameraView cameraView && newValue is CameraInfo)
{
cameraView.OnPropertyChanged(nameof(MinZoomFactor));
cameraView.OnPropertyChanged(nameof(MaxZoomFactor));
}
}

private static void TakeAutoSnapShotChanged(BindableObject bindable, object oldValue, object newValue)
{
if ((bool)newValue && !(bool)oldValue && bindable is CameraView cameraView)
{
cameraView.RefreshSnapshot(cameraView.GetSnapShot(cameraView.AutoSnapShotFormat));
}
}
private static void BarCodeOptionsChanged(BindableObject bindable, object oldValue, object newValue)
{
if (newValue != null && oldValue != newValue && bindable is CameraView cameraView && newValue is BarcodeDecodeOptions options)
Expand Down Expand Up @@ -230,12 +305,11 @@ public async Task<CameraResult> StopCameraAsync()
/// <param name="imageFormat">The capture image format</param>
public ImageSource GetSnapShot(ImageFormat imageFormat = ImageFormat.PNG)
{
ImageSource result = null;
if (Handler != null && Handler is CameraViewHandler handler)
{
result = handler.GetSnapShot(imageFormat);
SnapShot = handler.GetSnapShot(imageFormat);
}
return result;
return SnapShot;
}
/// <summary>
/// Saves a capture form the active camera playback in a file
Expand Down
Loading

0 comments on commit 4ee2b57

Please sign in to comment.