diff --git a/samples/Plugin.Maui.Feature.Sample/App.xaml.cs b/samples/Plugin.Maui.Feature.Sample/App.xaml.cs
index 18fadfc..5bab0b5 100644
--- a/samples/Plugin.Maui.Feature.Sample/App.xaml.cs
+++ b/samples/Plugin.Maui.Feature.Sample/App.xaml.cs
@@ -1,11 +1,11 @@
-namespace Plugin.Maui.Feature.Sample;
+namespace Plugin.Maui.Feature.Sample;
public partial class App : Application
{
- public App()
- {
- InitializeComponent();
+ public App()
+ {
+ InitializeComponent();
- MainPage = new AppShell();
- }
-}
\ No newline at end of file
+ MainPage = new AppShell();
+ }
+}
diff --git a/samples/Plugin.Maui.Feature.Sample/MainPage.xaml b/samples/Plugin.Maui.Feature.Sample/MainPage.xaml
index 6e07604..2532cbe 100644
--- a/samples/Plugin.Maui.Feature.Sample/MainPage.xaml
+++ b/samples/Plugin.Maui.Feature.Sample/MainPage.xaml
@@ -3,16 +3,27 @@
x:Class="Plugin.Maui.Feature.Sample.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
- Title="Feature Plugin">
-
-
-
-
+ Title="OCR Plugin">
+
-
+ Clicked="OpenFromCameraBtn_Clicked"
+ Text="From Camera .." />
+
+
+
+
diff --git a/samples/Plugin.Maui.Feature.Sample/MainPage.xaml.cs b/samples/Plugin.Maui.Feature.Sample/MainPage.xaml.cs
index 40d32a7..b9c5938 100644
--- a/samples/Plugin.Maui.Feature.Sample/MainPage.xaml.cs
+++ b/samples/Plugin.Maui.Feature.Sample/MainPage.xaml.cs
@@ -4,31 +4,78 @@ namespace Plugin.Maui.Feature.Sample;
public partial class MainPage : ContentPage
{
- readonly IOcrService _feature;
+ readonly IOcrService _ocr;
public MainPage(IOcrService feature)
{
InitializeComponent();
- _feature = feature;
+ _ocr = feature;
}
protected override async void OnAppearing()
{
base.OnAppearing();
- await _feature.InitAsync();
+ await _ocr.InitAsync();
}
- private async void RunOcrBtn_Clicked(object sender, EventArgs e)
+ private async void OpenFromCameraBtn_Clicked(object sender, EventArgs e)
{
- // load test.jpg and then run OCR on it
- using var fileStream = await FileSystem.Current.OpenAppPackageFileAsync("test.jpg");
+ if (MediaPicker.Default.IsCaptureSupported)
+ {
+ var photo = await MediaPicker.Default.CapturePhotoAsync();
- // read all bytes of the image from fileStream
- var imageData = new byte[fileStream.Length];
- await fileStream.ReadAsync(imageData, 0, imageData.Length);
+ if (photo != null)
+ {
+ // Open a stream to the photo
+ using var sourceStream = await photo.OpenReadAsync();
- var result = await _feature.RecognizeTextAsync(imageData);
+ // Create a byte array to hold the image data
+ var imageData = new byte[sourceStream.Length];
+
+ // Read the stream into the byte array
+ await sourceStream.ReadAsync(imageData);
+
+ var result = await _ocr.RecognizeTextAsync(imageData);
+
+ ResultLbl.Text = result.AllText;
+
+ ClearBtn.IsEnabled = true;
+ }
+ }
+ else
+ {
+ await DisplayAlert(title: "Sorry", message: "Image capture is not supported on this device.", cancel: "OK");
+ }
+ }
+
+ private async void OpenFromFileBtn_Clicked(object sender, EventArgs e)
+ {
+ var photo = await MediaPicker.Default.PickPhotoAsync();
+
+ if (photo != null)
+ {
+ // Open a stream to the photo
+ using var sourceStream = await photo.OpenReadAsync();
+
+ // Create a byte array to hold the image data
+ var imageData = new byte[sourceStream.Length];
+
+ // Read the stream into the byte array
+ await sourceStream.ReadAsync(imageData);
+
+ var result = await _ocr.RecognizeTextAsync(imageData);
+
+ ResultLbl.Text = result.AllText;
+
+ ClearBtn.IsEnabled = true;
+ }
+ }
+
+ private void ClearBtn_Clicked(object sender, EventArgs e)
+ {
+ ResultLbl.Text = string.Empty;
+ ClearBtn.IsEnabled = false;
}
}
diff --git a/samples/Plugin.Maui.Feature.Sample/Platforms/Android/AndroidManifest.xml b/samples/Plugin.Maui.Feature.Sample/Platforms/Android/AndroidManifest.xml
index ddd284f..1f6f375 100644
--- a/samples/Plugin.Maui.Feature.Sample/Platforms/Android/AndroidManifest.xml
+++ b/samples/Plugin.Maui.Feature.Sample/Platforms/Android/AndroidManifest.xml
@@ -1,6 +1,13 @@
-
+
-
+
+
+
+
+
+
+
+
diff --git a/samples/Plugin.Maui.Feature.Sample/Platforms/Android/MainActivity.cs b/samples/Plugin.Maui.Feature.Sample/Platforms/Android/MainActivity.cs
index 1228e53..6828eec 100644
--- a/samples/Plugin.Maui.Feature.Sample/Platforms/Android/MainActivity.cs
+++ b/samples/Plugin.Maui.Feature.Sample/Platforms/Android/MainActivity.cs
@@ -1,10 +1,24 @@
-using Android.App;
+using Android.App;
using Android.Content.PM;
using Android.OS;
+// Needed for Picking photo/video
+[assembly: UsesPermission(Android.Manifest.Permission.ReadExternalStorage, MaxSdkVersion = 32)]
+[assembly: UsesPermission(Android.Manifest.Permission.ReadMediaAudio)]
+[assembly: UsesPermission(Android.Manifest.Permission.ReadMediaImages)]
+[assembly: UsesPermission(Android.Manifest.Permission.ReadMediaVideo)]
+
+// Needed for Taking photo/video
+[assembly: UsesPermission(Android.Manifest.Permission.Camera)]
+[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage, MaxSdkVersion = 32)]
+
+// Add these properties if you would like to filter out devices that do not have cameras, or set to false to make them optional
+[assembly: UsesFeature("android.hardware.camera", Required = true)]
+[assembly: UsesFeature("android.hardware.camera.autofocus", Required = true)]
+
namespace Plugin.Maui.Feature.Sample;
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
-}
\ No newline at end of file
+}
diff --git a/samples/Plugin.Maui.Feature.Sample/Platforms/iOS/Info.plist b/samples/Plugin.Maui.Feature.Sample/Platforms/iOS/Info.plist
index 0004a4f..0252709 100644
--- a/samples/Plugin.Maui.Feature.Sample/Platforms/iOS/Info.plist
+++ b/samples/Plugin.Maui.Feature.Sample/Platforms/iOS/Info.plist
@@ -1,32 +1,40 @@
-
- LSRequiresIPhoneOS
-
- UIDeviceFamily
-
- 1
- 2
-
- UIRequiredDeviceCapabilities
-
- arm64
-
- UISupportedInterfaceOrientations
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UISupportedInterfaceOrientations~ipad
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationPortraitUpsideDown
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- XSAppIconAssets
- Assets.xcassets/appicon.appiconset
-
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+ NSCameraUsageDescription
+ This app needs access to the camera to take photos.
+ NSMicrophoneUsageDescription
+ This app needs access to microphone for taking videos.
+ NSPhotoLibraryAddUsageDescription
+ This app needs access to the photo gallery for picking photos and videos.
+ NSPhotoLibraryUsageDescription
+ This app needs access to photos gallery for picking photos and videos.
+
diff --git a/samples/Plugin.Maui.Feature.Sample/Plugin.Maui.OCR.Sample.csproj b/samples/Plugin.Maui.Feature.Sample/Plugin.Maui.OCR.Sample.csproj
index 5589e7b..eb7ee4f 100644
--- a/samples/Plugin.Maui.Feature.Sample/Plugin.Maui.OCR.Sample.csproj
+++ b/samples/Plugin.Maui.Feature.Sample/Plugin.Maui.OCR.Sample.csproj
@@ -1,4 +1,4 @@
-
+
net8.0-android;net8.0-ios;net8.0-maccatalyst
@@ -30,6 +30,11 @@
6.5
+
+ Apple Development: Created via API (BQQPP4HLY2)
+ VS: WildCard Development
+
+
@@ -47,13 +52,18 @@
-
-
-
-
+
+ 1.6.2.2
+
+
+
+
+
+ 119.0.0.5
+
diff --git a/samples/Plugin.Maui.Feature.Sample/Resources/Raw/test.jpg b/samples/Plugin.Maui.Feature.Sample/Resources/Raw/test.jpg
deleted file mode 100644
index 4ed7146..0000000
Binary files a/samples/Plugin.Maui.Feature.Sample/Resources/Raw/test.jpg and /dev/null differ
diff --git a/src/Plugin.Maui.OCR/OcrImplementation.android.cs b/src/Plugin.Maui.OCR/OcrImplementation.android.cs
index c3e87ea..07df0d3 100644
--- a/src/Plugin.Maui.OCR/OcrImplementation.android.cs
+++ b/src/Plugin.Maui.OCR/OcrImplementation.android.cs
@@ -1,7 +1,9 @@
+using System.Diagnostics;
using Android.Gms.Tasks;
using Android.Graphics;
using Android.Util;
using Plugin.Shared.OCR;
+using Xamarin.Google.MLKit.Common;
using Xamarin.Google.MLKit.Vision.Common;
using Xamarin.Google.MLKit.Vision.Text;
using Xamarin.Google.MLKit.Vision.Text.Latin;
@@ -43,6 +45,8 @@ public Task InitAsync(System.Threading.CancellationToken ct = default)
{
// Initialization might not be required for ML Kit's on-device text recognition,
// but you can perform any necessary setup here.
+
+
return Task.CompletedTask;
}
@@ -53,7 +57,36 @@ public async Task RecognizeTextAsync(byte[] imageData, System.Threadi
using var inputImage = InputImage.FromBitmap(image, 0);
using var textScanner = TextRecognition.GetClient(TextRecognizerOptions.DefaultOptions);
- return ProcessOcrResult(await ToAwaitableTask(textScanner.Process(inputImage).AddOnSuccessListener(new OnSuccessListener()).AddOnFailureListener(new OnFailureListener())));
+
+ MlKitException? lastException = null;
+ var maxRetries = 5;
+
+ for (var retry = 0; retry < maxRetries; retry++)
+ {
+ try
+ {
+ // Try to perform the OCR operation
+ return ProcessOcrResult(await ToAwaitableTask(textScanner.Process(inputImage).AddOnSuccessListener(new OnSuccessListener()).AddOnFailureListener(new OnFailureListener())));
+ }
+ catch (MlKitException ex) when (ex.Message.Contains("Waiting for the text optional module to be downloaded"))
+ {
+ // If the specific exception is caught, log it and wait before retrying
+ lastException = ex;
+ Debug.WriteLine($"OCR model is not ready. Waiting before retrying... Attempt {retry + 1}/{maxRetries}");
+ await Task.Delay(5000);
+ }
+ }
+
+ // If all retries have failed, throw the last exception
+ if (lastException != null)
+ {
+ throw lastException;
+ }
+ else
+ {
+ throw new InvalidOperationException("OCR operation failed without an exception.");
+ }
+
}
private static Task ToAwaitableTask(global::Android.Gms.Tasks.Task task)
diff --git a/src/Plugin.Maui.OCR/Plugin.Maui.OCR.csproj b/src/Plugin.Maui.OCR/Plugin.Maui.OCR.csproj
index 340c502..48815e1 100644
--- a/src/Plugin.Maui.OCR/Plugin.Maui.OCR.csproj
+++ b/src/Plugin.Maui.OCR/Plugin.Maui.OCR.csproj
@@ -1,108 +1,111 @@
-
- net8.0-android;net8.0-ios;net8.0-maccatalyst;net8.0
- $(TargetFrameworks);net8.0-windows10.0.19041.0
-
-
- true
- enable
- enable
- true
- latest
+
+ net8.0-android;net8.0-ios;net8.0-maccatalyst;net8.0
+ $(TargetFrameworks);net8.0-windows10.0.19041.0
+
+
+ true
+ enable
+ enable
+ true
+ latest
- 14.2
- 14.0
- 21.0
- 10.0.17763.0
- 10.0.17763.0
- 6.5
+ 14.2
+ 14.0
+ 21.0
+ 10.0.17763.0
+ 10.0.17763.0
+ 6.5
-
- kfrancis
- Copyright © Kori Francis
- True
- https://github.com/kfrancis/ocr
- https://github.com/kfrancis/ocr
- git
- dotnet-maui;maui;plugin;
- True
- true
- true
- snupkg
- .NET MAUI OCR Plugin
- Plugin.Maui.OCR provides the ability to perform OCR on an image using platform API.
- MIT
- True
- portable
- icon.png
- README.md
-
+
+ kfrancis
+ Copyright © Kori Francis
+ True
+ https://github.com/kfrancis/ocr
+ https://github.com/kfrancis/ocr
+ git
+ dotnet-maui;maui;plugin;
+ True
+ true
+ true
+ snupkg
+ .NET MAUI OCR Plugin
+ Plugin.Maui.OCR provides the ability to perform OCR on an image using platform API.
+ MIT
+ True
+ portable
+ icon.png
+ README.md
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ 116.0.0.4
+
+