diff --git a/MigrationForms/MigrationFormsExampleToMAUI.sln b/MigrationForms/MigrationFormsExampleToMAUI.sln
new file mode 100644
index 0000000..f7503f5
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI.sln
@@ -0,0 +1,16 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MigrationFormsExampleToMAUI", "MigrationFormsExampleToMAUI\MigrationFormsExampleToMAUI.csproj", "{D2BB8C7B-5986-4FAA-93E2-5C4C2F5D8ACA}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D2BB8C7B-5986-4FAA-93E2-5C4C2F5D8ACA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D2BB8C7B-5986-4FAA-93E2-5C4C2F5D8ACA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D2BB8C7B-5986-4FAA-93E2-5C4C2F5D8ACA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D2BB8C7B-5986-4FAA-93E2-5C4C2F5D8ACA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/App.xaml b/MigrationForms/MigrationFormsExampleToMAUI/App.xaml
new file mode 100644
index 0000000..afff2a6
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/App.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/App.xaml.cs b/MigrationForms/MigrationFormsExampleToMAUI/App.xaml.cs
new file mode 100644
index 0000000..5bea4d8
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/App.xaml.cs
@@ -0,0 +1,27 @@
+using MigrationFormsExampleToMAUI.Model;
+
+namespace MigrationFormsExampleToMAUI;
+
+public partial class App : Application
+{
+ public static Color ScanbotRed = Color.FromRgb(200, 25, 60);
+
+ public App()
+ {
+ InitializeComponent();
+
+#pragma warning disable CS4014
+ // There's no requirement to await this, can just disable warning
+ InitializeAsync();
+#pragma warning restore CS4014
+
+ MainPage = new AppShell();
+ }
+
+ async Task InitializeAsync()
+ {
+ await PageStorage.Instance.InitializeAsync();
+ await MigrationFormsExampleToMAUI.Model.Pages.Instance.LoadFromStorage();
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/AppShell.xaml b/MigrationForms/MigrationFormsExampleToMAUI/AppShell.xaml
new file mode 100644
index 0000000..ce13313
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/AppShell.xaml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/AppShell.xaml.cs b/MigrationForms/MigrationFormsExampleToMAUI/AppShell.xaml.cs
new file mode 100644
index 0000000..6fc7258
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/AppShell.xaml.cs
@@ -0,0 +1,9 @@
+namespace MigrationFormsExampleToMAUI;
+
+public partial class AppShell : Shell
+{
+ public AppShell()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/DependencyServices/MultiImagePicker.cs b/MigrationForms/MigrationFormsExampleToMAUI/DependencyServices/MultiImagePicker.cs
new file mode 100644
index 0000000..d7b033b
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/DependencyServices/MultiImagePicker.cs
@@ -0,0 +1,12 @@
+namespace MigrationFormsExampleToMAUI.DependencyServices;
+
+public partial class MultiImagePicker
+{
+ ///
+ /// Get photos paths from gallery with completion handler
+ ///
+ ///
+ public partial void PickPhotosAsync(Action> completionHandler);
+}
+
+
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/FilterPage.cs b/MigrationForms/MigrationFormsExampleToMAUI/FilterPage.cs
new file mode 100644
index 0000000..300da21
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/FilterPage.cs
@@ -0,0 +1,67 @@
+using MigrationFormsExampleToMAUI.Utils;
+using ScanbotSDK.MAUI.Constants;
+using ScanbotSDK.MAUI.Services;
+
+namespace MigrationFormsExampleToMAUI;
+
+public class FilterPage: ContentPage
+{
+ public StackLayout Container { get; set; }
+ public Image Image { get; set; }
+ public Button FilterButton { get; set; }
+
+ public ImageFilter CurrentFilter { get; set; }
+
+ public IScannedPage CurrentPage { get; set; }
+
+ public FilterPage(IScannedPage current)
+ {
+ CurrentPage = current;
+
+ Container = new StackLayout();
+ Container.Orientation = StackOrientation.Vertical;
+ Container.BackgroundColor = Colors.White;
+
+ Image = new Image
+ {
+ HorizontalOptions = LayoutOptions.FillAndExpand,
+ BackgroundColor = Colors.LightGray,
+ Aspect = Aspect.AspectFit
+ };
+ Image.SizeChanged += delegate
+ {
+ // Don't allow images larger than half of the screen
+ Image.HeightRequest = Content.Height / 2;
+ };
+ Container.Children.Add(Image);
+
+ FilterButton = new Button
+ {
+ Text = "None",
+ HorizontalOptions = LayoutOptions.FillAndExpand,
+ HeightRequest = 50,
+ Margin = new Thickness(10, 10, 10, 10)
+ };
+ FilterButton.Pressed += FilterButtonClicked;
+ Container.Children.Add(FilterButton);
+
+ Image.Source = CurrentPage.Document;
+
+ Content = Container;
+ }
+
+ async void FilterButtonClicked(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+
+ var action = await DisplayActionSheet(
+ "Filter", "Cancel", null, Enum.GetNames(typeof(ImageFilter))
+ );
+
+ ImageFilter filter;
+ Enum.TryParse(action, out filter);
+ CurrentFilter = filter;
+
+ Image.Source = await ScanbotSDK.MAUI.ScanbotSDK.SDKService.ApplyImageFilterAsync(Image.Source, filter);
+ }
+}
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/MainPage.xaml b/MigrationForms/MigrationFormsExampleToMAUI/MainPage.xaml
new file mode 100644
index 0000000..6910e23
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/MainPage.xaml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/MainPage.xaml.cs b/MigrationForms/MigrationFormsExampleToMAUI/MainPage.xaml.cs
new file mode 100644
index 0000000..da6957c
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/MainPage.xaml.cs
@@ -0,0 +1,372 @@
+
+using MigrationFormsExampleToMAUI.DependencyServices;
+using MigrationFormsExampleToMAUI.Model;
+using MigrationFormsExampleToMAUI.Utils;
+using MigrationFormsExampleToMAUI.Pages;
+using ScanbotSDK.MAUI;
+using ScanbotSDK.MAUI.Configurations;
+using ScanbotSDK.MAUI.Constants;
+using ScanbotSDK.MAUI.Models;
+
+using BarcodeConfirmationDialogConfiguration = ScanbotSDK.MAUI.Configurations.BarcodeConfirmationDialogConfiguration;
+
+namespace MigrationFormsExampleToMAUI;
+
+partial class MainPage : ContentPage
+{
+ StackLayout Container { get; set; }
+
+ public MainPage()
+ {
+ BackgroundColor = Colors.White;
+
+ Title = "SCANBOT SDK EXAMPLE";
+
+ Container = new StackLayout();
+ Container.Orientation = StackOrientation.Vertical;
+ Container.BackgroundColor = Colors.White;
+
+ var table = new TableView();
+ table.BackgroundColor = Colors.White;
+ Container.Children.Add(table);
+
+ table.Root = new TableRoot();
+
+ table.Root.Add(new TableSection("DOCUMENT SCANNER")
+ {
+ ViewUtils.CreateCell("Scan Document", ScanningUIClicked),
+ ViewUtils.CreateCell("Import image & Detect Document", ImportButtonClicked),
+ ViewUtils.CreateCell("View Image Results", ViewImageResultsClicked),
+ });
+ table.Root.Add(new TableSection("BARCODE DETECTOR")
+ {
+ ViewUtils.CreateCell("Scan QR- & Barcodes", BarcodeScannerClicked),
+ ViewUtils.CreateCell("Scan Multiple QR- & Barcodes", BatchBarcodeScannerClicked),
+ ViewUtils.CreateCell("Import Image & Detect Barcodes", ImportandDetectBarcodesClicked),
+ ViewUtils.CreateCell("Import images & Detect Barcodes", ImportImagesAndDetectBarcodesTapped),
+ ViewUtils.CreateCell("Set Barcode Formats Filter", SetBarcodeFormatsFilterClicked),
+ });
+ table.Root.Add(new TableSection("DATA DETECTORS")
+ {
+ ViewUtils.CreateCell("MRZ Scanner", MRZScannerClicked),
+ ViewUtils.CreateCell("EHIC Scanner", EHICScannerClicked),
+ ViewUtils.CreateCell("Generic Document Recognizer", GenericDocumentRecognizerClicked),
+ ViewUtils.CreateCell("Check Recognizer", CheckRecognizerClicked),
+ ViewUtils.CreateCell("Text Data Scanner", TextDataScannerClicked),
+ ViewUtils.CreateCell("Vin Data Scanner", VinDataScannerClicked),
+ });
+ table.Root.Add(new TableSection("MISCELLANEOUS")
+ {
+ ViewUtils.CreateCell("View License Info", ViewLicenseInfoClicked),
+ ViewUtils.CreateCell("Learn more about Scanbot SDK", LearnMoreClicked, App.ScanbotRed),
+ ViewUtils.CreateCopyrightCell()
+ });
+
+ Content = Container;
+ }
+
+ /**
+ * DOCUMENT SCANNER
+ */
+ async void ScanningUIClicked(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+
+ var configuration = new DocumentScannerConfiguration
+ {
+ CameraPreviewMode = CameraPreviewMode.FitIn,
+ IgnoreBadAspectRatio = true,
+ MultiPageEnabled = true,
+ PolygonColor = Colors.Red,
+ PolygonColorOK = Colors.Green,
+ BottomBarBackgroundColor = Colors.Blue,
+ PageCounterButtonTitle = "%d Page(s)",
+ //DocumentImageSizeLimit = new Size(2000, 3000),
+ // see further customization configs...
+ };
+ var result = await ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchDocumentScannerAsync(configuration);
+ if (result.Status == OperationResult.Ok)
+ {
+ foreach (var page in result.Pages)
+ {
+
+ await MigrationFormsExampleToMAUI.Model.Pages.Instance.Add(page);
+
+ // If encryption is enabled, load the decrypted document.
+ // Else accessible via page.Document
+ var quality = await ScanbotSDK.MAUI.ScanbotSDK.SDKService.DetectDocumentQualityAsync(await page.DecryptedDocument());
+ Console.WriteLine("Estimated quality for detected document: " + quality);
+ }
+
+ await Navigation.PushAsync(new ImageResultsPage());
+ }
+ }
+
+ async void ImportButtonClicked(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+
+ ImageSource source = await ScanbotSDK.MAUI.ScanbotSDK.PickerService.PickImageAsync();
+ if (source != null)
+ {
+ // Import the selected image as original image and create a Page object
+ var importedPage = await ScanbotSDK.MAUI.ScanbotSDK.SDKService.CreateScannedPageAsync(source);
+
+ // Run document detection on it
+ await importedPage.DetectDocumentAsync();
+ await MigrationFormsExampleToMAUI.Model.Pages.Instance.Add(importedPage);
+ await Navigation.PushAsync(new ImageResultsPage());
+ }
+ }
+
+ void ViewImageResultsClicked(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+ Navigation.PushAsync(new ImageResultsPage());
+ }
+
+ /**
+ * BARCODE DETECTOR
+ */
+ async void BarcodeScannerClicked(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+
+ var config = new BarcodeScannerConfiguration();
+ config.BarcodeFormats = BarcodeTypes.Instance.AcceptedTypes;
+ config.ConfirmationDialogConfiguration = GetConfirmationDialog();
+ config.OverlayConfiguration = GetSelectionOverlayConfig();
+ var result = await ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.OpenBarcodeScannerView(config);
+ if (result.Status == OperationResult.Ok)
+ {
+ if (result.Barcodes.Count == 0)
+ {
+ ViewUtils.Alert(this, "Oops!", "No barcodes found, please try again");
+ return;
+ }
+
+ var source = result.Image;
+ var barcodes = result.Barcodes;
+
+ await Navigation.PushAsync(new BarcodeResultsPage(source, barcodes));
+ }
+ }
+
+ async void BatchBarcodeScannerClicked(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+ var config = new BatchBarcodeScannerConfiguration();
+ config.BarcodeFormats = BarcodeTypes.Instance.AcceptedTypes;
+ config.OverlayConfiguration = GetSelectionOverlayConfig();
+ var result = await ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.OpenBatchBarcodeScannerView(config);
+ if (result.Status == OperationResult.Ok)
+ {
+ if (result.Barcodes.Count == 0)
+ {
+ ViewUtils.Alert(this, "Oops!", "No barcodes found, please try again");
+ return;
+ }
+
+ await Navigation.PushAsync(new BarcodeResultsPage(null, result.Barcodes));
+ }
+ }
+
+ async void ImportandDetectBarcodesClicked(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+ ImageSource source = await ScanbotSDK.MAUI.ScanbotSDK.PickerService.PickImageAsync();
+
+ if (source != null)
+ {
+ var barcodes = await ScanbotSDK.MAUI.ScanbotSDK.DetectionService.DetectBarcodesFrom(source);
+ await Navigation.PushAsync(new BarcodeResultsPage(source, barcodes));
+ }
+ }
+
+ void SetBarcodeFormatsFilterClicked(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+ Navigation.PushAsync(new BarcodeSelectorPage());
+ }
+
+ /**
+ * WORKFLOWS
+ */
+ async void MRZScannerClicked(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+
+ MrzScannerConfiguration configuration = new MrzScannerConfiguration
+ {
+ FinderWidthRelativeToDeviceWidth = 5,
+ FinderHeightRelativeToDeviceWidth = 1,
+ };
+
+ var result = await ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchMrzScannerAsync(configuration);
+ if (result.Status == OperationResult.Ok)
+ {
+ var message = SDKUtils.ParseMRZResult(result);
+ ViewUtils.Alert(this, "MRZ Scanner result", message);
+ }
+ }
+
+ async void EHICScannerClicked(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+
+ var configuration = new HealthInsuranceCardConfiguration { };
+ var result = await ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchHealthInsuranceCardScannerAsync(configuration);
+ if (result.Status == OperationResult.Ok)
+ {
+ var message = SDKUtils.ParseEHICResult(result);
+ ViewUtils.Alert(this, "MRZ Scanner result", message);
+ }
+ }
+
+ async void GenericDocumentRecognizerClicked(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+
+ var configuration = new GenericDocumentRecognizerConfiguration
+ {
+ DocumentType = GenericDocumentType.DeIdCard
+ };
+ var result = await ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchGenericDocumentRecognizerAsync(configuration);
+ if (result.Status == OperationResult.Ok)
+ {
+ var message = SDKUtils.ParseGDRResult(result);
+ ViewUtils.Alert(this, "GDR Result", message);
+ }
+ }
+
+ async void CheckRecognizerClicked(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+
+ var configuration = new CheckRecognizerConfiguration
+ {
+ AcceptedCheckStandards = new List() {
+ CheckStandard.USA,
+ CheckStandard.AUS,
+ CheckStandard.IND,
+ CheckStandard.FRA,
+ CheckStandard.KWT,
+ }
+ };
+
+ var result = await ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchCheckRecognizerAsync(configuration);
+ if (result.Status == OperationResult.Ok)
+ {
+ var message = SDKUtils.ParseCheckResult(result);
+ ViewUtils.Alert(this, "Check Result", message);
+ }
+
+ }
+
+ async void TextDataScannerClicked(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+
+ var step = new TextDataScannerStep("Scan your text", "", 1.0f, new AspectRatio(3, 1));
+ var configuration = new TextDataScannerConfiguration(step)
+ {
+ CancelButtonTitle = "Cancel",
+ FlashEnabled = false,
+ };
+
+ var result = await ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchTextDataScannerAsync(configuration);
+ if (result.Status == OperationResult.Ok)
+ {
+ var message = SDKUtils.ParseTextDataScannerResult(result);
+ ViewUtils.Alert(this, "Text Data Scanner Result", message);
+ }
+ }
+
+ void ViewLicenseInfoClicked(object sender, EventArgs e)
+ {
+ bool valid = ScanbotSDK.MAUI.ScanbotSDK.IsLicenseValid;
+ var message = "Scanbot SDK License is valid";
+ if (!valid)
+ {
+ message = "Scanbot SDK License is expired";
+ }
+ ViewUtils.Alert(this, "License info", message);
+ }
+
+ async void LearnMoreClicked(object sender, EventArgs e)
+ {
+ var uri = new Uri("https://scanbot.io/sdk");
+ await Browser.OpenAsync(uri, BrowserLaunchMode.SystemPreferred);
+ }
+
+ ///
+ /// Import images and detect barcodes from all the images.
+ /// Navigates all the barcode result list to the next page.
+ ///
+ ///
+ ///
+ ///
+ void ImportImagesAndDetectBarcodesTapped(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+ new MultiImagePicker().PickPhotosAsync(completionHandler: async (imageSources) =>
+ {
+ List barcodes = null;
+ bool canNavigate = false;
+ var filteredImageSource = imageSources.Where(source => !source.IsEmpty)?.ToList() ?? new List();
+ if (filteredImageSource.Count > 0)
+ {
+ canNavigate = true;
+ barcodes = await ScanbotSDK.MAUI.ScanbotSDK.DetectionService.DetectBarcodesFrom(filteredImageSource);
+ }
+
+ if (imageSources == null || imageSources.Any(source => source.IsEmpty))
+ {
+ await DisplayAlert("Alert", "Unable to pick at least 1 of the images.", "Ok");
+ }
+
+ if (canNavigate)
+ {
+ await Navigation.PushAsync(new BarcodeResultsPage(barcodes));
+ }
+ });
+ }
+
+ private SelectionOverlayConfiguration GetSelectionOverlayConfig()
+ {
+ var config = new SelectionOverlayConfiguration(false, BarcodeTextFormat.Code,
+ Colors.Yellow, Colors.Yellow, Colors.Black,
+ Colors.Red, Colors.Red, Colors.Black);
+ return config;
+ }
+
+ private async void VinDataScannerClicked(object sender, EventArgs e)
+ {
+ var configuration = new VINScannerConfiguration();
+ configuration.FinderAspectRatio = new AspectRatio(7, 1);
+ configuration.GuidanceText = "Please place the number inside finder area.";
+ var result = await ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchVINScannerAsync(configuration);
+ if (result?.Status == OperationResult.Ok && result?.ValidationSuccessful == true)
+ {
+ var message = result.Text;
+ ViewUtils.Alert(this, "VIN Scanner result", message);
+ }
+ }
+
+ ///
+ /// Init the Confirmation Dialog.
+ ///
+ ///
+ private BarcodeConfirmationDialogConfiguration GetConfirmationDialog()
+ {
+ return new BarcodeConfirmationDialogConfiguration
+ {
+ Title = "Confirmation Dialog",
+ Message = "Your barcode is scanned.",
+ ConfirmButtonTitle = "Confirm",
+ RetryButtonTitle = "Retry",
+ TextFormat = BarcodeTextFormat.CodeAndType,
+ ResultWithConfirmationEnabled = true,
+ };
+ }
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/MauiProgram.cs b/MigrationForms/MigrationFormsExampleToMAUI/MauiProgram.cs
new file mode 100644
index 0000000..e01b651
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/MauiProgram.cs
@@ -0,0 +1,81 @@
+using Microsoft.Extensions.Logging;
+using ScanbotSDK.MAUI;
+using ScanbotSDK.MAUI.Constants;
+
+namespace MigrationFormsExampleToMAUI
+{
+ public static partial class MauiProgram
+ {
+ internal const string LICENSE_KEY = null;
+
+ public static MauiApp CreateMauiApp()
+ {
+ var builder = MauiApp.CreateBuilder();
+ builder
+ .UseMauiApp()
+ .ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
+ });
+
+ SBSDKInitializer.Initialize(LICENSE_KEY, new ScanbotSDK.MAUI.SBSDKConfiguration
+ {
+ EnableLogging = true,
+ StorageBaseDirectory = StorageBaseDirectoryForExampleApp(),
+ StorageImageFormat = CameraImageFormat.Jpg,
+ StorageImageQuality = 50,
+ DetectorType = DocumentDetectorType.MLBased,
+ // You can enable encryption by uncommenting the following lines:
+ //Encryption = new SBSDKEncryption
+ //{
+ // Password = "SomeSecretPa$$w0rdForFileEncryption",
+ // Mode = EncryptionMode.AES256
+ //}
+ // Note: all the images and files exported through the SDK will
+ // not be openable from external applications, since they will be
+ // encrypted.
+ });
+
+ return builder.Build();
+ }
+
+ private static string StorageBaseDirectoryForExampleApp()
+ {
+ /** !!Please note!!
+
+ * In this demo app we overwrite the "StorageBaseDirectory"
+ * of the Scanbot SDK by a custom public (!) storage directory.
+ * "GetExternalFilesDir" returns an external, public (!) storage directoy.
+ * All image files as well export files (PDF, TIFF, etc) created
+ * by the Scanbot SDK in this demo app will be stored in a sub-folder
+ * of this storage directory and will be accessible
+ * for every(!) app having external storage permissions!
+
+ * We use the "ExternalStorageDirectory" here only for demo purposes,
+ * to be able to share generated PDF and TIFF files.
+ * (also see the example code for PDF and TIFF creation).
+
+ * If you need a secure storage for all images
+ * and export files (which is strongly recommended):
+ - Use the default settings of the Scanbot SDK (don't overwrite
+ the "StorageBaseDirectory" config parameter above)
+ - Set a suitable custom internal (!) StorageBaseDirectory.
+
+ * For more detais about the Android file system see:
+ - https://developer.android.com/guide/topics/data/data-storage
+ - https://docs.microsoft.com/en-us/xamarin/android/platform/files/
+ */
+ // var directory = GetExternalFilesDir(null).AbsolutePath;
+ // var externalPublicPath = Path.Combine(directory, "my-custom-storage");
+ // Directory.CreateDirectory(externalPublicPath);
+ // return externalPublicPath;
+
+ var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
+ var folder = Path.Combine(documents, "maui-dev-app-storage");
+ Directory.CreateDirectory(folder);
+
+ return folder;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/MigrationFormsExampleToMAUI.csproj b/MigrationForms/MigrationFormsExampleToMAUI/MigrationFormsExampleToMAUI.csproj
new file mode 100644
index 0000000..2b6f4e0
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/MigrationFormsExampleToMAUI.csproj
@@ -0,0 +1,89 @@
+
+
+
+ net8.0-android;net8.0-ios
+ true
+ Exe
+ MigrationFormsExampleToMAUI
+ true
+ true
+ enable
+ enable
+
+
+ MigrationForms
+
+
+ io.scanbot.example.sdk.xamarin.forms
+
+
+ 1.0
+ 1
+
+ 13.0
+ 21.0
+
+
+
+
+ SdkOnly
+ false
+
+
+
+
+ SdkOnly
+ -all
+ false
+ ios-arm64
+
+
+ true
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Model/BarcodeTypes.cs b/MigrationForms/MigrationFormsExampleToMAUI/Model/BarcodeTypes.cs
new file mode 100644
index 0000000..57f7e84
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Model/BarcodeTypes.cs
@@ -0,0 +1,48 @@
+using ScanbotSDK.MAUI.Constants;
+
+namespace MigrationFormsExampleToMAUI.Model;
+
+public class BarcodeTypes
+{
+ public static BarcodeTypes Instance { get; private set; } = new BarcodeTypes();
+
+ public Dictionary List { get; private set; } = new Dictionary();
+
+ public List AcceptedTypes
+ {
+ get
+ {
+ var result = new List();
+ foreach (var item in List)
+ {
+ if (item.Value)
+ {
+ result.Add(item.Key);
+ }
+ }
+
+ return result;
+ }
+ }
+
+ public bool IsChecked(BarcodeFormat lastCheckedFormat)
+ {
+ return AcceptedTypes.Contains(lastCheckedFormat);
+ }
+
+ public List All =>
+ Enum.GetValues(typeof(BarcodeFormat)).Cast().ToList();
+
+ private BarcodeTypes()
+ {
+ foreach (var item in All)
+ {
+ List.Add(item, true);
+ }
+ }
+
+ public void Update(BarcodeFormat type, bool value)
+ {
+ List[type] = value;
+ }
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Model/PageStorage.cs b/MigrationForms/MigrationFormsExampleToMAUI/Model/PageStorage.cs
new file mode 100644
index 0000000..5d5b80d
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Model/PageStorage.cs
@@ -0,0 +1,132 @@
+using ScanbotSDK.MAUI.Services;
+using SQLite;
+
+namespace MigrationFormsExampleToMAUI.Model;
+
+ public class PageStorage
+ {
+ public static PageStorage Instance = new PageStorage();
+
+ public const string DatabaseFilename = "SBSDKPageStorage.db3";
+
+ public const SQLiteOpenFlags Flags = SQLiteOpenFlags.ReadWrite
+ | SQLiteOpenFlags.Create | SQLiteOpenFlags.SharedCache;
+
+ public static string DatabasePath
+ {
+ get
+ {
+ var basePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ return Path.Combine(basePath, DatabaseFilename);
+ }
+ }
+
+ static SQLiteAsyncConnection Database = new SQLiteAsyncConnection(DatabasePath, Flags);
+
+ private PageStorage()
+ {
+
+ }
+
+ public async Task InitializeAsync()
+ {
+ var result = await Database.CreateTablesAsync(CreateFlags.None, typeof(DBPage));//.ConfigureAwait(false);
+ Console.WriteLine("Storage initialize: " + result);
+ }
+
+ public async Task Save(IScannedPage page)
+ {
+ var dbPage = DBPage.From(page);
+ return await Database.InsertAsync(dbPage);
+ }
+
+ public async Task Update(IScannedPage page)
+ {
+ return await Database.UpdateAsync(DBPage.From(page));
+ }
+
+ public async Task> Load()
+ {
+ var pages = await Database.Table().ToListAsync();
+ return pages;
+ }
+
+ public async Task Delete(IScannedPage page)
+ {
+ return await Database.DeleteAsync(DBPage.From(page));
+ }
+
+ public async Task Clear()
+ {
+ var mapping = Database.TableMappings.First(tables => tables.TableName == "DBPage");
+ if (mapping != null)
+ {
+ return await Database.DeleteAllAsync(mapping);
+ }
+
+ return -1;
+ }
+ }
+
+ /*
+ * SQLite storage requires a non-abstract class with a constructor and primitive types
+ */
+ public class DBPage
+ {
+ [PrimaryKey]
+ public string Id { get; set; }
+
+ public int Filter { get; set; }
+ public int DetectionStatus { get; set; }
+
+ public double X1 { get; set; }
+ public double Y1 { get; set; }
+ public double X2 { get; set; }
+ public double Y2 { get; set; }
+ public double X3 { get; set; }
+ public double Y3 { get; set; }
+ public double X4 { get; set; }
+ public double Y4 { get; set; }
+
+ public static DBPage From(IScannedPage page)
+ {
+
+ var result = new DBPage
+ {
+ Id = page.Id,
+ Filter = (int)page.Filter,
+ DetectionStatus = (int)page.DetectionStatus
+ };
+
+ result.MapPolygon(page.Polygon);
+
+ return result;
+ }
+
+ public void MapPolygon(Point[] points)
+ {
+ if (points.Length < 4)
+ {
+ return;
+ }
+ X1 = points[0].X;
+ Y1 = points[0].Y;
+ X2 = points[1].X;
+ Y2 = points[1].Y;
+ X3 = points[2].X;
+ Y3 = points[2].Y;
+ X4 = points[3].X;
+ Y4 = points[3].Y;
+ }
+
+ public Point[] CreatePolygon()
+ {
+ var result = new List();
+ result.Add(new Point(X1, Y1));
+ result.Add(new Point(X2, Y2));
+ result.Add(new Point(X3, Y3));
+ result.Add(new Point(X4, Y4));
+ return result.ToArray();
+ }
+
+ }
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Model/Pages.cs b/MigrationForms/MigrationFormsExampleToMAUI/Model/Pages.cs
new file mode 100644
index 0000000..65edcc7
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Model/Pages.cs
@@ -0,0 +1,72 @@
+using ScanbotSDK.MAUI.Constants;
+using ScanbotSDK.MAUI.Services;
+
+namespace MigrationFormsExampleToMAUI.Model;
+
+public class Pages
+{
+ public static readonly Pages Instance = new Pages();
+
+ public List List { get; set; } = new List();
+
+ public IEnumerable DocumentSources
+ {
+ get => List.Select(p => p.Document).Where(image => image != null);
+ }
+
+ public IScannedPage SelectedPage { get; set; }
+
+
+ private Pages() { }
+
+ public async Task RemoveSelection()
+ {
+ var result = await PageStorage.Instance.Delete(SelectedPage);
+ List.Remove(SelectedPage);
+ SelectedPage = null;
+ return result;
+ }
+
+ public async Task UpdateFilterForSelection(ImageFilter filter)
+ {
+ await SelectedPage.SetFilterAsync(filter);
+ return await UpdateSelection();
+ }
+
+ public async Task UpdateSelection()
+ {
+ return await PageStorage.Instance.Update(SelectedPage);
+ }
+
+ public async Task Add(IScannedPage page, bool save = true)
+ {
+ List.Add(page);
+ if (save)
+ {
+ await PageStorage.Instance.Save(page);
+ }
+ return true;
+ }
+
+ public async Task LoadFromStorage()
+ {
+ var pages = await PageStorage.Instance.Load();
+ foreach (var page in pages)
+ {
+ var reconstructed = await ScanbotSDK.MAUI.ScanbotSDK.SDKService.ReconstructPage(
+ page.Id,
+ page.CreatePolygon(),
+ (ImageFilter)page.Filter,
+ (DocumentDetectionStatus)page.DetectionStatus
+ );
+ await Add(reconstructed, false);
+ }
+ return true;
+ }
+
+ public async Task Clear()
+ {
+ List.Clear();
+ return await PageStorage.Instance.Clear();
+ }
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Pages/BarcodeResultsPage.cs b/MigrationForms/MigrationFormsExampleToMAUI/Pages/BarcodeResultsPage.cs
new file mode 100644
index 0000000..b19f6ad
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Pages/BarcodeResultsPage.cs
@@ -0,0 +1,178 @@
+using ScanbotSDK.MAUI.Models;
+
+namespace MigrationFormsExampleToMAUI.Pages;
+
+public class BarcodeResultsPage: ContentPage
+{
+ const int ROWHEIGHT = 60;
+
+ public ListView List { get; set; }
+
+ public Image SnappedImage { get; set; }
+
+ public List Barcodes { get; set; }
+
+ public ActivityIndicator Loader { get; set; }
+
+ public BarcodeResultsPage(List barcodes)
+ {
+ BackgroundColor = Colors.White;
+ SetTitle();
+
+ Barcodes = barcodes;
+ InitializeList();
+ Content = List;
+ }
+
+ public BarcodeResultsPage(ImageSource source, List barcodes = null)
+ {
+ SetTitle();
+ BackgroundColor = Colors.White;
+
+ var Container = new StackLayout();
+ Container.Orientation = StackOrientation.Vertical;
+ Container.BackgroundColor = Colors.White;
+
+ Content = Container;
+
+ SnappedImage = new Image
+ {
+ HorizontalOptions = LayoutOptions.FillAndExpand,
+ BackgroundColor = Colors.LightGray,
+ Aspect = Aspect.AspectFit
+ };
+ SnappedImage.SizeChanged += delegate
+ {
+ if (source == null || source.IsEmpty)
+ {
+ // If there is no snapped image, do not show empty container
+ SnappedImage.HeightRequest = 0;
+ return;
+ }
+ // Don't allow images larger than a third of the screen
+ SnappedImage.HeightRequest = Content.Height / 3;
+ };
+ Loader = new ActivityIndicator
+ {
+ VerticalOptions = LayoutOptions.FillAndExpand,
+ HorizontalOptions = LayoutOptions.CenterAndExpand,
+ IsEnabled = true,
+ IsRunning = true,
+ };
+
+ Container.Children.Add(SnappedImage);
+ Container.Children.Add(Loader);
+
+ SnappedImage.Source = source;
+
+ if (barcodes == null)
+ {
+ //var copy = Utils.Copy(source);
+ DetectBarcodes(source, delegate
+ {
+ InitializeList();
+ Container.Children.Remove(Loader);
+ Container.Children.Add(List);
+ });
+ }
+ else
+ {
+ Barcodes = barcodes;
+ InitializeList();
+ Container.Children.Remove(Loader);
+ Container.Children.Add(List);
+ }
+
+ }
+
+ async void DetectBarcodes(ImageSource source, Action callback = null)
+ {
+ Barcodes = await ScanbotSDK.MAUI.ScanbotSDK.DetectionService.DetectBarcodesFrom(source);
+ callback();
+ }
+
+ void SetTitle()
+ {
+ Title = "DETECTED BARCODES";
+ }
+
+ void InitializeList()
+ {
+ List = new ListView();
+ List.BackgroundColor = Colors.White;
+ List.ItemTemplate = new DataTemplate(typeof(BarcodeCell));
+ List.HasUnevenRows = true;
+ List.ItemsSource = Barcodes;
+ }
+
+ public class BarcodeCell : ViewCell
+ {
+ public Barcode Source { get; private set; }
+
+ public Image ImageView { get; set; }
+
+ public Label TypeLabel { get; set; }
+
+ public Label ContentLabel { get; set; }
+
+ public BarcodeCell()
+ {
+ ImageView = new Image
+ {
+ Aspect = Aspect.AspectFit,
+ HeightRequest = ROWHEIGHT,
+ WidthRequest = ROWHEIGHT,
+ BackgroundColor = Color.FromRgb(250, 250, 250)
+ };
+
+ TypeLabel = new Label
+ {
+ HorizontalOptions = LayoutOptions.StartAndExpand,
+ VerticalOptions = LayoutOptions.FillAndExpand,
+ VerticalTextAlignment = TextAlignment.Center,
+ Margin = new Thickness(10, 0, 0, 0),
+ TextColor = Colors.Black
+ };
+
+ ContentLabel = new Label
+ {
+ HorizontalOptions = LayoutOptions.StartAndExpand,
+ VerticalOptions = LayoutOptions.FillAndExpand,
+ VerticalTextAlignment = TextAlignment.Center,
+ Margin = new Thickness(10, 0, 0, 0),
+ TextColor = Colors.DarkGray
+ };
+
+ StackLayout labelContainer = new StackLayout
+ {
+ Orientation = StackOrientation.Vertical,
+ HorizontalOptions = LayoutOptions.StartAndExpand,
+ VerticalOptions = LayoutOptions.FillAndExpand,
+ Children = { TypeLabel, ContentLabel }
+ };
+
+ View = new StackLayout()
+ {
+ BackgroundColor = Colors.White,
+ HorizontalOptions = LayoutOptions.FillAndExpand,
+ VerticalOptions = LayoutOptions.FillAndExpand,
+ Orientation = StackOrientation.Horizontal,
+ Padding = new Thickness(0, 0, 10, 0),
+ Children = { ImageView, labelContainer }
+ };
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ Source = (Barcode)BindingContext;
+
+ ImageView.Source = Source.Image;
+
+ TypeLabel.Text = Source.Format.ToString();
+ ContentLabel.Text = Source.Text;
+
+ base.OnBindingContextChanged();
+ }
+ }
+}
+
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Pages/BarcodeSelectorPage.cs b/MigrationForms/MigrationFormsExampleToMAUI/Pages/BarcodeSelectorPage.cs
new file mode 100644
index 0000000..d95e858
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Pages/BarcodeSelectorPage.cs
@@ -0,0 +1,19 @@
+using MigrationFormsExampleToMAUI.Model;
+using MigrationFormsExampleToMAUI.Subviews.Cells;
+
+namespace MigrationFormsExampleToMAUI.Pages;
+
+public class BarcodeSelectorPage : ContentPage
+{
+ public BarcodeSelectorPage()
+ {
+ Title = "ACCEPTED BARCODES";
+ var list = new ListView();
+ list.ItemTemplate = new DataTemplate(typeof(BarcodeFormatCell));
+ list.ItemsSource = BarcodeTypes.Instance.List;
+ list.RowHeight = 50;
+ list.BackgroundColor = Colors.White;
+
+ Content = list;
+ }
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Pages/ImageDetailPage.cs b/MigrationForms/MigrationFormsExampleToMAUI/Pages/ImageDetailPage.cs
new file mode 100644
index 0000000..a532644
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Pages/ImageDetailPage.cs
@@ -0,0 +1,86 @@
+using MigrationFormsExampleToMAUI.Subviews.ActionBar;
+using MigrationFormsExampleToMAUI.Utils;
+using ScanbotSDK.MAUI.Constants;
+
+namespace MigrationFormsExampleToMAUI.Pages;
+
+ public class ImageDetailPage : ContentPage
+ {
+ public Image Image { get; private set; }
+
+ public BottomActionBar BottomBar { get; private set; }
+
+ public ImageFilter CurrentFilter { get; set; }
+
+ public ImageDetailPage()
+ {
+ Image = new Image
+ {
+ HorizontalOptions = LayoutOptions.FillAndExpand,
+ BackgroundColor = Colors.LightGray,
+ Aspect = Aspect.AspectFit,
+ };
+ Image.SizeChanged += delegate
+ {
+ // Don't allow images larger than 2/3 of the screen
+ Image.HeightRequest = Content.Height / 3 * 2;
+ };
+
+ BottomBar = new BottomActionBar(true);
+
+ Content = new StackLayout
+ {
+ Children = { Image, BottomBar }
+ };
+
+ BottomBar.AddClickEvent(BottomBar.CropButton, OnCropButtonClick);
+ BottomBar.AddClickEvent(BottomBar.FilterButton, OnFilterButtonClick);
+ BottomBar.AddClickEvent(BottomBar.DeleteButton, OnDeleteButtonClick);
+
+ LoadImage();
+ }
+
+ async void LoadImage()
+ {
+ // If encryption is enabled, load the decrypted document.
+ // Else accessible via Document or DocumentPreview
+ Image.Source = await Model.Pages.Instance.SelectedPage.DecryptedDocument();
+ //Image.Source = Pages.Instance.SelectedPage.Document;
+ }
+
+ async void OnCropButtonClick(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+ if (!SDKUtils.CheckPage(this, Model.Pages.Instance.SelectedPage)) { return; }
+
+ await ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchCroppingScreenAsync(Model.Pages.Instance.SelectedPage);
+ await Model.Pages.Instance.UpdateSelection();
+
+ Image.Source = null;
+ LoadImage();
+ }
+
+ async void OnFilterButtonClick(object sender, EventArgs e)
+ {
+ if (!SDKUtils.CheckLicense(this)) { return; }
+ if (!SDKUtils.CheckPage(this, Model.Pages.Instance.SelectedPage)) { return; }
+
+ var buttons = Enum.GetNames(typeof(ImageFilter));
+ var action = await DisplayActionSheet("Filter", "Cancel", null, buttons);
+
+ ImageFilter filter;
+ Enum.TryParse(action, out filter);
+ CurrentFilter = filter;
+
+ await Model.Pages.Instance.UpdateFilterForSelection(filter);
+ LoadImage();
+ }
+
+ async void OnDeleteButtonClick(object sender, EventArgs e)
+ {
+ await Model.Pages.Instance.RemoveSelection();
+ Image.Source = null;
+ await Navigation.PopAsync();
+ }
+
+ }
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Pages/ImageResultsPage.cs b/MigrationForms/MigrationFormsExampleToMAUI/Pages/ImageResultsPage.cs
new file mode 100644
index 0000000..6106938
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Pages/ImageResultsPage.cs
@@ -0,0 +1,164 @@
+using MigrationFormsExampleToMAUI.Subviews.ActionBar;
+using MigrationFormsExampleToMAUI.Subviews.Cells;
+using MigrationFormsExampleToMAUI.Utils;
+using ScanbotSDK.MAUI;
+using ScanbotSDK.MAUI.Constants;
+using ScanbotSDK.MAUI.Models;
+using ScanbotSDK.MAUI.Services;
+
+namespace MigrationFormsExampleToMAUI.Pages;
+
+public class ImageResultsPage: ContentPage
+{
+ public StackLayout Stack { get; private set; }
+
+ public ListView List { get; private set; }
+
+ public BottomActionBar BottomBar { get; private set; }
+
+ public ActivityIndicator Loader { get; set; }
+
+ public ImageResultsPage()
+ {
+ Title = "Image Results";
+
+ List = new ListView
+ {
+ ItemTemplate = new DataTemplate(typeof(ImageResultCell)),
+ RowHeight = 120,
+ BackgroundColor = Colors.White
+ };
+
+ BottomBar = new BottomActionBar(false);
+ BottomBar.AddClickEvent(BottomBar.AddButton, OnAddButtonClick);
+ BottomBar.AddClickEvent(BottomBar.SaveButton, OnSaveButtonClick);
+ BottomBar.AddClickEvent(BottomBar.DeleteAllButton, OnDeleteButtonClick);
+
+ Loader = new ActivityIndicator
+ {
+ VerticalOptions = LayoutOptions.CenterAndExpand,
+ HorizontalOptions = LayoutOptions.CenterAndExpand,
+ Color = App.ScanbotRed,
+ IsRunning = true,
+ IsEnabled = true
+ };
+
+ Stack = new StackLayout
+ {
+ Orientation = StackOrientation.Vertical,
+ Spacing = 0,
+ Children = { List, BottomBar }
+ };
+
+ Content = new ScrollView
+ {
+ Content = Stack
+ };
+
+ List.ItemTapped += OnItemClick;
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+ ReloadData();
+ }
+
+ private void OnItemClick(object sender, ItemTappedEventArgs e)
+ {
+ Model.Pages.Instance.SelectedPage = (IScannedPage)e.Item;
+ Navigation.PushAsync(new ImageDetailPage());
+ }
+
+ async void OnAddButtonClick(object sender, EventArgs e)
+ {
+ var configuration = new DocumentScannerConfiguration
+ {
+ CameraPreviewMode = CameraPreviewMode.FitIn,
+ IgnoreBadAspectRatio = true,
+ MultiPageEnabled = true,
+ PolygonColor = Colors.Red,
+ PolygonColorOK = Colors.Green,
+ BottomBarBackgroundColor = Colors.Blue,
+ PageCounterButtonTitle = "%d Page(s)",
+
+ };
+ var result = await ScanbotSDK.MAUI.ScanbotSDK.ReadyToUseUIService.LaunchDocumentScannerAsync(configuration);
+ if (result.Status == OperationResult.Ok)
+ {
+ foreach (var page in result.Pages)
+ {
+ Model.Pages.Instance.List.Add(page);
+ }
+ }
+ }
+
+ async void OnSaveButtonClick(object sender, EventArgs e)
+ {
+ var parameters = new[] {"PDF", "PDF with OCR", "TIFF (1-bit, B&W)" };
+ string action = await DisplayActionSheet("Save Image as", "Cancel", null, parameters);
+
+ if (action == null || action.Equals("Cancel"))
+ {
+ return;
+ }
+
+ if (!SDKUtils.CheckLicense(this)) { return; }
+ if (!SDKUtils.CheckDocuments(this, Model.Pages.Instance.DocumentSources)) { return; }
+
+ if (action.Equals(parameters[0]))
+ {
+ var fileUri = await ScanbotSDK.MAUI.ScanbotSDK.SDKService
+ .CreatePdfAsync(Model.Pages.Instance.DocumentSources.OfType(), PDFPageSize.A4);
+ ViewUtils.Alert(this, "Success: ", "Wrote documents to: " + fileUri.AbsolutePath);
+ }
+ else if (action.Equals(parameters[1]))
+ {
+ string path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
+ string pdfFilePath = Path.Combine(path, Guid.NewGuid() + ".pdf");
+
+ var ocrConfig = ScanbotSDK.MAUI.ScanbotSDK.SDKService.DefaultOcrConfig;
+ // Uncomment below code to use the old OCR approach. Use [OCRMode.Legacy] and set the required [InstalledLanguages] property.
+ //var languages = new List { "en", "de" };
+ //var ocrConfig = new OcrConfigs
+ //{
+ // InstalledLanguages = languages,
+ // OcrMode = OCRMode.Legacy,
+ // LanguageDataPath = ocrConfig.LanguageDataPath
+ //};
+
+ var result = await ScanbotSDK.MAUI.ScanbotSDK.SDKService.PerformOcrAsync(
+ Model.Pages.Instance.DocumentSources.OfType(),
+ ocrConfig,
+ pdfFilePath);
+ // Or do something else with the result: result.Pages...
+ ViewUtils.Alert(this, "PDF with OCR layer stored: ", pdfFilePath);
+ }
+ else if (action.Equals(parameters[2]))
+ {
+ var fileUri = await ScanbotSDK.MAUI.ScanbotSDK.SDKService.WriteTiffAsync(
+ Model.Pages.Instance.DocumentSources.OfType(),
+ new TiffOptions { OneBitEncoded = true, Dpi = 300, Compression = TiffCompressionOptions.CompressionCcittT6 }
+ );
+ ViewUtils.Alert(this, "Success: ", "Wrote documents to: " + fileUri.AbsolutePath);
+ }
+ }
+
+ private async void OnDeleteButtonClick(object sender, EventArgs e)
+ {
+ var message = "Do you really want to delete all image data?";
+ var result = await DisplayAlert("Attention!", message, "Yes", "No");
+ if (result)
+ {
+ await Model.Pages.Instance.Clear();
+ await ScanbotSDK.MAUI.ScanbotSDK.SDKService.CleanUp();
+ ReloadData();
+ }
+ }
+
+ void ReloadData()
+ {
+ List.ItemsSource = null;
+ List.ItemsSource = Model.Pages.Instance.List;
+ }
+}
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/AndroidManifest.xml b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 0000000..bd010b8
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Assets/AboutAssets.txt b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Assets/AboutAssets.txt
new file mode 100644
index 0000000..072563f
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Assets/AboutAssets.txt
@@ -0,0 +1,19 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories) and given a Build Action of "AndroidAsset".
+
+These files will be deployed with your package and will be accessible using Android's
+AssetManager, like this:
+
+public class ReadAsset : Activity
+{
+ protected override void OnCreate (Bundle bundle)
+ {
+ base.OnCreate (bundle);
+
+ InputStream input = Assets.Open ("my_asset.txt");
+ }
+}
+
+Additionally, some Android functions will automatically load asset files:
+
+Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Assets/SBSDKLanguageData/deu.traineddata b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Assets/SBSDKLanguageData/deu.traineddata
new file mode 100644
index 0000000..97ed7b2
Binary files /dev/null and b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Assets/SBSDKLanguageData/deu.traineddata differ
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Assets/SBSDKLanguageData/eng.traineddata b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Assets/SBSDKLanguageData/eng.traineddata
new file mode 100644
index 0000000..bbef467
Binary files /dev/null and b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Assets/SBSDKLanguageData/eng.traineddata differ
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Assets/SBSDKLanguageData/osd.traineddata b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Assets/SBSDKLanguageData/osd.traineddata
new file mode 100644
index 0000000..527457c
Binary files /dev/null and b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Assets/SBSDKLanguageData/osd.traineddata differ
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/DependencyServices/MultiImagePicker.cs b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/DependencyServices/MultiImagePicker.cs
new file mode 100644
index 0000000..fa66034
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/DependencyServices/MultiImagePicker.cs
@@ -0,0 +1,65 @@
+using NativeMedia;
+
+namespace MigrationFormsExampleToMAUI.DependencyServices;
+
+ ///
+ /// Android Multiple Image picker class implementation of Dependency Service IMultiImagePicker interface
+ ///
+ public partial class MultiImagePicker
+ {
+ ///
+ /// Picks multiple photos from the Photos Application and invokes the completion handler after selection
+ ///
+ ///
+ public async partial void PickPhotosAsync(Action> completionHandler)
+ {
+ var result = await PickMultipleMediaFiles();
+ completionHandler?.Invoke(result);
+ }
+
+ ///
+ /// Picks multiple images from the gallery
+ ///
+ ///
+ private async Task> PickMultipleMediaFiles()
+ {
+ var imagesSources = new List();
+ var cts = new CancellationTokenSource();
+ IMediaFile[] files = null;
+ try
+ {
+ var request = new MediaPickRequest(10, MediaFileType.Image)
+ {
+ Title = "Select"
+ };
+
+ cts.CancelAfter(TimeSpan.FromMinutes(5));
+
+ var results = await MediaGallery.PickAsync(request, cts.Token);
+ files = results?.Files?.ToArray();
+
+ if (files == null)
+ return null;
+
+ foreach (var file in files)
+ {
+ var stream = await file.OpenReadAsync();
+ imagesSources.Add(ImageSource.FromStream(() => stream));
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ // handling a cancellation request
+ }
+ catch (Exception)
+ {
+ // handling other exceptions
+ }
+ finally
+ {
+ cts.Dispose();
+ }
+
+ return imagesSources;
+ }
+ }
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/MainActivity.cs b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/MainActivity.cs
new file mode 100644
index 0000000..bf01b15
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/MainActivity.cs
@@ -0,0 +1,36 @@
+using Android;
+using Android.App;
+using Android.Content;
+using Android.Content.PM;
+using Android.OS;
+using AndroidX.Core.App;
+using MigrationFormsExampleToMAUI.DependencyServices;
+
+namespace MigrationFormsExampleToMAUI;
+
+[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true,
+ ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode |
+ ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
+public class MainActivity : MauiAppCompatActivity
+{
+ protected override void OnCreate(Bundle savedInstanceState)
+ {
+ base.OnCreate(savedInstanceState);
+ // Media Gallery init implementation
+ NativeMedia.Platform.Init(this, savedInstanceState);
+
+ ActivityCompat.RequestPermissions(this, new string[] {
+ Manifest.Permission.Camera,
+ Manifest.Permission.ReadExternalStorage,
+ Manifest.Permission.WriteExternalStorage
+ }, 0);
+ }
+
+ protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
+ {
+ if (NativeMedia.Platform.CheckCanProcessResult(requestCode, resultCode, data))
+ NativeMedia.Platform.OnActivityResult(requestCode, resultCode, data);
+
+ base.OnActivityResult(requestCode, resultCode, data);
+ }
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/MainApplication.cs b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/MainApplication.cs
new file mode 100644
index 0000000..0a6f6bc
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/MainApplication.cs
@@ -0,0 +1,15 @@
+using Android.App;
+using Android.Runtime;
+
+namespace MigrationFormsExampleToMAUI;
+
+[Application]
+public class MainApplication : MauiApplication
+{
+ public MainApplication(IntPtr handle, JniHandleOwnership ownership)
+ : base(handle, ownership)
+ {
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Resources/values/colors.xml b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 0000000..c04d749
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Platforms/iOS/AppDelegate.cs b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/iOS/AppDelegate.cs
new file mode 100644
index 0000000..c616055
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/iOS/AppDelegate.cs
@@ -0,0 +1,10 @@
+using Foundation;
+using MigrationFormsExampleToMAUI.DependencyServices;
+
+namespace MigrationFormsExampleToMAUI;
+
+[Register("AppDelegate")]
+public class AppDelegate : MauiUIApplicationDelegate
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Platforms/iOS/DependencyServices/MultiImagePicker.cs b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/iOS/DependencyServices/MultiImagePicker.cs
new file mode 100644
index 0000000..fef3ca4
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/iOS/DependencyServices/MultiImagePicker.cs
@@ -0,0 +1,73 @@
+using GMImagePicker;
+using Photos;
+using UIKit;
+
+namespace MigrationFormsExampleToMAUI.DependencyServices;
+
+ ///
+ /// iOS Multiple Image picker class implementation of Dependency Service IMultiImagePicker interface
+ ///
+ public partial class MultiImagePicker
+ {
+ ///
+ /// Picks multiple photos from the Photos Application and invokes the completion handler after selection
+ ///
+ ///
+ public partial void PickPhotosAsync(Action> completionHandler)
+ {
+ var picker = new GMImagePickerController();
+ picker.AllowsMultipleSelection = true;
+ picker.DisplayAlbumsNumberOfAssets = true;
+ picker.MediaTypes = new[] { PHAssetMediaType.Image };
+ picker.GridSortOrder = SortOrder.Descending;
+ picker.ModalPresentationStyle = UIKit.UIModalPresentationStyle.FullScreen;
+
+ // Cancelled
+ picker.Canceled += (sender, e) =>
+ {
+ // cancellation handler
+ };
+
+ // Picked images
+ picker.FinishedPickingAssets += async (sender, args) =>
+ {
+ var list = new List();
+ ImageSource imageSource = ImageSource.FromFile(string.Empty); // empty source
+ string filePath = string.Empty;
+ foreach (PHAsset asset in args?.Assets)
+ {
+ filePath = await GetAbsoluteUrl(asset);
+ if (!string.IsNullOrEmpty(filePath))
+ {
+ imageSource = ImageSource.FromFile(filePath);
+ }
+ list.Add(imageSource);
+ }
+ completionHandler?.Invoke(list);
+ };
+
+ var viewController = (UIApplication.SharedApplication.Delegate as AppDelegate).Window?.RootViewController;
+ if (viewController != null)
+ {
+ viewController.PresentViewController(picker, true, null);
+ }
+ }
+
+ ///
+ /// Get the Absolute Url from PHAsset
+ ///
+ ///
+ ///
+ private Task GetAbsoluteUrl(PHAsset asset)
+ {
+ var taskSource = new TaskCompletionSource();
+ var optionEditing = new PHContentEditingInputRequestOptions();
+ PHContentEditingHandler handler = (contentEditingInput, info) =>
+ {
+ taskSource.TrySetResult(contentEditingInput?.FullSizeImageUrl?.Path);
+ };
+
+ asset.RequestContentEditingInput(optionEditing, completionHandler: handler);
+ return taskSource.Task;
+ }
+ }
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Platforms/iOS/Info.plist b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/iOS/Info.plist
new file mode 100644
index 0000000..96c0cc2
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/iOS/Info.plist
@@ -0,0 +1,52 @@
+
+
+
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ MinimumOSVersion
+ 13.0
+ CFBundleDisplayName
+ Scanbot.SDK.Example.Forms
+ CFBundleVersion
+ 1.0.0
+ UILaunchStoryboardName
+ LaunchScreen
+ CFBundleName
+ Scanbot.SDK.Example.Forms
+ XSAppIconAssets
+ Resources/AppIcon.appiconset
+ UIStatusBarStyle
+ UIStatusBarStyleLightContent
+ UIViewControllerBasedStatusBarAppearance
+
+ CFBundleIdentifier
+ io.scanbot.example.sdk.xamarin.forms
+ CFBundleShortVersionString
+ 1
+ NSCameraUsageDescription
+ Scanbot SDK requires camera access in order to scan documents
+ UIFileSharingEnabled
+
+ NSPhotoLibraryAddUsageDescription
+ Scanbot SDK requires photos app access in order to scan documents
+ NSPhotoLibraryUsageDescription
+ Scanbot SDK requires photos app access in order to scan documents
+
+
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Platforms/iOS/Program.cs b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/iOS/Program.cs
new file mode 100644
index 0000000..60b1f1c
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Platforms/iOS/Program.cs
@@ -0,0 +1,15 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace MigrationFormsExampleToMAUI;
+
+public class Program
+{
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Properties/launchSettings.json b/MigrationForms/MigrationFormsExampleToMAUI/Properties/launchSettings.json
new file mode 100644
index 0000000..edf8aad
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Windows Machine": {
+ "commandName": "MsixPackage",
+ "nativeDebugging": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Resources/AppIcon/appicon.svg b/MigrationForms/MigrationFormsExampleToMAUI/Resources/AppIcon/appicon.svg
new file mode 100644
index 0000000..9d63b65
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Resources/AppIcon/appicon.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Resources/AppIcon/appiconfg.svg b/MigrationForms/MigrationFormsExampleToMAUI/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 0000000..21dfb25
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Resources/Fonts/OpenSans-Regular.ttf b/MigrationForms/MigrationFormsExampleToMAUI/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 0000000..2d1edf0
Binary files /dev/null and b/MigrationForms/MigrationFormsExampleToMAUI/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Resources/Fonts/OpenSans-Semibold.ttf b/MigrationForms/MigrationFormsExampleToMAUI/Resources/Fonts/OpenSans-Semibold.ttf
new file mode 100644
index 0000000..fe13d06
Binary files /dev/null and b/MigrationForms/MigrationFormsExampleToMAUI/Resources/Fonts/OpenSans-Semibold.ttf differ
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Resources/Images/dotnet_bot.png b/MigrationForms/MigrationFormsExampleToMAUI/Resources/Images/dotnet_bot.png
new file mode 100644
index 0000000..f93ce02
Binary files /dev/null and b/MigrationForms/MigrationFormsExampleToMAUI/Resources/Images/dotnet_bot.png differ
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Resources/Raw/AboutAssets.txt b/MigrationForms/MigrationFormsExampleToMAUI/Resources/Raw/AboutAssets.txt
new file mode 100644
index 0000000..15d6244
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,15 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories). Deployment of the asset to your application
+is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
+
+
+
+These files will be deployed with you package and will be accessible using Essentials:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Resources/ScanbotSDKOCRData.bundle/Info.plist b/MigrationForms/MigrationFormsExampleToMAUI/Resources/ScanbotSDKOCRData.bundle/Info.plist
new file mode 100644
index 0000000..a9af554
Binary files /dev/null and b/MigrationForms/MigrationFormsExampleToMAUI/Resources/ScanbotSDKOCRData.bundle/Info.plist differ
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Resources/ScanbotSDKOCRData.bundle/deu.traineddata b/MigrationForms/MigrationFormsExampleToMAUI/Resources/ScanbotSDKOCRData.bundle/deu.traineddata
new file mode 100755
index 0000000..97ed7b2
Binary files /dev/null and b/MigrationForms/MigrationFormsExampleToMAUI/Resources/ScanbotSDKOCRData.bundle/deu.traineddata differ
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Resources/ScanbotSDKOCRData.bundle/eng.traineddata b/MigrationForms/MigrationFormsExampleToMAUI/Resources/ScanbotSDKOCRData.bundle/eng.traineddata
new file mode 100755
index 0000000..bbef467
Binary files /dev/null and b/MigrationForms/MigrationFormsExampleToMAUI/Resources/ScanbotSDKOCRData.bundle/eng.traineddata differ
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Resources/ScanbotSDKOCRData.bundle/osd.traineddata b/MigrationForms/MigrationFormsExampleToMAUI/Resources/ScanbotSDKOCRData.bundle/osd.traineddata
new file mode 100755
index 0000000..527457c
Binary files /dev/null and b/MigrationForms/MigrationFormsExampleToMAUI/Resources/ScanbotSDKOCRData.bundle/osd.traineddata differ
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Resources/Splash/splash.svg b/MigrationForms/MigrationFormsExampleToMAUI/Resources/Splash/splash.svg
new file mode 100644
index 0000000..21dfb25
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Resources/Splash/splash.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Resources/Styles/Colors.xaml b/MigrationForms/MigrationFormsExampleToMAUI/Resources/Styles/Colors.xaml
new file mode 100644
index 0000000..30307a5
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Resources/Styles/Colors.xaml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+ #512BD4
+ #ac99ea
+ #242424
+ #DFD8F7
+ #9880e5
+ #2B0B98
+
+ White
+ Black
+ #D600AA
+ #190649
+ #1f1f1f
+
+ #E1E1E1
+ #C8C8C8
+ #ACACAC
+ #919191
+ #6E6E6E
+ #404040
+ #212121
+ #141414
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Resources/Styles/Styles.xaml b/MigrationForms/MigrationFormsExampleToMAUI/Resources/Styles/Styles.xaml
new file mode 100644
index 0000000..e0d36bb
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Resources/Styles/Styles.xaml
@@ -0,0 +1,426 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Subviews/ActionBar/BottomActionBar.cs b/MigrationForms/MigrationFormsExampleToMAUI/Subviews/ActionBar/BottomActionBar.cs
new file mode 100644
index 0000000..07d59e2
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Subviews/ActionBar/BottomActionBar.cs
@@ -0,0 +1,65 @@
+namespace MigrationFormsExampleToMAUI.Subviews.ActionBar
+{
+ public class BottomActionBar : StackLayout
+ {
+ public const int HEIGHT = 50;
+
+ // Pseudo-universal bottom action bar for multiple pages:
+ // These are initialized in Image Results Page
+ public BottomActionButton AddButton { get; private set; }
+ public BottomActionButton SaveButton { get; private set; }
+ public BottomActionButton DeleteAllButton { get; private set; }
+
+ // Whereas these are initialized in Image Details Page
+ public BottomActionButton CropButton { get; private set; }
+ public BottomActionButton FilterButton { get; private set; }
+ public BottomActionButton DeleteButton { get; private set; }
+
+ public BottomActionBar(bool isDetailPage)
+ {
+ BackgroundColor = App.ScanbotRed;
+ Orientation = StackOrientation.Horizontal;
+ HeightRequest = HEIGHT;
+ HorizontalOptions = LayoutOptions.FillAndExpand;
+ VerticalOptions = LayoutOptions.EndAndExpand;
+
+ if (isDetailPage)
+ {
+ CropButton = new BottomActionButton("crop.png", "CROP");
+ CreateButton(CropButton);
+ FilterButton = new BottomActionButton("filter.png", "FILTER");
+ CreateButton(FilterButton);
+ DeleteButton = new BottomActionButton("delete.png", "DELETE");
+ CreateButton(DeleteButton, true);
+ }
+ else
+ {
+ AddButton = new BottomActionButton("add.png", "ADD");
+ CreateButton(AddButton);
+ SaveButton = new BottomActionButton("save.png", "SAVE");
+ CreateButton(SaveButton);
+ DeleteAllButton = new BottomActionButton("delete.png", "DELETE ALL");
+ CreateButton(DeleteAllButton, true);
+ }
+ }
+
+ void CreateButton(BottomActionButton button, bool alignRight = false)
+ {
+ button.HeightRequest = HEIGHT;
+ if (alignRight)
+ {
+ button.HorizontalOptions = LayoutOptions.EndAndExpand;
+ }
+
+ Children.Add(button);
+ }
+
+ public void AddClickEvent(BottomActionButton button, EventHandler action)
+ {
+ var recognizer = new TapGestureRecognizer();
+ recognizer.Tapped += action;
+
+ button.GestureRecognizers.Add(recognizer);
+ }
+ }
+}
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Subviews/ActionBar/BottomActionButton.cs b/MigrationForms/MigrationFormsExampleToMAUI/Subviews/ActionBar/BottomActionButton.cs
new file mode 100644
index 0000000..aade8ea
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Subviews/ActionBar/BottomActionButton.cs
@@ -0,0 +1,30 @@
+namespace MigrationFormsExampleToMAUI.Subviews.ActionBar;
+
+public class BottomActionButton: StackLayout
+{
+ public Image Image { get; private set; }
+
+ public Label Label { get; private set; }
+
+ public BottomActionButton(string resource, string text)
+ {
+ Orientation = StackOrientation.Horizontal;
+
+ Image = new Image();
+ Image.Source = resource;
+ Image.WidthRequest = 26;
+ Image.HeightRequest = 26;
+ Image.Margin = new Thickness(3, 12, 3, 12);
+ Image.VerticalOptions = LayoutOptions.Center;
+ Children.Add(Image);
+
+ Label = new Label();
+ Label.Text = text;
+ Label.TextColor = Colors.White;
+ Label.VerticalOptions = LayoutOptions.Center;
+ Label.Margin = new Thickness(0, 0, 5, 0);
+ Label.FontSize = 14;
+ Children.Add(Label);
+ }
+}
+
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Subviews/Cells/BarcodeFormatCell.cs b/MigrationForms/MigrationFormsExampleToMAUI/Subviews/Cells/BarcodeFormatCell.cs
new file mode 100644
index 0000000..011ee5e
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Subviews/Cells/BarcodeFormatCell.cs
@@ -0,0 +1,52 @@
+using MigrationFormsExampleToMAUI.Model;
+using BarcodeFormat = ScanbotSDK.MAUI.Constants.BarcodeFormat;
+
+namespace MigrationFormsExampleToMAUI.Subviews.Cells;
+
+public class BarcodeFormatCell : ViewCell
+{
+ public KeyValuePair Source { get; private set; }
+
+ public Label Label { get; set; }
+
+ public Switch Switch { get; set; }
+
+ public BarcodeFormatCell()
+ {
+ Label = new Label()
+ {
+ VerticalTextAlignment = TextAlignment.Center,
+ Margin = new Thickness(10, 0, 0, 0),
+ TextColor = Colors.Black
+ };
+
+ Switch = new Switch
+ {
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.EndAndExpand
+ };
+
+ View = new StackLayout()
+ {
+ HorizontalOptions = LayoutOptions.FillAndExpand,
+ VerticalOptions = LayoutOptions.FillAndExpand,
+ Orientation = StackOrientation.Horizontal,
+ Margin = new Thickness(0, 0, 10, 0),
+ Children = { Label, Switch }
+ };
+
+ Switch.Toggled += delegate
+ {
+ BarcodeTypes.Instance.Update(Source.Key, Switch.IsToggled);
+ };
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ Source = (KeyValuePair)BindingContext;
+ Label.Text = Source.Key.ToString();
+ Switch.IsToggled = Source.Value;
+
+ base.OnBindingContextChanged();
+ }
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Subviews/Cells/ImageResultCell.cs b/MigrationForms/MigrationFormsExampleToMAUI/Subviews/Cells/ImageResultCell.cs
new file mode 100644
index 0000000..e6fc993
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Subviews/Cells/ImageResultCell.cs
@@ -0,0 +1,37 @@
+using ScanbotSDK.MAUI.Services;
+
+namespace MigrationFormsExampleToMAUI.Subviews.Cells;
+
+public class ImageResultCell : ViewCell
+{
+ public IScannedPage Source { get; private set; }
+
+ Image Document { get; set; }
+
+ public ImageResultCell()
+ {
+ Document = new Image();
+ Document.Margin = new Thickness(10, 10, 10, 10);
+
+ View = new StackLayout
+ {
+ Orientation = StackOrientation.Horizontal,
+ Children = { Document }
+ };
+ }
+
+ protected override async void OnBindingContextChanged()
+ {
+ if (BindingContext == null)
+ {
+ return;
+ }
+ Source = (IScannedPage)BindingContext;
+ // If encryption is enabled, load the decrypted document.
+ // Else accessible via page.Document
+ Document.Source = await Source.DecryptedDocumentPreview();
+ //Document.Source = Source.DocumentPreview;
+ base.OnBindingContextChanged();
+ }
+
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Utils/SDKUtils.cs b/MigrationForms/MigrationFormsExampleToMAUI/Utils/SDKUtils.cs
new file mode 100644
index 0000000..b9ddaa3
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Utils/SDKUtils.cs
@@ -0,0 +1,93 @@
+using System.Text;
+using ScanbotSDK.MAUI.Models;
+using ScanbotSDK.MAUI.Services;
+
+namespace MigrationFormsExampleToMAUI.Utils;
+
+public class SDKUtils
+ {
+ public static bool CheckLicense(ContentPage context)
+ {
+ if (!ScanbotSDK.MAUI.ScanbotSDK.IsLicenseValid)
+ {
+ ViewUtils.Alert(context, "Oops!", "License expired or invalid");
+ }
+ return ScanbotSDK.MAUI.ScanbotSDK.IsLicenseValid;
+ }
+
+ public static bool CheckPage(ContentPage context, IScannedPage page)
+ {
+ var result = page != null;
+ if (!result)
+ {
+ ViewUtils.Alert(context, "Oops!", "Please select a page");
+ }
+ return result;
+ }
+
+ public static bool CheckDocuments(ContentPage context, IEnumerable documents)
+ {
+ var result = documents != null && documents.Count() > 0;
+ if (!result)
+ {
+ ViewUtils.Alert(context, "Oops!", "Please import or scan a document first");
+ }
+ return result;
+ }
+
+ public static string ParseBarcodes(List barcodes)
+ {
+ var builder = new StringBuilder();
+
+ foreach (var code in barcodes)
+ {
+ builder.AppendLine($"{code.Format}: {code.Text}");
+ }
+
+ return builder.ToString();
+ }
+
+ public static string ParseMRZResult(MrzScannerResult result)
+ {
+ var builder = new StringBuilder();
+ builder.AppendLine($"DocumentType: {result.DocumentType}");
+ foreach (var field in result.Document.Fields)
+ {
+ builder.AppendLine($"{field.Type.Name}: {field.Value.Text} ({field.Value.Confidence:F2})");
+ }
+ return builder.ToString();
+ }
+
+ public static string ParseEHICResult(HealthInsuranceCardScannerResult result)
+ {
+ var builder = new StringBuilder();
+ builder.AppendLine($"DocumentType: European Health insurance card");
+ foreach (var field in result.Fields)
+ {
+ builder.AppendLine($"{field.Type}: {field.Value} ({field.Confidence:F2})");
+ }
+ return builder.ToString();
+ }
+
+ public static string ParseGDRResult(GenericDocumentRecognizerResult result)
+ {
+ var firstDocument = result.Documents.First();
+ return string.Join("\n", firstDocument.Fields
+ .Where((f) => f != null && f.Type != null && f.Type.Name != null && f.Value != null && f.Value.Text != null)
+ .Select((f) => string.Format("{0}: {1}", f.Type.Name, f.Value.Text))
+ );
+ }
+
+ public static string ParseCheckResult(CheckRecognizerResult result)
+ {
+ return string.Join("\n", result.Document.Fields
+ .Where((f) => f != null && f.Type != null && f.Type.Name != null && f.Value != null && f.Value.Text != null)
+ .Select((f) => string.Format("{0}: {1}", f.Type.Name, f.Value.Text))
+ );
+ }
+
+ public static string ParseTextDataScannerResult(TextDataScannerResult result)
+ {
+ return string.Format("{0} (confidence: {1})", result.Text, result.Confidence);
+ }
+ }
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Utils/StringUtils.cs b/MigrationForms/MigrationFormsExampleToMAUI/Utils/StringUtils.cs
new file mode 100644
index 0000000..d0ac929
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Utils/StringUtils.cs
@@ -0,0 +1,8 @@
+namespace MigrationFormsExampleToMAUI.Utils;
+
+public class StringUtils
+{
+ public static string CopyrightLabel =>
+ $"Copyright (c) {DateTime.Now.Year} doo GmbH. All rights reserved";
+
+}
\ No newline at end of file
diff --git a/MigrationForms/MigrationFormsExampleToMAUI/Utils/ViewUtils.cs b/MigrationForms/MigrationFormsExampleToMAUI/Utils/ViewUtils.cs
new file mode 100644
index 0000000..236426d
--- /dev/null
+++ b/MigrationForms/MigrationFormsExampleToMAUI/Utils/ViewUtils.cs
@@ -0,0 +1,51 @@
+namespace MigrationFormsExampleToMAUI.Utils;
+
+public class ViewUtils
+{
+ public static async void Alert(ContentPage context, string title, string message)
+ {
+ await context.DisplayAlert(title, message, "Close");
+ }
+
+ public static ViewCell CreateCell(string title, EventHandler action, Color? color = null)
+ {
+ if (color == null)
+ {
+ color = Colors.Black;
+ }
+
+ var cell = new ViewCell
+ {
+ View = new Label
+ {
+ Text = title,
+ VerticalTextAlignment = TextAlignment.Center,
+ Margin = new Thickness(20, 0, 0, 0),
+ FontSize = 14,
+ TextColor = (Color)color
+ }
+ };
+
+ cell.Tapped += action;
+
+ return cell;
+ }
+
+ public static ViewCell CreateCopyrightCell()
+ {
+ var cell = new ViewCell
+ {
+ View = new Label
+ {
+ Text = StringUtils.CopyrightLabel,
+ HorizontalTextAlignment = TextAlignment.Center,
+ VerticalTextAlignment = TextAlignment.Center,
+ Padding = new Thickness(0, 25, 0, 25),
+ TextColor = Colors.Gray,
+ FontSize = 12
+ }
+ };
+
+ return cell;
+ }
+}
\ No newline at end of file