SDK

Adding the SDK to your project

To add our SDK to your own application, you should add it to the build process of your application. Currently we only support doing this using Gradle.
Adding the SDK to your gradle build file consist of two steps:

Adding the repository

To add the repository to your project, open the settings.gradle file in your application.
Edit the repositories part of your settings.gradle, so that it contains the following:

                
maven {
    credentials {
        username 'ExampleUsername'
        password 'ExamplePassword'
    }
    url "https://custom-ocr.klippa.com/sdk/android/maven"
}
maven { url "https://jitpack.io" }
                
            

We add jitpack.io because some of our dependencies are hosted there. If your settings.gradle already contains jitpack.io, don't add it.
The full repositories section of your settings.gradle might look like this now:

                
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
    repositories {
        google()
        mavenCentral()
        maven {
            credentials {
                username 'ExampleUsername'
                password 'ExamplePassword'
            }
            url "https://custom-ocr.klippa.com/sdk/android/maven"
        }
        maven { url "https://jitpack.io" }
    }
}
                
            

Adding the dependency

Edit the dependencies part of your build.gradle, so that it contains the following:

                
implementation 'com.klippa:utility_meter_scanner:0.0.2'
                
            

The full dependencies section of your build.gradle might look like this now:

                
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    implementation 'com.klippa:utility_meter_scanner:0.0.2'
}
                
            

When you build your app now, it should download our library as dependency, and with that all dependencies of our library.

Starting the Utility Meter Scanner

To start the scanner, import our package:

                        
// Add to the top of your file
import com.klippa.utilitymeterscanner.KlippaUtilityMeterScanner
                        
                    
                        
// Add to the top of your file
import com.klippa.utilitymeterscanner.KlippaUtilityMeterScanner;
                        
                    

Then start our scanner Activity from your own Activity:

                        
// We use this constant to keep track of our activity request.
companion object {
    const val KLIPPA_UTILITY_SCANNER_REQUEST_CODE = 1
}

private fun startKlippaUtilityMeterScanner() {
    // Add license
    KlippaUtilityMeterScanner.license = "replace-with-received-license"

    // Launch the KlippaUtilityMeterScanner
    val klippaUtilityMeterScanner = Intent(this, KlippaUtilityMeterScanner::class.java)
    startActivityForResult(klippaUtilityMeterScanner, this.KLIPPA_UTILITY_SCANNER_REQUEST_CODE)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == this.KLIPPA_UTILITY_SCANNER_REQUEST_CODE && resultCode == RESULT_OK) {
        val receivedData = data ?: return
        val extras = receivedData.extras ?: return

        if (extras.containsKey(KlippaUtilityMeterScanner.EXTRACTED_ITEMS)) {
            val extractedItems: ArrayList<ExtractedGroupOfItems> = extras.getParcelableArrayList<ExtractedGroupOfItems>(KlippaUtilityMeterScanner.EXTRACTED_ITEMS) as ArrayList<ExtractedGroupOfItems>
            Toast.makeText(this, "Result was " + extractedItems.size + " items", Toast.LENGTH_LONG).show()
        }

    } else if (requestCode == this.KLIPPA_UTILITY_SCANNER_REQUEST_CODE && resultCode == RESULT_CANCELED) {
        var error: String? = null
        if (data != null) {
            error = data.getStringExtra(KlippaUtilityMeterScanner.ERROR)
        }
        if (error != null) {
            Toast.makeText(this, "Scanner was canceled with error: $error", Toast.LENGTH_LONG).show()
        } else {
            Toast.makeText(this, "Scanner was canceled", Toast.LENGTH_LONG).show()
        }
    } else {
        super.onActivityResult(requestCode, resultCode, data)
    }
}
                        
                    
                        
// We use this constant to keep track of our activity request.
public static int KLIPPA_UTILITY_SCANNER_REQUEST_CODE = 1;

private void startKlippaUtilityMeterScanner() {
    // Add license
    KlippaUtilityMeterScanner.Setup.setLicense("replace-with-received-license");

    // Launch the Klippa Scanner
    Intent klippaUtilityMeterScanner = new Intent(this, KlippaUtilityMeterScanner.class);
    startActivityForResult(klippaUtilityMeterScanner, KLIPPA_UTILITY_SCANNER_REQUEST_CODE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == KLIPPA_UTILITY_SCANNER_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        /* Get the ArrayList of extracted items */
        if (data != null) {
            if (data.hasExtra(KlippaUtilityMeterScanner.EXTRACTED_ITEMS)) {
                ArrayList<ExtractedGroupOfItems> items = data.getParcelableArrayListExtra(KlippaUtilityMeterScanner.EXTRACTED_ITEMS);
                Toast.makeText(this, "Result was " + items.size() + " items", Toast.LENGTH_LONG).show();
            }
        }
    } else if (requestCode == KLIPPA_UTILITY_SCANNER_REQUEST_CODE && resultCode == Activity.RESULT_CANCELED) {
        String error = null;
        if (data != null) {
            error = data.getStringExtra(KlippaUtilityMeterScanner.ERROR);
        }
        if (error != null) {
            Toast.makeText(this, "Scanner was canceled with error: " + error, Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(this, "Scanner was canceled", Toast.LENGTH_LONG).show();
        }
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}
                        
                    

Customizing the utility meter scanner

The SDK has a few customizing settings, the following methods are available:

                        
// Set object detection sensitivity
KlippaUtilityMeterScanner.model.threshold = 0.5f

// Which values can be edited during review screen.
KlippaUtilityMeterScanner.extractionModel.canEditKeys = arrayListOf("QR-Code", "Barcode", "Value")

// Whether to show the icon to enable "multi-document-mode"
KlippaUtilityMeterScanner.menu.allowMultiDocumentsMode = true

// Whether the "multi-document-mode" should be enabled by default.
KlippaUtilityMeterScanner.menu.isMultiDocumentsModeEnabled = true

// Whether the crop mode (object detection) should be enabled by default.
KlippaUtilityMeterScanner.menu.isCropEnabled = true

// If you would like to enable automatic capturing of images.
KlippaUtilityMeterScanner.menu.allowTimer = true
KlippaUtilityMeterScanner.menu.isTimerEnabled = true
KlippaUtilityMeterScanner.durations.timerDuration = 0.5

// Ability to disable/hide the shutter button.
KlippaUtilityMeterScanner.shutterButton.allowShutterButton = true
KlippaUtilityMeterScanner.shutterButton.hideShutterButton = false

// Define the max resolution of the output file. It’s possible to set only one of these values. We will make sure the picture fits in the given resolution. We will also keep the aspect ratio of the image.
KlippaUtilityMeterScanner.images.resolutionMaxWidth = 1920
KlippaUtilityMeterScanner.images.resolutionMaxHeight = 1080

// Set the output quality (between 0-100) of the jpg encoder. Default is 100.
KlippaUtilityMeterScanner.images.outputQuality = 95

// Where to put the image results.
KlippaUtilityMeterScanner.images.outputDirectory = "/sdcard/utilitymeterscanner"

// The filename to use for the output images, supports replacement tokens %dateTime% and %randomUUID%.
KlippaUtilityMeterScanner.images.outputFileName = "KlippaUtilityMeterScannerExample-%dateTime%-%randomUUID%"

// To limit the amount of images that can be taken.
KlippaUtilityMeterScanner.images.imageLimit = 5

// The threshold sensitive the motion detection is. (lower value is higher sensitivity, default 50).
KlippaUtilityMeterScanner.images.imageMovingSensitivity = 50

// To add extra horizontal and / or vertical padding to the cropped meter image.
KlippaUtilityMeterScanner.images.cropPadding = Size(100, 100)

// The warning message when someone should move closer to a meter, should be a string.
KlippaUtilityMeterScanner.messages.moveCloserMessage = "Move closer to the meter"

// The warning message when the camera preview has to much motion to be able to automatically take a photo.
KlippaUtilityMeterScanner.messages.imageMovingMessage = "Camera is moving too much"

// The message to display when the limit has been reached.
KlippaUtilityMeterScanner.messages.imageLimitReached = "You have reached the limit"

// After capture, show a checkmark preview with this success message, instead of a preview of the image.
KlippaUtilityMeterScanner.messages.successMessage = "Success"
KlippaUtilityMeterScanner.durations.successPreviewDuration = 0.4

// What the default color conversion will be (grayscale, original).
KlippaUtilityMeterScanner.colors.defaultColor = KlippaUtilityMeterScanner.DefaultColor.ORIGINAL

// The amount of seconds the preview should be visible for, should be a float.
KlippaUtilityMeterScanner.durations.previewDuration = 1.5

// Whether to save the images to the gallery
KlippaUtilityMeterScanner.storeImagesToGallery = false
                        
                    
                        
// Set object detection sensitivity
KlippaUtilityMeterScanner.Setup.getModel().setThreshold(0.5f);

// Which values can be edited during review screen.
KlippaUtilityMeterScanner.Setup.getExtractionModel().setCanEditKeys(Arrays.asList("QR-Code", "Barcode", "Value"));

// Whether to show the icon to enable "multi-document-mode"
KlippaUtilityMeterScanner.Setup.getMenu().setAllowMultiDocumentsMode(true);

// Whether the "multi-document-mode" should be enabled by default.
KlippaUtilityMeterScanner.Setup.getMenu().setMultiDocumentsModeEnabled(true);

// Whether the crop mode (object detection) should be enabled by default.
KlippaUtilityMeterScanner.Setup.getMenu().setCropEnabled(true);

// If you would like to enable automatic capturing of images.
KlippaUtilityMeterScanner.Setup.getMenu().setAllowTimer(true);
KlippaUtilityMeterScanner.Setup.getMenu().setTimerEnabled(true);
KlippaUtilityMeterScanner.Setup.getDurations().setTimerDuration(0.5);

// Ability to disable/hide the shutter button.
KlippaUtilityMeterScanner.Setup.getShutterButton().setAllowShutterButton(true);
KlippaUtilityMeterScanner.Setup.getShutterButton().setHideShutterButton(false);

// Define the max resolution of the output file. It’s possible to set only one of these values. We will make sure the picture fits in the given resolution. We will also keep the aspect ratio of the image.
KlippaUtilityMeterScanner.Setup.getImages().setResolutionMaxWidth(1920);
KlippaUtilityMeterScanner.Setup.getImages().setResolutionMaxHeight(1080);

// Set the output quality (between 0-100) of the jpg encoder. Default is 100.
KlippaUtilityMeterScanner.Setup.getImages().setOutputQuality(95);

// Where to put the image results.
KlippaUtilityMeterScanner.Setup.getImages().setOutputDirectory("/sdcard/utilitymeterscanner");

// The filename to use for the output images, supports replacement tokens %dateTime% and %randomUUID%.
KlippaUtilityMeterScanner.Setup.getImages().setOutputFileName("KlippaUtilityMeterScannerExample-%dateTime%-%randomUUID%");

// To limit the amount of images that can be taken.
KlippaUtilityMeterScanner.Setup.getImages().setImageLimit(5);

// The threshold sensitive the motion detection is. (lower value is higher sensitivity, default 50).
KlippaUtilityMeterScanner.Setup.getImages().setImageMovingSensitivity(50);

// To add extra horizontal and / or vertical padding to the cropped meter image.
KlippaUtilityMeterScanner.Setup.getImages().setCropPadding(new Size(100, 100));

// The warning message when someone should move closer to a meter, should be a string.
KlippaUtilityMeterScanner.Setup.getMessages().setMoveCloserMessage("Move closer to the meter");

// The warning message when the camera preview has to much motion to be able to automatically take a photo.
KlippaUtilityMeterScanner.Setup.getMessages().setImageMovingMessage("Camera is moving too much");

// The message to display when the limit has been reached.
KlippaUtilityMeterScanner.Setup.getMessages().setImageLimitReached("You have reached the limit");

// After capture, show a checkmark preview with this success message, instead of a preview of the image.
KlippaUtilityMeterScanner.Setup.getMessages().setSuccessMessage("Success");
KlippaUtilityMeterScanner.Setup.getDurations().setSuccessPreviewDuration(0.4);

// What the default color conversion will be (grayscale, original).
KlippaUtilityMeterScanner.Setup.getColors().setDefaultColor(KlippaUtilityMeterScanner.DefaultColor.ORIGINAL);

// The amount of seconds the preview should be visible for, should be a float.
KlippaUtilityMeterScanner.Setup.getDurations().setPreviewDuration(1.5);

// Whether to save the images to the gallery
KlippaUtilityMeterScanner.Setup.setStoreImagesToGallery(true);
                        
                    

How to change the colors of the scanner?

Add or edit the file android/app/src/main/res/values/colors.xml, add the following:

                    
<?xml version="1.0" encoding="utf-8"?>
<resources>
    	<color name="klippa_scanner_sdk_color_Primary">#2dc36a</color>
    	<color name="klippa_scanner_sdk_color_PrimaryDark">#308D53</color>
    	<color name="klippa_scanner_sdk_color_Accent">#2dc36a</color>
    	<color name="klippa_scanner_sdk_color_Overlay">#2dc36a</color>
    	<color name="klippa_scanner_sdk_color_Warning">#BFFF0000</color>
    	<color name="klippa_scanner_sdk_color_IconDisabledColor">#80FFFFFF</color>
    	<color name="klippa_scanner_sdk_color_IconEnabledColor">#FFFFFFFF</color>
    	<color name="klippa_scanner_sdk_color_ReviewIconColor">#FFFFFFFF</color>
</resources>
                    
                

APK size / APK splitting

Because our SDK includes OpenCV, which includes native code which in turn generates a native library (.so file) for every architecture, the APK size can increase quite a bit by using our SDK.
To minimize the impact of the SDK on your app size, you can enable APK splitting in your build.grade, like this:

                
android {
    // Other options
    splits {
       abi {
           enable true
           universalApk false
           reset()
           include 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
       }
    }
    // Other options
}
                
            

In our example app (which contains not much more than the SDK), this resulted in the following APK files:

  • arm64-v8a.apk: 8.8MB
  • armeabi-v7a.apk: 7.8MB
  • x86_64.apk: 23.4MB
  • x86.apk: 16.5MB

While creating a single APK file creates a file that is 41MB, since most phones are ARM, on average APK splitting will result in a 32.7MB smaller APK.

Please note that all APK files should be signed and uploaded to the Play Store to get a release that works on all devices.

JavaDoc

The JavaDoc of the latest version is available for download here.

Versions

The following versions are available:

Version Gradle dependency JavaDoc
0.0.2 implementation 'com.klippa:utility_meter_scanner:0.0.2' Download
0.0.1 implementation 'com.klippa:utility_meter_scanner:0.0.1' Download

Changelog

0.0.1

  • First release of Utility Meter Scanner SDK