Advanced Topics

This section covers detailed topics about the Anyline SDK on Android. You will not require knowledge about these advanced topics in your every day use of the SDK. However, in case you need specific information about certain topics in the Anyline SDK, it will be covered here.

Android 6 Runtime Camera Permissions

Starting with Android 6.0 (API Level 23), permissions are granted at runtime.

The Anyline SDK requires the camera permission to be granted before a scanView is inflated.

Therefore it is advised to check the permissions in an Activity prior to starting the Activity holding the scanView. If a scanView is inflated without the required camera permission, the SDK will throw a RuntimeException containing the message Camera usage not permitted.

Use CameraPermissionHelper Class

New in version 3.10.

As of version 3.10 of the Anyline Android SDK, you can use the CameraPermissionHelper class to conveniently check the required camera permissions before starting a scan acitivity.

Instantiate in onCreate
        // CameraPermissionHelper simplifies the request for the camera permission
        cameraPermissionHelper = new CameraPermissionHelper(this);
Check and request permissions
    private void checkedStartActivity(Intent intent) {

        // ask if permissions were already granted
        if(cameraPermissionHelper.hasPermissions()){
            startActivity(intent);
        } else{
            // otherwise request the permissions
            cameraPermissionHelper.requestPermissions();
            this.targetIntent = intent;
        }
    }
Handle the permission request results
    /**
     * Callback from the permission request. Can be directly forwarded to the
     * {@link CameraPermissionHelper#onRequestPermissionsResult(int, String[], int[])} to check the Camera
     * Permissions for Anyline.
     *
     * Any other permissions result requested by the app will also be forwarded to here, and should be checked after
     * the call to the {@link CameraPermissionHelper}
     *
     * @param requestCode the code of the permission request
     * @param permissions the requested permissions
     * @param grantResults the results of the request
     */
    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {

        // CameraPermissionHelper will return true if the permission for the camera was granted (and was made via the
        // CameraPermissionHelper class)
        if(cameraPermissionHelper.onRequestPermissionsResult(requestCode, permissions, grantResults)){
            startActivity(targetIntent);
        } else{
            // Displays a message to the user, asking to grant the permissions for the camera in order for Anyline to
            // work
            cameraPermissionHelper.showPermissionMessage(null);
        }

        // Other app permission checks go here
    }

Revoked permissions API < 23

In Android versions prior to 6.0 (API Level 23), if the user revokes the permission in Androids Settings > Apps, the Anyline SDK will report an error if the CameraOpenListener is set.

Add a DebugListener

To receive debug information about the current scan, a debug listener can be set to the scan view.

Add a debug listener to the scan view
scanView.setDebugListener(new AnylineDebugListener() {
    @Override
    public void onDebug(String name, Object value) {

    }

    @Override
    public void onRunSkipped(RunFailure runFailure) {

    }
});

The AnylineDebugListener interface defines the following callback methods:

onDebug(String name, Object value)

Debug information, like brightness values, text-outlines and other information is reported via this callback method.

The name parameter represents the name of the reported variable, while the value parameter holds the actual value of the variable.

To conveniently check the the reported information, the AnylineDebugListener interface defines constants for the names and the actual classes of the reported variables.

Please see AnylineDebugListener in the Javadocs for further information about checkeing and casting the variables.

onRunSkipped(RunFailure runFailure)

If a scan run - one run on a single image - is skipped, information why it was skipped is provided in this callback method via a RunFailure object.

Please see AnylineDebugListener in the Javadocs for further information.

Run Skipped

A skipped run does not mean that the scanning as a whole is stopped. The scanning proceeds with the next image until a valid result is detected. The information provided in the RunFailure is solely for debug purposes

Add a CameraOpenListener

To be notified about camera events from the SDK, i.e. camera open events and camera errors, you can set a CameraOpenListener.

The callback methods will provide information if the camera was succesfully opened, or if there was an error opening the camera in the Anyline SDK.

Add the Anyline Javadoc to Android Studio

You can easily add the Javadoc of the Anyline SDK to your Android Studio project.

  • In the Project Explorer, change your view setting to Project
  • Expand External Libraries

image-android-studio-external-library

  • Right click on the Anyline SDK library, and select Library Properties…
    • If you want to add the latest API doc from the web, click on the image-android-studio-web-api-doc-plus icon and add https://documentation.anyline.io/api/android/index.html
    • If you want to add the Javadoc that came with the bundle, click on the image-android-studio-local-api-doc-plus icon, navigate to the extracted bundle directory, go to Documentation, select javadoc and click okay.

image-android-studio-select-javadoc

Online Version

The online version of the Javadoc will always refer to the latest SDK version. If you use an older version of the Anyline SDK, it is advised to add the Javadoc included in the bundle you downloaded,

Use the Android Camera 2 API

The Anyline SDK does not use the Android Camera 2 API per default on devices supporting it.

If you would like to enable the Android Camera API 2 on devices that support it, you can do so by settting the app:api_2_enabled="true" in the xml layout of the scan view. For example:

Disable Camera 2 API
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="at.nineyards.anyline.examples.meter.ScanMeterActivity">

    <at.nineyards.anyline.modules.document.DocumentScanView
        android:id="@+id/document_scan_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:api_2_enabled="true"
        />

        ...

</RelativeLayout>

This does not affect devices that do not support the Camera API 2 - they will use a fallback mechanism to use the Android Camera API 1.

Visual Feedback & Camera API 2

On devices using the Camera API 2, adding an animation to the visual feedback may cause performance problems while scanning. See Camera API 2 and Visual Feedback Animations for further information.

Register a Listener for anyline_view_configuration_cutout changes

To get notified about changes in the layout of the anyline_view_configuration_cutout and the watermark, you can register a CutoutUpdateListener on the scanView.

This way you will be informed about changes in the, and avoid problems with your layout interfering with the cutout or the watermark.

To do so, you have to implement the CutoutUpdateListener Interface

The CutoutUpdateListener
/**
 * A listener that can be used to get notified if the cutout changed
 */
public interface CutoutUpdateListener {

    /**
     * Method is called on the UI thread, whenever the cutout is updated.
     *
     * @param cutoutRect    the rect where the cutout is on the view
     * @param waterMarkRect the rect where the watermark is on the view (may be null with commercial license)
     */
    void onCutoutUpdate(@NonNull Rect cutoutRect, @Nullable Rect waterMarkRect);
}

and set it on the scanView

Set the CutoutUpdateListener on the scanView
scanView.setCutoutUpdateListener(cutoutUpdateListener);

Proguard

If you use Proguard to obfuscate your app that includes the Anyline SDK for Android, you need to add an exception for the Anyline SDK in your Proguard config file.

Turn on Proguard for release builds
android {
    //...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

To add an exception for the Anyline SDK, you have to add the following lines to your proguard config file.

In case you used the configuration from the example above, your proguard file is called proguard-rules.pro.

The proguard exceptions for the Anyline SDK
-keep class at.nineyards.anyline.** { *; }
-dontwarn at.nineyards.anyline.**
-keep class org.opencv.** { *; }
-dontwarn org.opencv.**

How to Split APK into Several Smaller Ones

Tip

This is outdated as Anyline only ships with two ABIS (x86 and armv7) by default now and there has been a new method introduced recently at Android APK Splits.

It is possible to split your app into multiple APKs and upload them all to the Play Store. The Play Store will pick the correct APK to download.

This is currently not recommended by Google unless the APK is bigger than 50MB. (see: Google developer page on multiple APKs).

The following can be added in the android section of the build.gradle to generate a separate APK for every supported CPU architecture (ABI).

    //at the end of the android section
productFlavors {
    x86 {
        flavorDimension "abi"
        ndkConfig.abiFilters "x86"
        // this is the flavor part of the version code.
        // It must be higher than the arm one for devices supporting
        // both, as x86 is preferred.
        versionCode = 4
    }
    armv7 {
        flavorDimension "abi"
        ndkConfig.abiFilters "armeabi-v7a"
        versionCode = 3
    }

    arm {
        flavorDimension "abi"
        ndkConfig.abiFilters "armeabi"
        versionCode = 2
    }

    mips {
        flavorDimension "abi"
        ndkConfig.abiFilters "mips"
        versionCode = 1
    }
    fat {
        flavorDimension "abi"
        // fat binary, lowest version code to be
        // the last option
        versionCode = 0
    }

    // make per-variant version code
    applicationVariants.all { variant ->
        // get the version code of each flavor
        def abiVersion = variant.productFlavors.get(0).versionCode

        // set the composite code
        variant.mergedFlavor.versionCode = defaultConfig.versionCode * 10 + abiVersion
    }
}

This example will generate 5 APKs. 4 for every supported ABI and 1 big APK with everything. The APKs need to have a different versions, for the Play Store to be able to deliver the correct one. The Play Store chooses the highest compatible version so armeabi-v7a must be higher than armeabi and x86 must be higher than armeabi-v7a (because x86 devices can usually also use armeabi libraries).

The versioning in this example is a bit different than the one suggested on different places like this site from Intel. Because it is not optimal that an old x86 version has a higher number than any other newer version, this could be problematic if you change from multiple APKs to a single APK later on.

The example shows one possibility, you can also use 3 APKs: x86, armeabi-v7a, and one with mips + armeabi.

The splitting has advantages during development, because you can chose the product flavor you want to use and that reduces the build and upload time a bit.

Merge conflict of Manifests

If you encounter a merge-conflict in your AndroidManifest because you added the com.google.android.gms.vision.DEPENDENCIES you may want to use the replace functionality.

Have a look at the official Android Developer Documentation for details.

However, you should keep the barcode value, if you want to use the native barcode scanner. So for example, if you want to use face and barcode do the following in your AndroidManifest:

Defining Vision Dependency in the AndroidManifest.xml
 <meta-data  tools:node="replace"
    android:name="com.google.android.gms.vision.DEPENDENCIES"
    android:value="barcode,face"/>

Error: duplicate files during packaging of APK

The Anyline SDK ships with its own version of the GNU STL library. This might cause a conflict with other included libraries, for example React Native’s com.facebook.react:react-native.

To solve this issue, add the following lines to your app/build.gradle

android.packagingOptions {
    pickFirst('lib/*/libgnustl_shared.so')
}

Note

This does not only apply to libgnustl_shared but to any library that is detected as a duplicate