.NET MAUI - Android Project

This section will guide you through the implementation process of Anyline on the MAUI - Android project, including initialization, configuration, and the handling of scan results.

Add Anyline.SDK.NET.Android via NuGet

To access the functionality of the Anyline .NET SDK, simply right-click on your project, go to manage Nuget packages and search for "Anyline".

Select the package Anyline.SDK.NET.Android and install the latest version available.

Accept the license agreement and you’re ready to use Anyline!

Set minimum Android target

In your MAUI project’s .csproj file, set the minimum version of your Android project to API level 24 (Android 7.0) or higher.

"<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('(TargetFramework)')) == 'android'">24.0</SupportedOSPlatformVersion>"

Add necessary permissions and features

The scanning activity needs the following permissions and features:

  • Permissions

    • CAMERA

    • VIBRATE

  • Features

    • android.hardware.camera

    • android.hardware.camera.flash

    • android.hardware.camera.autofocus

This is how you can add them to your AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.anyline.examples" android:versionCode="1" android:versionName="1.0" android:installLocation="preferExternal">
	<!--add android:hardwareAccelerated="true" for Camera2 API support-->
	<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true" android:hardwareAccelerated="true"></application>
	<uses-permission android:name="android.permission.CAMERA" />
	<uses-permission android:name="android.permission.VIBRATE" />
	<uses-feature android:name="android.hardware.camera" android:required="false" />
	<uses-feature android:name="android.hardware.camera.flash" />
	<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
</manifest>

Initialize the Anyline SDK

In this step, we will implement the partial class that implements the behaviour of the AnylineSDKService class, in the MAUI layer of our project. For this, create a new class in your Platforms/Android folder, called AnylineSDKService.Android.cs:

AnylineSDKService.Android.cs
namespace Anyline
{
	public partial class AnylineSDKService
	{
		public partial bool SetupWithLicenseKey(string licenseKey, out string licenseErrorMessage)
		{
			try
			{
				IO.Anyline2.AnylineSdk.Init(licenseKey, context: Examples.MAUI.MainActivity.Instance);
				licenseErrorMessage = null;
				return true;
			}
			catch (Exception ex)
			{
				licenseErrorMessage = ex.Message;
				return false;
			}
		}
	}
}

As you may have noticed, a Context is necessary to initialize the Anyline SDK. To retrieve this context, a simple solution consists of storing the MainActivity’s context in a static variable, which can then be accessed by the AnylineSDKService, for example:

MainActivity.cs
public class MainActivity : MauiAppCompatActivity
{
	public static Activity Instance;

	protected override void OnCreate(Bundle savedInstanceState)
	{
		base.OnCreate(savedInstanceState);
		Instance = this;

		...
	}
}

You can obtain this application Context however best fits your project.

Add the AnylineScanningViewRenderer Renderer

In your Platforms/Android folder, add a new folder called CustomRenderers, and inside of it, a new class called AnylineScanningViewRenderer.cs:

AnylineScanningViewRenderer.cs
using Android.App;
using Android.Content;
using Android.Content.Res;
using Android.Graphics;
using Android.Runtime;
using Android.Util;
using Android.Widget;
using Anyline.Examples.MAUI.Views;
using IO.Anyline2.Camera;
using IO.Anyline2;
using IO.Anyline2.View;
using Java.Util;
using Microsoft.Maui.Controls.Handlers.Compatibility;
using Microsoft.Maui.Controls.Platform;

namespace Anyline.Examples.MAUI.Platforms.Android.CustomRenderers
{
	/// <summary>
	/// This class is responsible for rendering the Anyline ScanView natively.
	/// </summary>
	internal class AnylineScanningViewRenderer : ViewRenderer, IEvent
	{
        private bool _initialized;
        private IO.Anyline2.View.ScanView _scanView;
        private Context _context;

        public AnylineScanningViewRenderer(Context context) : base(context)
        {
            _context = context;
        }

		protected override void OnAttachedToWindow()
		{
			base.OnAttachedToWindow();
			InitializeAnyline();
		}

		private void InitializeAnyline()
		{
			if (initialized)
				return;

			try
			{
                _scanView = new IO.Anyline2.View.ScanView(_context);

				// Obtain the JSON config file path from the "AnylineScanningView", defined in the MAUI level.
				string jsonConfigFilePath = (Element as AnylineScanningView).JSONConfigPath.Replace(".json", "") + ".json";

                // This is the main intialization method that will create our use case depending on the JSON configuration.
                _scanView.Init(jsonConfigFilePath);

                _scanView.ScanViewPlugin.ResultReceived = this;
                _scanView.ScanViewPlugin.ResultsReceived = this;

                // Handle camera open events
                _scanView.CameraView.CameraOpened += _scanView_CameraOpened;

                // Handle camera error events
                _scanView.CameraView.CameraError += _scanView_CameraError;

				initialized = true;
				AddView(_scanView);
            }
            catch (Exception e)
            {
                // show error
                Toast.MakeText(_context, e.ToString(), ToastLength.Long).Show();
                Log.Debug("AnylineScanningViewRenderer - Android", e.ToString());
            }
		}

        private void _scanView_CameraError(object sender, CameraErrorEventArgs e)
        {
            Log.Debug("AnylineScanningViewRenderer - Android", e.ToString());
        }

		private void _scanView_CameraOpened(object sender, CameraOpenedEventArgs e)
		{
			if (_scanView != null)
				_scanView.Start();
		}

        /// <summary>
        /// This method is called when a scan result is found.
        /// Since the native Java type is generic, we translated the type of the parameter to ScanResult due to .NET Android generic binding limitations.
        /// </summary>
        /// <param name="data">The scan result</param>
        public void EventReceived(Java.Lang.Object data)
        {
			DisposeAnyline();

            var anylineScanResult = data as IO.Anyline2.ScanResult;

			var bitmap = anylineScanResult.CutoutImage.Bitmap;
			MemoryStream stream = new MemoryStream();
			bitmap.Compress(Bitmap.CompressFormat.Jpeg, 100, stream);
			byte[] bitmapData = stream.ToArray();

			var mrzString = (anylineScanResult.PluginResult).MrzResult.MrzString;

			(Element as ScanExamplePage).OnResult?.Invoke(mrzString, bitmapData);
		}

        /// <summary>
        /// On layout change, propagate changes to ScanView.
        /// </summary>
        /// <param name="changed"></param>
        /// <param name="left"></param>
        /// <param name="top"></param>
        /// <param name="right"></param>
        /// <param name="bottom"></param>
        protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
        {
            base.OnLayout(changed, left, top, right, bottom);
            if (_scanView != null)
                _scanView.Layout(left, top, right, bottom);
        }

		/// <summary>
		/// On device rotated, dispose and re-initialize the ScanView.
		/// </summary>
		/// <param name="newConfig"></param>
		protected override void OnConfigurationChanged(Configuration newConfig)
		{
			base.OnConfigurationChanged(newConfig);
			DisposeAnyline();
			InitializeAnyline();
		}

		protected override void OnDetachedFromWindow()
		{
			DisposeAnyline();
			base.OnDetachedFromWindow();
		}

        private void DisposeAnyline()
        {
            if (_scanView != null)
            {
                _scanView.Stop();
                _scanView.CameraView.CameraOpened -= _scanView_CameraOpened;
                _scanView.CameraView.CameraError -= _scanView_CameraError;
                _scanView.CameraView.ReleaseCameraInBackground();
                _scanView.Dispose();
                _scanView = null;
            }
            _initialized = false;
            RemoveAllViews();
        }
	}
}

That’s it!

Your MAUI - Android project is all setup and ready to scan! Select a device and start scanning MRZ strings.

In the next section we will implement the MAUI - iOS renderer for our AnylineScanPage.

Full Source Code

You can find the full source code on the .NET GitHub Repository.
Open the solution in Visual Studio 2022+, build and deploy the application onto your Android device. More detailed information about all Anyline plugins can be found in the plugins section of this documentation.