Skip to content
Open
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## [60.2.15]
- [BarcodeScanner] Shows validation failure messages above the scan rectangle when rejected barcode results include an error message, adds success/error haptics, and adds automatic camera zoom tips with `BarcodeScannerStartOptions.Hint`.

## [60.2.14]
- [CollectionView][iOS] Fixed item template root corner radius and stroke being cleared when `AutoCornerRadius` is disabled.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml
version="1.0"
encoding="utf-8"?>

<dui:ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:dui="http://dips.com/mobile.ui"
x:Class="Components.ComponentsSamples.BarcodeScanning.BarcodeFeedbackSample">
<dui:ContentPage.ToolbarItems>
<ToolbarItem IconImageSource="{dui:Icons close_line}"
Clicked="Close" />
</dui:ContentPage.ToolbarItems>
<dui:CameraPreview x:Name="CameraPreview" />
</dui:ContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
using Components.Resources.LocalizedStrings;
using DIPS.Mobile.UI.API.Camera;
using DIPS.Mobile.UI.API.Camera.BarcodeScanning;
using DIPS.Mobile.UI.Resources.Colors;
using DIPS.Mobile.UI.Resources.Styles;
using DIPS.Mobile.UI.Resources.Styles.Label;
using DuiColors = DIPS.Mobile.UI.Resources.Colors.Colors;
using DuiLabel = DIPS.Mobile.UI.Components.Labels.Label;

namespace Components.ComponentsSamples.BarcodeScanning;

public partial class BarcodeFeedbackSample
{
private readonly BarcodeScanner m_barcodeScanner;
private View? m_tooltipView;
private int m_validationAttempt;

public BarcodeFeedbackSample()
{
InitializeComponent();
m_barcodeScanner = new BarcodeScanner();
}

private async Task Start()
{
try
{
await m_barcodeScanner.Start(new BarcodeScannerStartOptions
{
Preview = CameraPreview,
OnCameraFailed = CameraFailed,
OnBarcodeAcceptedAsync = HandleBarcodeAcceptedAsync,
ValidateBarcodeAsync = ValidateBarcodeAsync,
OnBarcodeRejectedAsync = HandleBarcodeRejectedAsync,
Strategy = new ScanRectangleBarcodeScanStrategy
{
WidthFraction = 0.8f,
HeightFraction = 0.3f
},
Hint = new BarcodeScannerHintOptions
{
ShowAutomaticHint = true,
HintText = LocalizedStrings.BarcodeScannerFeedbackFocusHint,
Delay = TimeSpan.FromSeconds(5)
}
});

m_barcodeScanner.SetTooltipView(GetTooltipView());
}
catch (Exception exception)
{
await Application.Current?.MainPage?.DisplayAlert("Failed, check console!", exception.Message, "Ok")!;
Console.WriteLine(exception);
}
}

private View GetTooltipView()
{
if (m_tooltipView is not null)
return m_tooltipView;

m_tooltipView = new DuiLabel
{
Text = LocalizedStrings.BarcodeScannerFeedbackTooltipText,
Style = Styles.GetLabelStyle(LabelStyle.UI200),
TextColor = DuiColors.GetColor(ColorName.color_text_on_button),
HorizontalTextAlignment = TextAlignment.Center,
LineBreakMode = LineBreakMode.WordWrap
};

return m_tooltipView;
}

private void CameraFailed(CameraException e)
{
App.Current.MainPage.DisplayAlert("Something failed!", e.Message, "Ok");
}

private Task HandleBarcodeAcceptedAsync(BarcodeScanResult barcodeScanResult)
{
Console.WriteLine($"Accepted barcode: {barcodeScanResult.Barcode.RawValue}");
return Task.CompletedTask;
}

private async Task<BarcodeScanValidationResult> ValidateBarcodeAsync(string barcode)
{
await Task.Delay(150);

var feedback = (m_validationAttempt++ % 4) switch
{
0 => (Message: LocalizedStrings.BarcodeScannerFeedbackAlreadyScanned, ReasonCode: "already-scanned"),
1 => (Message: LocalizedStrings.BarcodeScannerFeedbackWrongPatient, ReasonCode: "wrong-patient"),
2 => (Message: LocalizedStrings.BarcodeScannerFeedbackNotLabel, ReasonCode: "not-label"),
_ => default
};

return feedback.Message is null
? BarcodeScanValidationResult.Valid()
: BarcodeScanValidationResult.Invalid(feedback.Message, feedback.ReasonCode);
}

private Task HandleBarcodeRejectedAsync(BarcodeScanResult barcodeScanResult, BarcodeScanValidationResult validationResult)
{
Console.WriteLine($"Rejected barcode: {barcodeScanResult.Barcode.RawValue}. {validationResult.ReasonCode}");
return Task.CompletedTask;
}

protected override void OnAppearing()
{
_ = Start();
base.OnAppearing();
}

protected override void OnDisappearing()
{
m_barcodeScanner.StopAndDispose();
base.OnDisappearing();
}

private void Close(object? sender, EventArgs e)
{
Shell.Current.Navigation.PopModalAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<dui:ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:dui="http://dips.com/mobile.ui"
xmlns:localizedStrings="clr-namespace:Components.Resources.LocalizedStrings"
x:Class="Components.ComponentsSamples.BarcodeScanning.BarcodeScanningHubSamples">
<dui:VerticalStackLayout Spacing="0"
Margin="{dui:Thickness Left=content_margin_medium, Right=content_margin_medium, Top=content_margin_large}"
Expand All @@ -25,6 +26,11 @@
Subtitle="Scan rectangle with tooltip view above"
HasBottomDivider="True" />

<dui:NavigationListItem x:Name="FeedbackScannerItem"
Title="{x:Static localizedStrings:LocalizedStrings.BarcodeScannerFeedbackTitle}"
Subtitle="{x:Static localizedStrings:LocalizedStrings.BarcodeScannerFeedbackSubtitle}"
HasBottomDivider="True" />

<dui:NavigationListItem x:Name="CounterScannerItem"
Title="Barcode Counter"
Subtitle="Scan continuously and count barcodes" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public BarcodeScanningHubSamples()
BasicScannerItem.Command = new Command(() => OpenModal(new BarcodeScanningSample()));
OverlayScannerItem.Command = new Command(() => OpenModal(new BarcodeOverlaySample()));
TooltipScannerItem.Command = new Command(() => OpenModal(new BarcodeTooltipSample()));
FeedbackScannerItem.Command = new Command(() => OpenModal(new BarcodeFeedbackSample()));
CounterScannerItem.Command = new Command(() => OpenModal(new BarcodeCounterSample()));
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,27 @@
<data name="BarcodeScanning" xml:space="preserve">
<value>Barcode Scanning</value>
</data>
<data name="BarcodeScannerFeedbackTitle" xml:space="preserve">
<value>Barcode scanner feedback</value>
</data>
<data name="BarcodeScannerFeedbackSubtitle" xml:space="preserve">
<value>Shows rejection reasons above the scan area</value>
</data>
<data name="BarcodeScannerFeedbackTooltipText" xml:space="preserve">
<value>Place the label inside the frame and hold the camera steady.</value>
</data>
<data name="BarcodeScannerFeedbackFocusHint" xml:space="preserve">
<value>Hold the label farther away to focus.</value>
</data>
<data name="BarcodeScannerFeedbackAlreadyScanned" xml:space="preserve">
<value>The label has already been scanned.</value>
</data>
<data name="BarcodeScannerFeedbackWrongPatient" xml:space="preserve">
<value>The label does not belong to this patient.</value>
</data>
<data name="BarcodeScannerFeedbackNotLabel" xml:space="preserve">
<value>The code you scanned is not a label.</value>
</data>
<data name="Barcode" xml:space="preserve">
<value>Barcode</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,27 @@
<data name="BarcodeScanning" xml:space="preserve">
<value>Strekkode skanning</value>
</data>
<data name="BarcodeScannerFeedbackTitle" xml:space="preserve">
<value>Strekkode skanning med tilbakemelding</value>
</data>
<data name="BarcodeScannerFeedbackSubtitle" xml:space="preserve">
<value>Viser avvisningsårsaker over skanneområdet</value>
</data>
<data name="BarcodeScannerFeedbackTooltipText" xml:space="preserve">
<value>Plasser etiketten innenfor rammen og hold kameraet rolig.</value>
</data>
<data name="BarcodeScannerFeedbackFocusHint" xml:space="preserve">
<value>Hold etiketten lenger unna for å fokusere.</value>
</data>
<data name="BarcodeScannerFeedbackAlreadyScanned" xml:space="preserve">
<value>Etiketten er allerede skannet.</value>
</data>
<data name="BarcodeScannerFeedbackWrongPatient" xml:space="preserve">
<value>Etiketten hører ikke til denne pasienten.</value>
</data>
<data name="BarcodeScannerFeedbackNotLabel" xml:space="preserve">
<value>Koden du skannet er ikke en etikett.</value>
</data>
<data name="Barcode" xml:space="preserve">
<value>Strekkode</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using DIPS.Mobile.UI.API.Camera.Preview;
using DIPS.Mobile.UI.API.Vibration;
using DIPS.Mobile.UI.Internal.Logging;

namespace DIPS.Mobile.UI.API.Camera.BarcodeScanning;
Expand Down Expand Up @@ -110,6 +111,7 @@ private async Task<BarcodeScanValidationResult> ValidateBarcodeScanResultAsync(B
private async Task<bool> AcceptBarcodeScanResultAsync(BarcodeScanResult barcodeScanResult, BarcodeScanRectangleOverlay? scanRectangleOverlay)
{
State = BarcodeScannerState.SuccessAnimating;
RunFeedbackVibration(VibrationService.Success);

var successAnimationTask = scanRectangleOverlay?.PlaySuccessAndResetAsync() ?? Task.CompletedTask;
if (ShouldShowProgressCounter && scanRectangleOverlay is not null)
Expand Down Expand Up @@ -150,6 +152,7 @@ private async Task<bool> AcceptBarcodeScanResultAsync(BarcodeScanResult barcodeS
private async Task<bool> RejectBarcodeScanResultAsync(BarcodeScanResult barcodeScanResult, BarcodeScanValidationResult validationResult, BarcodeScanRectangleOverlay? scanRectangleOverlay)
{
State = BarcodeScannerState.FailureAnimating;
RunFeedbackVibration(VibrationService.Error);

if (scanRectangleOverlay is not null)
{
Expand All @@ -163,6 +166,21 @@ private async Task<bool> RejectBarcodeScanResultAsync(BarcodeScanResult barcodeS
return !m_isDisposed && State != BarcodeScannerState.Paused;
}

private static void RunFeedbackVibration(Action vibration)
{
MainThread.BeginInvokeOnMainThread(() =>
{
try
{
vibration.Invoke();
}
catch (Exception exception)
{
DUILogService.LogError<BarcodeScanner>($"Barcode scanner feedback vibration failed: {exception.Message}");
}
});
}

private async Task NotifyBarcodeAcceptedAsync(BarcodeScanResult barcodeScanResult)
{
try
Expand Down
Loading