diff --git a/src/Functions/SantaTalk.Models/SantaTalk.Models.csproj b/src/Functions/SantaTalk.Models/SantaTalk.Models.csproj index 9f5c4f4..6e86919 100644 --- a/src/Functions/SantaTalk.Models/SantaTalk.Models.csproj +++ b/src/Functions/SantaTalk.Models/SantaTalk.Models.csproj @@ -4,4 +4,8 @@ netstandard2.0 + + + + diff --git a/src/Functions/SantaTalk.Models/SantaVisionResult.cs b/src/Functions/SantaTalk.Models/SantaVisionResult.cs new file mode 100644 index 0000000..c8829ea --- /dev/null +++ b/src/Functions/SantaTalk.Models/SantaVisionResult.cs @@ -0,0 +1,39 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace SantaTalk.Models +{ + public class Word + { + public List BoundingBox { get; set; } + public string Text { get; set; } + public string Confidence { get; set; } + } + + public class Line + { + public List BoundingBox { get; set; } + public string Text { get; set; } + public List Words { get; set; } + } + + public class RecognitionResult + { + public int Page { get; set; } + public double ClockwiseOrientation { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public string Unit { get; set; } + public List Lines { get; set; } + } + + public class RootObject + { + public string Status { get; set; } + + [JsonProperty("recognitionResults")] + public List RecognitionResults { get; set; } + } +} diff --git a/src/SantaTalk.Android/MainActivity.cs b/src/SantaTalk.Android/MainActivity.cs index 15fa73e..8db9358 100644 --- a/src/SantaTalk.Android/MainActivity.cs +++ b/src/SantaTalk.Android/MainActivity.cs @@ -6,6 +6,7 @@ using Android.Views; using Android.Widget; using Android.OS; +using Plugin.CurrentActivity; namespace SantaTalk.Droid { @@ -22,6 +23,7 @@ protected override void OnCreate(Bundle savedInstanceState) Xamarin.Essentials.Platform.Init(this, savedInstanceState); global::Xamarin.Forms.Forms.Init(this, savedInstanceState); FFImageLoading.Forms.Platform.CachedImageRenderer.Init(true); + CrossCurrentActivity.Current.Init(this, savedInstanceState); LoadApplication(new App()); } public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) diff --git a/src/SantaTalk.Android/Properties/AndroidManifest.xml b/src/SantaTalk.Android/Properties/AndroidManifest.xml index ba3f8c2..99bbfb1 100644 --- a/src/SantaTalk.Android/Properties/AndroidManifest.xml +++ b/src/SantaTalk.Android/Properties/AndroidManifest.xml @@ -1,6 +1,15 @@  - - + + + + + + + \ No newline at end of file diff --git a/src/SantaTalk.Android/Properties/AssemblyInfo.cs b/src/SantaTalk.Android/Properties/AssemblyInfo.cs index 82620f6..11fbf35 100644 --- a/src/SantaTalk.Android/Properties/AssemblyInfo.cs +++ b/src/SantaTalk.Android/Properties/AssemblyInfo.cs @@ -32,3 +32,5 @@ // Add some common permissions, these can be removed if not needed [assembly: UsesPermission(Android.Manifest.Permission.Internet)] [assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)] +[assembly: UsesFeature("android.hardware.camera", Required = false)] +[assembly: UsesFeature("android.hardware.camera.autofocus", Required = false)] diff --git a/src/SantaTalk.Android/Resources/xml/file_paths.xml b/src/SantaTalk.Android/Resources/xml/file_paths.xml new file mode 100644 index 0000000..7634148 --- /dev/null +++ b/src/SantaTalk.Android/Resources/xml/file_paths.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/SantaTalk.Android/SantaTalk.Android.csproj b/src/SantaTalk.Android/SantaTalk.Android.csproj index ffcd81d..c787721 100644 --- a/src/SantaTalk.Android/SantaTalk.Android.csproj +++ b/src/SantaTalk.Android/SantaTalk.Android.csproj @@ -1,4 +1,4 @@ - + Debug @@ -52,6 +52,9 @@ + + 4.0.1.5 + @@ -98,43 +101,50 @@ - - + + + + - - + + + + - - + + + + - - + + + + - - + + + + - - + + + + - - + + + + - - - - - - - @@ -148,5 +158,10 @@ - - + + + Designer + + + + \ No newline at end of file diff --git a/src/SantaTalk.sln b/src/SantaTalk.sln index bb1864b..4e115fc 100644 --- a/src/SantaTalk.sln +++ b/src/SantaTalk.sln @@ -1,71 +1,79 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SantaTalk", "SantaTalk\SantaTalk.csproj", "{091675BF-545D-4B21-A77B-F24ABDEAF6AA}" +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29613.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SantaTalk", "SantaTalk\SantaTalk.csproj", "{091675BF-545D-4B21-A77B-F24ABDEAF6AA}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SantaTalk.Android", "SantaTalk.Android\SantaTalk.Android.csproj", "{9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SantaTalk.Models", "Functions\SantaTalk.Models\SantaTalk.Models.csproj", "{07CBBC6F-2C49-44FD-B479-90907EC442BA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SantaTalk.Models", "Functions\SantaTalk.Models\SantaTalk.Models.csproj", "{07CBBC6F-2C49-44FD-B479-90907EC442BA}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SantaTalk.iOS", "SantaTalk.iOS\SantaTalk.iOS.csproj", "{1A8F4AAD-CF42-44DA-8289-1E100047AE1D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - Debug|iPhoneSimulator = Debug|iPhoneSimulator - Release|iPhoneSimulator = Release|iPhoneSimulator Debug|iPhone = Debug|iPhone + Debug|iPhoneSimulator = Debug|iPhoneSimulator + Release|Any CPU = Release|Any CPU Release|iPhone = Release|iPhone + Release|iPhoneSimulator = Release|iPhoneSimulator EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Release|Any CPU.Build.0 = Release|Any CPU - {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Debug|iPhone.ActiveCfg = Debug|Any CPU {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Debug|iPhone.Build.0 = Debug|Any CPU + {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Release|Any CPU.Build.0 = Release|Any CPU {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Release|iPhone.ActiveCfg = Release|Any CPU {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Release|iPhone.Build.0 = Release|Any CPU + {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {091675BF-545D-4B21-A77B-F24ABDEAF6AA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Release|Any CPU.Build.0 = Release|Any CPU - {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Debug|Any CPU.Deploy.0 = Debug|Any CPU {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Debug|iPhone.ActiveCfg = Debug|Any CPU {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Debug|iPhone.Build.0 = Debug|Any CPU + {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Release|Any CPU.Build.0 = Release|Any CPU {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Release|iPhone.ActiveCfg = Release|Any CPU {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Release|iPhone.Build.0 = Release|Any CPU + {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {9F3CF5B6-BC95-42B0-97A9-6024681F1FC0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Release|Any CPU.Build.0 = Release|Any CPU - {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Debug|iPhone.ActiveCfg = Debug|Any CPU {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Debug|iPhone.Build.0 = Debug|Any CPU + {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Release|Any CPU.Build.0 = Release|Any CPU {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Release|iPhone.ActiveCfg = Release|Any CPU {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Release|iPhone.Build.0 = Release|Any CPU + {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {07CBBC6F-2C49-44FD-B479-90907EC442BA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator - {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Release|Any CPU.ActiveCfg = Release|iPhoneSimulator - {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Release|Any CPU.Build.0 = Release|iPhoneSimulator - {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator - {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator - {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator - {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Debug|iPhone.ActiveCfg = Debug|iPhone {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Debug|iPhone.Build.0 = Debug|iPhone + {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator + {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator + {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Release|Any CPU.ActiveCfg = Release|iPhoneSimulator + {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Release|Any CPU.Build.0 = Release|iPhoneSimulator {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Release|iPhone.ActiveCfg = Release|iPhone {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Release|iPhone.Build.0 = Release|iPhone + {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator + {1A8F4AAD-CF42-44DA-8289-1E100047AE1D}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {856E615C-5EFE-46E4-8884-2C0B5C563964} EndGlobalSection EndGlobal diff --git a/src/SantaTalk/MainPage.xaml b/src/SantaTalk/MainPage.xaml index 7263026..ea7e4d7 100644 --- a/src/SantaTalk/MainPage.xaml +++ b/src/SantaTalk/MainPage.xaml @@ -22,8 +22,14 @@ BackgroundGradientEndColor="{StaticResource light_gradient}"> + + + + + + diff --git a/src/SantaTalk/SantaTalk.csproj b/src/SantaTalk/SantaTalk.csproj index 7ad5715..c2917f2 100644 --- a/src/SantaTalk/SantaTalk.csproj +++ b/src/SantaTalk/SantaTalk.csproj @@ -11,6 +11,7 @@ + diff --git a/src/SantaTalk/ViewModels/MainPageViewModel.cs b/src/SantaTalk/ViewModels/MainPageViewModel.cs index 2663946..ca36910 100644 --- a/src/SantaTalk/ViewModels/MainPageViewModel.cs +++ b/src/SantaTalk/ViewModels/MainPageViewModel.cs @@ -1,18 +1,192 @@ using System; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; using System.Windows.Input; using MvvmHelpers; +using Newtonsoft.Json.Linq; +using Plugin.Media; +using Plugin.Media.Abstractions; +using SantaTalk.Models; using Xamarin.Forms; namespace SantaTalk { public class MainPageViewModel : BaseViewModel { + // Add your Computer Vision subscription key and endpoint to your environment variables. + static string subscriptionKey = Environment.GetEnvironmentVariable("COMPUTER_VISION_SUBSCRIPTION_KEY"); + + static string endpoint = Environment.GetEnvironmentVariable("COMPUTER_VISION_ENDPOINT"); + + // the Batch Read method endpoint + static string uriBase = endpoint + "vision/v2.1/read/core/asyncBatchAnalyze"; public MainPageViewModel() { SendLetterCommand = new Command(async () => { await Application.Current.MainPage.Navigation.PushAsync(new ResultsPage(KidsName, LetterText)); }); + TakePictureCommand = new Command(async () => + { + await CrossMedia.Current.Initialize(); + + if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported) + { + return; + } + + var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions + { + Directory = "SantaChallenge", + Name = "test.jpg", + PhotoSize = PhotoSize.Medium + }); + + if (file == null) + return; + try + { + AnalysePicture = true; + var result = await ReadText(file.Path); + var splittedResult = result.Split(Environment.NewLine.ToCharArray()); + LetterText = result; + KidsName = splittedResult[0]; + } + catch (Exception e) + { + return; + } + finally + { + AnalysePicture = false; + } + }); + } + + /// + /// Gets the text from the specified image file by using + /// the Computer Vision REST API. + /// + /// The image file with text. + private async Task ReadText(string imageFilePath) + { + try + { + HttpClient client = new HttpClient(); + + // Request headers. + client.DefaultRequestHeaders.Add( + "Ocp-Apim-Subscription-Key", subscriptionKey); + + // Assemble the URI for the REST API method. + string uri = uriBase; + + HttpResponseMessage response; + + // Two REST API methods are required to extract text. + // One method to submit the image for processing, the other method + // to retrieve the text found in the image. + + // operationLocation stores the URI of the second REST API method, + // returned by the first REST API method. + string operationLocation; + + // Reads the contents of the specified local image + // into a byte array. + byte[] byteData = GetImageAsByteArray(imageFilePath); + + // Adds the byte array as an octet stream to the request body. + using (ByteArrayContent content = new ByteArrayContent(byteData)) + { + // This example uses the "application/octet-stream" content type. + // The other content types you can use are "application/json" + // and "multipart/form-data". + content.Headers.ContentType = + new MediaTypeHeaderValue("application/octet-stream"); + + // The first REST API method, Batch Read, starts + // the async process to analyze the written text in the image. + response = await client.PostAsync(uri, content); + } + + // The response header for the Batch Read method contains the URI + // of the second method, Read Operation Result, which + // returns the results of the process in the response body. + // The Batch Read operation does not return anything in the response body. + if (response.IsSuccessStatusCode) + operationLocation = + response.Headers.GetValues("Operation-Location").FirstOrDefault(); + else + { + // Display the JSON error data. + string errorString = await response.Content.ReadAsStringAsync(); + Console.WriteLine("\n\nResponse:\n{0}\n", + JToken.Parse(errorString).ToString()); + throw new Exception(errorString); + } + + // If the first REST API method completes successfully, the second + // REST API method retrieves the text written in the image. + // + // Note: The response may not be immediately available. Text + // recognition is an asynchronous operation that can take a variable + // amount of time depending on the length of the text. + // You may need to wait or retry this operation. + // + // This example checks once per second for ten seconds. + string contentString; + int i = 0; + do + { + System.Threading.Thread.Sleep(1000); + response = await client.GetAsync(operationLocation); + contentString = await response.Content.ReadAsStringAsync(); + ++i; + } + while (i < 10 && contentString.IndexOf("\"status\":\"Succeeded\"") == -1); + + if (i == 10 && contentString.IndexOf("\"status\":\"Succeeded\"") == -1) + { + Console.WriteLine("\nTimeout error.\n"); + throw new TimeoutException(); + } + + // Display the JSON response. + Console.WriteLine("\nResponse:\n\n{0}\n", + JToken.Parse(contentString).ToString()); + var result = Newtonsoft.Json.JsonConvert.DeserializeObject(contentString); + if (result.RecognitionResults.FirstOrDefault() != null) + { + var listOfWords = result.RecognitionResults.First().Lines.Select(line => line.Text).ToList(); + return string.Join(Environment.NewLine, listOfWords); + } + throw new Exception("No result could be parsed"); + } + catch (Exception e) + { + Console.WriteLine("\n" + e.Message); + throw e; + } + } + + /// + /// Returns the contents of the specified file as a byte array. + /// + /// The image file to read. + /// The byte array of the image data. + static byte[] GetImageAsByteArray(string imageFilePath) + { + // Open a read-only file stream for the specified file. + using (FileStream fileStream = + new FileStream(imageFilePath, FileMode.Open, FileAccess.Read)) + { + // Read the file's contents into a byte array. + BinaryReader binaryReader = new BinaryReader(fileStream); + return binaryReader.ReadBytes((int)fileStream.Length); + } } string kidsName; @@ -23,12 +197,22 @@ public string KidsName } string letterText = "Dear Santa..."; + private bool analysePicture; + public string LetterText { get => letterText; set => SetProperty(ref letterText, value); } + public bool AnalysePicture + { + get => analysePicture; + set => SetProperty(ref analysePicture, value); + } + public ICommand SendLetterCommand { get; } + + public ICommand TakePictureCommand { get; } } }