Skip to content

Commit 6016b7a

Browse files
committed
support multi image capture
1 parent 26b222c commit 6016b7a

26 files changed

Lines changed: 553 additions & 159 deletions

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## [56.1.0]
2+
- [ImageCapture] Added support for capturing multiple images in one session, with optional confirmation of each image.
3+
14
## [56.0.3]
25
- [ItemPicker] Fixed bug where SelectedItem was lost in ContextMenu mode when BindableLayout rebuilds with new VM instances, due to reference equality checks in ItemsSource change handling
36

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml
2+
version="1.0"
3+
encoding="utf-8"?>
4+
5+
<dui:ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
6+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
7+
xmlns:dui="http://dips.com/mobile.ui"
8+
xmlns:helpers="clr-namespace:Components.Helpers"
9+
xmlns:imageCapturing="clr-namespace:Components.ComponentsSamples.ImageCapturing"
10+
x:Class="Components.ComponentsSamples.ImageCapturing.ImageCapturingSamples">
11+
<dui:VerticalStackLayout Spacing="0">
12+
<dui:NavigationListItem Title="Single Capture"
13+
Command="{helpers:NavigationCommand {x:Type imageCapturing:SingleImageCaptureSample}}"
14+
HasBottomDivider="True" />
15+
<dui:NavigationListItem Title="Multi Capture"
16+
Command="{helpers:NavigationCommand {x:Type imageCapturing:MultiImageCaptureSample}}" />
17+
</dui:VerticalStackLayout>
18+
</dui:ContentPage>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Components.ComponentsSamples.ImageCapturing;
2+
3+
public partial class ImageCapturingSamples
4+
{
5+
public ImageCapturingSamples()
6+
{
7+
InitializeComponent();
8+
}
9+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml
2+
version="1.0"
3+
encoding="utf-8"?>
4+
5+
<dui:ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
6+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
7+
xmlns:dui="http://dips.com/mobile.ui"
8+
x:Class="Components.ComponentsSamples.ImageCapturing.MultiImageCaptureModalPage">
9+
10+
<Grid SafeAreaEdges="None">
11+
<dui:CameraPreview x:Name="CameraPreview" />
12+
</Grid>
13+
14+
</dui:ContentPage>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using DIPS.Mobile.UI.API.Camera;
2+
using DIPS.Mobile.UI.API.Camera.ImageCapturing;
3+
using DIPS.Mobile.UI.API.Camera.ImageCapturing.Settings;
4+
5+
namespace Components.ComponentsSamples.ImageCapturing;
6+
7+
public partial class MultiImageCaptureModalPage
8+
{
9+
private readonly bool m_requiresConfirmation;
10+
private readonly List<CapturedImage> m_capturedImages = [];
11+
private readonly ImageCapture m_imageCapture = new();
12+
private readonly TaskCompletionSource<List<CapturedImage>> m_completion =
13+
new(TaskCreationOptions.RunContinuationsAsynchronously);
14+
15+
public MultiImageCaptureModalPage(bool requiresConfirmation)
16+
{
17+
InitializeComponent();
18+
m_requiresConfirmation = requiresConfirmation;
19+
}
20+
21+
public Task<List<CapturedImage>> CompletionTask => m_completion.Task;
22+
23+
protected override async void OnAppearing()
24+
{
25+
base.OnAppearing();
26+
27+
var cameraOptions = new CameraOptions
28+
{
29+
CancelButtonCommand = new Command(OnCancelled)
30+
};
31+
32+
var multiImageCaptureOptions = new MultiImageCaptureOptions
33+
{
34+
RequiresConfirmationOnEachImage = m_requiresConfirmation,
35+
FinishedButtonCommand = new Command(OnFinished)
36+
};
37+
38+
await m_imageCapture.StartMultiImageCapture(CameraPreview, OnImageCaptured, OnCameraFailed,
39+
cameraOptions, multiImageCaptureOptions);
40+
}
41+
42+
protected override void OnDisappearing()
43+
{
44+
base.OnDisappearing();
45+
46+
// Covers swipe-to-dismiss and Android back button. No-op if a handler already completed the TCS.
47+
m_completion.TrySetResult([]);
48+
}
49+
50+
private void OnImageCaptured(CapturedImage capturedImage)
51+
{
52+
m_capturedImages.Add(capturedImage);
53+
}
54+
55+
private async void OnFinished()
56+
{
57+
m_completion.TrySetResult(m_capturedImages);
58+
await Shell.Current.Navigation.PopModalAsync();
59+
}
60+
61+
private async void OnCancelled()
62+
{
63+
m_completion.TrySetResult([]);
64+
await Shell.Current.Navigation.PopModalAsync();
65+
}
66+
67+
private async void OnCameraFailed(CameraException e)
68+
{
69+
await DisplayAlertAsync("Something failed!", e.Message, "Ok");
70+
m_completion.TrySetResult([]);
71+
await Shell.Current.Navigation.PopModalAsync();
72+
}
73+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?xml
2+
version="1.0"
3+
encoding="utf-8"?>
4+
5+
<dui:ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
6+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
7+
xmlns:dui="http://dips.com/mobile.ui"
8+
xmlns:localizedStrings="clr-namespace:DIPS.Mobile.UI.Resources.LocalizedStrings.LocalizedStrings;assembly=DIPS.Mobile.UI"
9+
x:Class="Components.ComponentsSamples.ImageCapturing.MultiImageCaptureSample">
10+
11+
<Grid SafeAreaEdges="None">
12+
<Grid x:Name="SelectionView"
13+
RowDefinitions="Auto,Auto,Auto"
14+
VerticalOptions="Center"
15+
Padding="16"
16+
RowSpacing="12">
17+
<Label Grid.Row="0"
18+
Text="Choose mode to test"
19+
HorizontalTextAlignment="Center"
20+
FontSize="18" />
21+
<Button Grid.Row="1"
22+
x:Name="StartWithoutConfirmationButton"
23+
Text="Without confirmation"
24+
Clicked="OnStartWithoutConfirmationClicked" />
25+
<Button Grid.Row="2"
26+
x:Name="StartWithConfirmationButton"
27+
Text="With confirmation"
28+
Clicked="OnStartWithConfirmationClicked" />
29+
</Grid>
30+
<Grid x:Name="ResultView"
31+
IsVisible="False"
32+
RowDefinitions="Auto,*,Auto"
33+
Padding="16">
34+
<Label x:Name="ResultLabel"
35+
HorizontalTextAlignment="Center"
36+
FontSize="18"
37+
Margin="0,8" />
38+
<ScrollView Grid.Row="1">
39+
<VerticalStackLayout x:Name="ImageList"
40+
Spacing="12" />
41+
</ScrollView>
42+
<Button Grid.Row="2"
43+
Text="{x:Static localizedStrings:DUILocalizedStrings.Close}"
44+
Clicked="OnCloseClicked"
45+
Margin="0,8,0,0" />
46+
</Grid>
47+
</Grid>
48+
49+
</dui:ContentPage>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using DIPS.Mobile.UI.API.Camera.ImageCapturing;
2+
using Image = DIPS.Mobile.UI.Components.Images.Image.Image;
3+
4+
namespace Components.ComponentsSamples.ImageCapturing;
5+
6+
public partial class MultiImageCaptureSample
7+
{
8+
public MultiImageCaptureSample()
9+
{
10+
InitializeComponent();
11+
}
12+
13+
private void OnStartWithoutConfirmationClicked(object? sender, EventArgs e)
14+
{
15+
_ = StartMultiCapture(requiresConfirmation: false);
16+
}
17+
18+
private void OnStartWithConfirmationClicked(object? sender, EventArgs e)
19+
{
20+
_ = StartMultiCapture(requiresConfirmation: true);
21+
}
22+
23+
private async Task StartMultiCapture(bool requiresConfirmation)
24+
{
25+
var modalPage = new MultiImageCaptureModalPage(requiresConfirmation);
26+
await Shell.Current.Navigation.PushModalAsync(modalPage);
27+
var capturedImages = await modalPage.CompletionTask;
28+
29+
var message = capturedImages.Count == 0
30+
? "Cancelled, no images kept"
31+
: $"Captured {capturedImages.Count} image(s)";
32+
ShowResult(message, capturedImages);
33+
}
34+
35+
private void ShowResult(string message, List<CapturedImage> imagesToDisplay)
36+
{
37+
SelectionView.IsVisible = false;
38+
ResultView.IsVisible = true;
39+
ResultLabel.Text = message;
40+
41+
ImageList.Children.Clear();
42+
foreach (var capturedImage in imagesToDisplay)
43+
{
44+
var imageBytes = capturedImage.AsByteArray;
45+
var image = new Image
46+
{
47+
Source = ImageSource.FromStream(() => new MemoryStream(imageBytes)),
48+
HeightRequest = 200,
49+
Aspect = Aspect.AspectFit
50+
};
51+
ImageList.Children.Add(image);
52+
}
53+
}
54+
55+
private void OnCloseClicked(object? sender, EventArgs e)
56+
{
57+
ResultView.IsVisible = false;
58+
ImageList.Children.Clear();
59+
SelectionView.IsVisible = true;
60+
}
61+
}

src/app/Components/ComponentsSamples/ImageCapturing/ImageCaptureSample.xaml renamed to src/app/Components/ComponentsSamples/ImageCapturing/SingleImageCaptureSample.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
77
xmlns:dui="http://dips.com/mobile.ui"
88
xmlns:localizedStrings="clr-namespace:DIPS.Mobile.UI.Resources.LocalizedStrings.LocalizedStrings;assembly=DIPS.Mobile.UI"
9-
x:Class="Components.ComponentsSamples.ImageCapturing.ImageCaptureSample">
9+
x:Class="Components.ComponentsSamples.ImageCapturing.SingleImageCaptureSample">
1010

1111
<dui:ContentPage.ToolbarItems>
1212
<ToolbarItem Text="{x:Static localizedStrings:DUILocalizedStrings.Close}"

src/app/Components/ComponentsSamples/ImageCapturing/ImageCaptureSample.xaml.cs renamed to src/app/Components/ComponentsSamples/ImageCapturing/SingleImageCaptureSample.xaml.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313

1414
namespace Components.ComponentsSamples.ImageCapturing;
1515

16-
public partial class ImageCaptureSample
16+
public partial class SingleImageCaptureSample
1717
{
1818
private readonly List<CapturedImage> m_images;
1919
private readonly ImageCapture m_imageCapture;
2020
private bool m_firstTime = true;
2121

22-
public ImageCaptureSample()
22+
public SingleImageCaptureSample()
2323
{
2424
InitializeComponent();
2525
m_images = [];
@@ -35,13 +35,14 @@ private async void GalleryThumbnails_OnCameraButtonTapped(object? sender, EventA
3535
private async Task StartImageCapture()
3636
{
3737
ToggleCamera(true);
38-
await m_imageCapture.Start(CameraPreview, OnImageCaptured, OnCameraFailed,
39-
settings =>
40-
{
41-
settings.PostCaptureAction = PostCaptureAction.Close;
42-
settings.CanChangeMaxHeightOrWidth = true;
43-
settings.DoneButtonCommand = new Command(Close);
44-
});
38+
39+
var cameraOptions = new CameraOptions
40+
{
41+
CanChangeMaxHeightOrWidth = true,
42+
CancelButtonCommand = new Command(Close)
43+
};
44+
45+
await m_imageCapture.StartSingleImageCapture(CameraPreview, OnImageCaptured, OnCameraFailed, cameraOptions);
4546
}
4647

4748
private void Close()

src/app/Components/REGISTER_YOUR_SAMPLES_HERE.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public static List<Sample> RegisterSamples()
6363
new(SampleType.Components, "Labels", () => new LabelsSamples()),
6464
new(SampleType.Components, LocalizedStrings.TextFields, () => new TextFieldsSamples()),
6565
new(SampleType.Components, LocalizedStrings.BarcodeScanning, () => new BarcodeScanningSample()),
66-
new(SampleType.Components, LocalizedStrings.PhotoCapturing, () => new ImageCaptureSample(), true),
66+
new(SampleType.Components, LocalizedStrings.PhotoCapturing, () => new ImageCapturingSamples()),
6767
new(SampleType.Components, "Tip", () => new TipSamples()),
6868
new(SampleType.Components, "Syntax Highlighting", () => new SyntaxHighlightingSamples()),
6969
new(SampleType.Components, "Amplitude View", () => new AmplitudeViewSamples()),

0 commit comments

Comments
 (0)