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