Implementing Anyline

This section will guide you through the implementation process of the Anyline Scan View, including configuration and handling scan results.

Adding a Scan View

  • Open the Main.axml file in your Resources/Layout node

    • Alternatively, create new ScanActivity.java and scan_activity.axml Activity and Layout files, and open the scan_activity.axml file

  • Switch from the Design tab to the Source tab

  • Change the XML Code to the example shown below:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:id="@+id/main_layout">
	<io.anyline2.view.ScanView
		android:id="@+id/scan_view"
		android:layout_width="match_parent"
		android:layout_height="match_parent" />
</RelativeLayout>

You now added a ScanView to your Activity. Your scanning use-case depends on initializing this ScanView with a specific configuration. For this documentation show-case, we’re going to scan the MRZ string of Identification Documents.

Providing an Anyline Configuration to the Scan View

With the Anyline Configuration, you can define your whole use case, including scanning parameters and the look & feel of your scanning view.
The configuration parameters define the visual information presented to the user, as well as the scan settings (like the resolution, etc.) and feedback that is presented to the user.

Have a look at the Scanning Capabilities section for more specific implementation details.

You can adapt your scan view easily by adding a .json file as an asset to your project and loading it. In this example, we will create a .json file and configure it for the MRZ scanning.

Visual Studio

  • In your Assets treenode, right-click on the folder and select Add > New Item…​

  • Choose Visual C# > Text File and name it scanConfig.json

Build Action

Make sure that the build action for this file is set to AndroidAsset.

Now open the file and paste the code from the following example:

{
	"cameraConfig": {
		"captureResolution": "1080p",
		"zoomGesture": true
	},
	"flashConfig": {
		"mode": "manual",
		"alignment": "top_left"
	},
	"viewPluginConfig": {
		"pluginConfig": {
			"id": "ID",
			"cancelOnResult": true,
			"mrzConfig": {
				"strictMode": false,
				"cropAndTransformID": false
			}
		},
		"cutoutConfig": {
			"maxWidthPercent": "90%",
			"maxHeightPercent": "90%",
			"alignment": "center",
			"ratioFromSize": {
				"width": 50,
				"height": 31
			},
			"cropPadding": {
			},
			"outerColor": "000000",
			"outerAlpha": 0.3,
			"strokeWidth": 2,
			"strokeColor": "0099FF",
			"cornerRadius": 4,
			"feedbackStrokeColor": "0099FF"
		},
		"scanFeedbackConfig": {
			"style": "rect",
			"visualFeedbackRedrawTimeout": 100,
			"strokeWidth": 2,
			"strokeColor": "0099FF",
			"fillColor": "220099FF",
			"beepOnResult": true,
			"vibrateOnResult": true,
			"blinkAnimationOnResult": false
		}
	}
}

This JSON builds the whole MRZ scanning use-case.

The Activity Lifecycle

In the Scan Activity lifecycle, it’s important to configure/initialize and start/stop Anyline at the right places. This section will cover where you should implement which functionality in order to avoid pitfalls and provide a smooth scanning experience.

OnCreate

The OnCreate lifecycle method is where the ScanView should be set up.

protected override void OnCreate(Bundle bundle)
{
	base.OnCreate(bundle);
	try
	{
		SupportActionBar.SetHomeButtonEnabled(true);
		SupportActionBar.SetDisplayHomeAsUpEnabled(true);

		Title = "I'm scanning with Anyline!"

		Window.SetFlags(WindowManagerFlags.KeepScreenOn, WindowManagerFlags.KeepScreenOn);

		SetContentView(Resource.Layout.scan_activity);
		_scanView = FindViewById<IO.Anyline2.View.ScanView>(Resource.Id.scan_view);

		// the initialization parses the JSON configuration file and builds the whole use-case
		_scanView.Init("scan_config.json");

		// attributes this activity as the listener for results
		_scanView.ScanViewPlugin.ResultReceived = this;

		// handle camera open events
		_scanView.CameraView.CameraOpened += ScanView_CameraOpened;

		// handle camera error events
		_scanView.CameraView.CameraError += ScanView_CameraError;

		_isInitialized = true;
	}
	catch (Exception e)
	{
		System.Diagostics.Debug.WriteLine(e.ToString());
		// treat the exception accordingly
	}
}

Start Scanning

OnResume is usually the lifecycle method where you want to start the scanning process. To do so, you can follow the example below:

protected override void OnResume()
{
	base.OnResume();
	try
	{
		if (_isInitialized)
			_scanView.Start();
	}
	catch (Exception e)
	{
		System.Diagostics.Debug.WriteLine(e.ToString());
		// treat the exception accordingly
	}
}

Stop Scanning

When your ScanView loses focus, you should also stop the scanning process. The OnPause is usually the lifecycle method where you want to stop the scanning process. To do so, you can follow the example below:

protected override void OnPause()
{
	base.OnPause();
	StopScanning();
}

Also, make sure to stop the scanning process when the Home and Back buttons are tapped. You can also use the Dispose() method to release any attached listener and avoid memory leaks.

public override bool OnOptionsItemSelected(IMenuItem item)
{
	switch (item.ItemId)
	{
		case Android.Resource.Id.Home:
			StopScanning();
			return true;
		default:
			return base.OnOptionsItemSelected(item);
	}
}

public override void OnBackPressed()
{
	base.OnBackPressed();
	StopScanning();
}

private void StopScanning()
{
	if (_scanView != null)
	{
		_scanView.Stop();
		_scanView.CameraView.ReleaseCameraInBackground();
	}
}

protected override void Dispose(bool disposing)
{
	base.Dispose(disposing);
	try
	{
		if (_scanView != null)
		{
			_scanView.Dispose();
			_scanView.CameraView.CameraOpened -= ScanView_CameraOpened;
			_scanView.CameraView.CameraError -= ScanView_CameraError;
			_scanView = null;

			_isInitialized = false;
		}
	}
	catch (Exception) { }
}

Handling Scan Results

In this example, we’ve set our own ScanActivity as the listener for results (in the OnCreate method). To receive those results, the ScanActivity needs to implement the IEvent interface.

The interface can be implemented by right-clicking on the IEvent interface and selecting Implement Interface. A method that is called each time our scan view is scanning and reporting a result will be generated.

public class ScanActivity : AppCompatActivity, IO.Anyline2.IEvent
{
	...

	public void EventReceived(Java.Lang.Object data)
	{
		var scanResult = (data as IO.Anyline2.ScanResult);
		System.Diagnostics.Debug.WriteLine(scanResult.Result);
	}

	...
}

Cleaning up Resources

Anyline is using a lot of memory and processing power in order to perform scans in the fastest and most accurate way possible. Therefore, it is important that the resources are freed again. Usually, the GarbageCollector (GC) does this automatically. However, it cannot clean up a resource if there is still a handle on that resource, for example an Anyline scanning thread is still running or Events are still registered. Please also take care of freeing the Anyline images from the scan result when you don’t need to display them anymore.

protected override void Dispose(bool disposing)
{
	base.Dispose(disposing);
	try
	{
		if (_scanView != null)
		{
			_scanView.Dispose();
			_scanView.CameraOpened -= ScanView_CameraOpened;
			_scanView.CameraError -= ScanView_CameraError;
			_scanView = null;

			_isInitialized = false;
		}
	}
	catch (Exception) { }
}

Full Source Code

You can find the full source code on GitHub.
Open the solution in Visual Studio, build and deploy the application onto your Android device. For more Android-specific documentation, please refer to the documentation of the Anyline Native Android SDK.

Reduce Final App Size

Check now the Reduce App Size to learn how to remove unnecessary assets and reduce the final app’s size.