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 gradle file, open the build.gradle file in your application. Make sure you use the build.grade of your project, not the top-level one.
Edit the repositories part of your build.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 build.gradle already contains jitpack.io, don't add it.
The full repositories section of your build.gradle might look like this now:

                
repositories {
    jcenter()
    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:scanner:2.1.3'
                
            

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:scanner:2.1.3'
}
                
            

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

Starting the scanner

To start the scanner, import our package:

                
// Add to the top of your file
import com.klippa.scanner.KlippaScanner;
                
            

Then start our scanner Activity from your own Activity:

                
// We use this constant to keep track of our activity request.
public static int KLIPPA_SCANNER_REQUEST_CODE = 1;

private void startKlippaScanner() {
    // Launch the Klippa Scanner
    Intent klippaScannerIntent = new Intent(this, KlippaScanner.class);
    klippaScannerIntent.putExtra(KlippaScanner.LICENSE, "replace-with-received-license");
    startActivityForResult(klippaScannerIntent, KLIPPA_SCANNER_REQUEST_CODE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == KLIPPA_SCANNER_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        // Get the ArrayList of scanned images
        ArrayList<Image> images = data.getParcelableArrayListExtra(KlippaScanner.IMAGES);
        for (Image image : images) {
            // Use the result image.
            Log.i(TAG, "Got image: " + image.toString());
            Log.i(TAG, "Image path: " + image.getFilePath());

            // Get image as Java File object.
            File imageFile = image.getFile();
        }
    } else if (requestCode == KLIPPA_SCANNER_REQUEST_CODE && resultCode == Activity.RESULT_CANCELED) {
        if (data.hasExtra(KlippaScanner.ERROR) && data.getStringExtra(KlippaScanner.ERROR) != null) {
            String error = data.getStringExtra(KlippaScanner.ERROR);
            Log.e(TAG, "Scanner was canceled with error: " + error);
        } else {
            Log.e(TAG, "Scanner was canceled");
        }
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}
                
            

Customizing the scanner

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

                
// Whether to show the icon to enable "multi-document-mode"
klippaScannerIntent.putExtra(KlippaScanner.ALLOW_CREATE_MULTIPLE_RECEIPTS, true);

// Whether the "multi-document-mode" should be enabled by default.
klippaScannerIntent.putExtra(KlippaScanner.DEFAULT_CREATE_MULTIPLE_RECEIPTS, true);

// Ability to disable/hide the shutter button (only works when a model is supplied as well).
klippaScannerIntent.putExtra(KlippaScanner.ALLOW_SHUTTER_BUTTON, false);
klippaScannerIntent.putExtra(KlippaScanner.HIDE_SHUTTER_BUTTON, true);

// Whether the crop mode (auto edge detection) should be enabled by default.
klippaScannerIntent.putExtra(KlippaScanner.DEFAULT_CROP, true);

// 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. Default is max resolution of camera.
klippaScannerIntent.putExtra(KlippaScanner.RESOLUTION_MAX_WIDTH, 1920);
klippaScannerIntent.putExtra(KlippaScanner.RESOLUTION_MAX_HEIGHT, 1080);

// Set the output quality (between 0-100) of the jpg encoder. Default is 100.
klippaScannerIntent.putExtra(KlippaScanner.OUTPUT_QUALITY, 95);

// The warning message when someone should move closer to a document, should be a string.
klippaScannerIntent.putExtra(KlippaScanner.MOVE_CLOSER_MESSAGE, "Move closer to the document");

// The warning message when the camera preview has to much motion to be able to automatically take a photo.
klippaScannerIntent.putExtra(KlippaScanner.IMAGE_MOVING_MESSAGE, "Camera is moving too much");

// What the default color conversion will be (grayscale, original).
klippaScannerIntent.putExtra(KlippaScanner.DEFAULT_COLOR, "original");

// Where to put the image results.
klippaScannerIntent.putExtra(KlippaScanner.OUTPUT_DIRECTORY, "/sdcard/scanner");

// The filename to use for the output images, supports replacement tokens %dateTime% and %randomUUID%.
klippaScannerIntent.putExtra(KlippaScanner.OUTPUT_FILENAME, "KlippaScannerExample-%dateTime%-%randomUUID%");

// To limit the amount of images that can be taken.
klippaScannerIntent.putExtra(KlippaScanner.IMAGE_LIMIT, 5);

// The message to display when the limit has been reached.
klippaScannerIntent.putExtra(KlippaScanner.IMAGE_LIMIT_REACHED_MESSAGE, "You have reached the limit");

// The threshold sensitive the motion detection is. (lower value is higher sensitivity, default 50).
klippaScannerIntent.putExtra(KlippaScanner.IMAGE_MOVING_SENSITIVITY, 50);

// The primary color of the interface, should be a resource. This is used for the app bar.
klippaScannerIntent.putExtra(KlippaScanner.PRIMARY_COLOR, getResources().getColor(android.R.color.holo_orange_light));

// The primary dark color of the interface, should be a resource. This is used for the status bar.
klippaScannerIntent.putExtra(KlippaScanner.PRIMARY_DARK_COLOR, getResources().getColor(android.R.color.holo_purple));

// The accent color of the interface, should be a resource. This is used for control elements.
klippaScannerIntent.putExtra(KlippaScanner.ACCENT_COLOR, getResources().getColor(android.R.color.holo_red_dark));

// The overlay color (when using document detection), should be a resource.
klippaScannerIntent.putExtra(KlippaScanner.OVERLAY_COLOR, getResources().getColor(android.R.color.holo_blue_bright));

// The color of the background of the warning message, should be a resource.
klippaScannerIntent.putExtra(KlippaScanner.WARNING_COLOR, getResources().getColor(android.R.color.holo_red_light));

// The color of the menu icons when they are enabled, should be a resource.
klippaScannerIntent.putExtra(KlippaScanner.ICON_ENABLED_COLOR, getResources().getColor(android.R.color.holo_blue_bright));

// The color of the menu icons when they are disabled, should be a resource.
klippaScannerIntent.putExtra(KlippaScanner.ICON_DISABLED_COLOR, getResources().getColor(android.R.color.holo_red_dark));

// The color of the menu icons of the screen where you can review/edit the images, should be a resource.
klippaScannerIntent.putExtra(KlippaScanner.REVIEW_ICON, getResources().getColor(android.R.color.holo_blue_bright));

// The amount of seconds the preview should be visible for, should be a float.
klippaScannerIntent.putExtra(KlippaScanner.PREVIEW_DURATION, 1.5);

// If you would like to use a custom model for object detection. Model + labels file should be packaged in your bundle.
klippaScannerIntent.putExtra(KlippaScanner.MODEL_NAME, "model");
klippaScannerIntent.putExtra(KlippaScanner.MODEL_LABELS_NAME, "labelmap");

// If you would like to enable automatic capturing of images.
klippaScannerIntent.putExtra(KlippaScanner.TIMER_ENABLED, true);
klippaScannerIntent.putExtra(KlippaScanner.TIMER_DURATION, 0.4);

// To add extra horizontal and / or vertical padding to the cropped image.
klippaScannerIntent.putExtra(KlippaScanner.CROP_PADDING_WIDTH, 100);
klippaScannerIntent.putExtra(KlippaScanner.CROP_PADDING_HEIGHT, 100);

// After capture, show a checkmark preview with this success message, instead of a preview of the image.
klippaScannerIntent.putExtra(KlippaScanner.SUCCESS_MESSAGE, "Success");
klippaScannerIntent.putExtra(KlippaScanner.SUCCESS_PREVIEW_DURATION, 0.4);

// The options above also return the value that the option has when the activity finished:
boolean createMultipleReceipts = data.getBooleanExtra(KlippaScanner.CREATE_MULTIPLE_RECEIPTS, false);
boolean cropperWasEnabled = data.getBooleanExtra(KlippaScanner.CROP, false);
String color = data.getStringExtra(KlippaScanner.COLOR);
                
            

Calling the OCR API

It's possible to use the results of the scanner with the OCR API.

To use it, generate a Public API key with our API through your own backend, this makes sure your API key won't be leaked and/or abused.

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

                
implementation 'com.klippa:ocrapi:0.0.5'
                
            

You then have the possibility to call the OCR API client and run it on the images of the scanner.
The following methods runs the OCR on every image and then shows the total amount in a Toast.

                

// Place this at the top of class.
import com.klippa.ocrapi.ApiCallback;
import com.klippa.ocrapi.ApiClient;
import com.klippa.ocrapi.ApiException;
import com.klippa.ocrapi.Configuration;
import com.klippa.ocrapi.api.ParsingApi;
import com.klippa.ocrapi.auth.ApiKeyAuth;
import com.klippa.ocrapi.model.ReceiptBody;
import com.klippa.ocrapi.model.Receipt;
import com.klippa.scanner.KlippaScanner;
import com.klippa.scanner.object.Image;

// Place this inside your Activity class.
private int ocrResults = 0;
private double totalAmount = 0;

// Call this method with the result images for the scanner.
private void processImageOCR(ArrayList&lt;Image&gt; images) {
    // Generate a public API key for your customer here.
    String ocrAPIPublicKey = "";

    Activity context = this;
    ocrResults = 0;
    totalAmount = 0;

    ApiClient apiClient = Configuration.getDefaultApiClient();
    ApiKeyAuth authentication = (ApiKeyAuth) apiClient.getAuthentication("APIPublicKeyHeader");
    authentication.setApiKey(ocrAPIPublicKey);
    ParsingApi parsingAPI = new ParsingApi(apiClient);

    for (int i=0;i&lt;images.size();i++) {
        final Image image = images.get(i);
        try {
            parsingAPI.parseDocumentAsync(image.getFile(), null, null, null, null, null, null, new ApiCallback&lt;ReceiptBody&gt;() {
                @Override
                public void onFailure(ApiException e, int statusCode, Map&lt;String, List&lt;String&gt;&gt; responseHeaders) {
                    Log.e(TAG, e.toString());
                    // @todo: handle error.
                    ocrResults++;
                    if (ocrResults == images.size()) {
                        showTotalAmountToast(context);
                    }
                }

                @Override
                public void onSuccess(ReceiptBody result, int statusCode, Map&lt;String, List&lt;String&gt;&gt; responseHeaders) {
                    Receipt receipt = result.getData();
                    if (receipt != null) {
                        Log.i(TAG, "Got receipt: " + receipt.toString());
                        totalAmount += receipt.getAmount();
                    } else {
                        Log.i(TAG, "Got no receipt");
                    }

                    ocrResults++;
                    if (ocrResults == images.size()) {
                        showTotalAmountToast(context);
                    }
                }

                @Override
                public void onUploadProgress(long bytesWritten, long contentLength, boolean done) {
                }

                @Override
                public void onDownloadProgress(long bytesRead, long contentLength, boolean done) {
                }
            });
        } catch (ApiException e) {
            e.printStackTrace();
        }
    }
}

private void showTotalAmountToast(Activity context) {
    context.runOnUiThread(new Runnable() {
        @Override
        public void run() {
            NumberFormat formatter = new DecimalFormat("#0.00");
            Toast.makeText(context, "Total amount: " + formatter.format(totalAmount / 100.0), Toast.LENGTH_LONG).show();
        }
    });
}

                
            

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
2.1.3 implementation 'com.klippa:scanner:2.1.3' Download
2.1.2 implementation 'com.klippa:scanner:2.1.2' Download
2.1.1 implementation 'com.klippa:scanner:2.1.1' Download
2.1.0 implementation 'com.klippa:scanner:2.1.0' Download
2.0.8 implementation 'com.klippa:scanner:2.0.8' Download
2.0.7 implementation 'com.klippa:scanner:2.0.7' Download
2.0.6 implementation 'com.klippa:scanner:2.0.6' Download
2.0.5 implementation 'com.klippa:scanner:2.0.5' Download
2.0.4 implementation 'com.klippa:scanner:2.0.4' Download
2.0.3 implementation 'com.klippa:scanner:2.0.3' Download
2.0.2 implementation 'com.klippa:scanner:2.0.2' Download
2.0.1 implementation 'com.klippa:scanner:2.0.1' Download
2.0.0 implementation 'com.klippa:scanner:2.0.0' Download
1.3.7 implementation 'com.klippa:scanner:1.3.7' Download
1.3.6 implementation 'com.klippa:scanner:1.3.6' Download
1.3.5 implementation 'com.klippa:scanner:1.3.5' Download
1.3.5-rc implementation 'com.klippa:scanner:1.3.5-rc' Download
1.3.4 implementation 'com.klippa:scanner:1.3.4' Download
1.3.3 implementation 'com.klippa:scanner:1.3.3' Download
1.3.2 implementation 'com.klippa:scanner:1.3.2' Download
1.3.0 implementation 'com.klippa:scanner:1.3.0' Download
1.2.0 implementation 'com.klippa:scanner:1.2.0' Download
1.1.6 implementation 'com.klippa:scanner:1.1.6' Download
1.1.5 implementation 'com.klippa:scanner:1.1.5' Download
1.1.4 implementation 'com.klippa:scanner:1.1.4' Download
1.1.3 implementation 'com.klippa:scanner:1.1.3' Download
1.1.2 implementation 'com.klippa:scanner:1.1.2' Download
1.1.1 implementation 'com.klippa:scanner:1.1.1' Download
1.0.9 implementation 'com.klippa:scanner:1.0.9' Download

Changelog

2.1.3

Fixed

  • Issue where SDK could stop responding when tapping the trash icon.

2.1.2

Fixed

  • Issue where FloatingActionButton size was not being set correctly.

2.1.1

Fixed

  • Issue where AlertDialog theme was not set correctly.

2.1.0

Added

  • Ability to automatically go to the review screen once the image limit has been reached by setting shouldGoToReviewScreenWhenImageLimitReached (default false).
  • Ability to hide top navigation bar buttons by setting: userCanRotateImage, userCanCropManually, userCanChangeColorSetting accordingly (each default true).
  • Deleting an image now prompts an alert to confirm your action. Texts can be set with: klippa_delete_button_text, klippa_retake_button_text, klippa_cancel_button_text in your strings.xml.
  • Ability to cancel the scanner completely on the review screen by pressing cancel, texts can be set with: klippa_cancel_delete_images, klippa_cancel_confirmation in your strings.xml.
  • Users can now complete scan process from the review screen by pressing “>” button.
  • Support for longer receipts.

Changed

  • Image quality is now higher.
  • Default max image resolutions from 1000 to 3000.

2.0.8

Fixed

  • Issue where users could take more pictures than the specified image limit.

Changed

  • Now imageMovingMessage only shows when we have detected an object.

2.0.7

Added

  • Ability to disable or enable if the taken photos should be stored to the gallery by setting STORE_IMAGES_TO_GALLERY.
  • Support for Android API Level 31.

2.0.6

Added

  • Ability to hide timer button by setting ALLOW_TIMER
  • Returns state of timer button in intent with TIMER_ENABLED

2.0.5

Fixed

  • Issue where OUTPUT_DIRECTORY would not be set correctly.
  • Issue with backwards compatibility with DEFAULT_COLOR.

Changed

  • Images are now aligned with iOS KlippaScanner SDK.

2.0.4

Fixed

  • Issue where license error was not providing correct error in some cases.

2.0.3

Added

  • Ability to disable/hide the shutter button. ALLOW_SHUTTER_BUTTON and HIDE_SHUTTER_BUTTON
  • Ability to adjust the sensitivity of the motion detection, lower values give higher sensitivity. IMAGE_MOVING_SENSITIVITY

2.0.2

Changed

  • Readded needed variables for React Native.

2.0.1

Fixed

  • Issue where some colors would not load correctly
  • Issue where sometimes preview would not disappear.

2.0.0

Changed

  • Now completely rewritten in Kotlin.
  • Supports AndroidX
  • Default color scheme is now Klippa colors.

Added

  • Scanner can now be setup with KlippaScanner.Setup.*, setting up with Intent still works like before. Please see this for more information.
  • Short image preview after taking picture by setting a duration for the preview with: KlippaScanner.PREVIEW_DURATION.
  • Icon colors can now be set with: ICON_ENABLED_COLOR, ICON_DISABLED_COLOR, REVIEW_ICON

1.3.7

Fixed

  • Issue where autofocus was not being called when needed.

Added

  • Functionality when timer is enabled scanner will not automatically take photos if too much movement is detected.
  • Optionally add user indication when movement is detected. KlippaScanner.IMAGE_MOVING_MESSAGE

1.3.6

Fixed

  • Fixed a bug where sorting method could throw exception

1.3.5

Changed

  • Use org.tensorflow:tensorflow-lite version 2.4.0 instead of 0.0.0-nightly
  • Use org.tensorflow:tensorflow-lite-metadata version 0.1.0 instead of 0.0.0-nightly

Fixed

  • The SDK mistakenly included a drawable resources for the launcher icon

1.3.3 and 1.3.4

Fixed

  • Fixed a bug when parsing integers to doubles for SUCCESS_PREVIEW_DURATION and TIMER_DURATION

1.3.2

Changed

  • The minSdkVersion has been changed to 21.
  • Added the ability to recognize custom objects (instead of documents) using a trained TensorFlow Lite model. Klippa can train these custom models for whatever you like to capture using the KlippaScanner SDK.
  • Model can be supplied by using the options KlippaScanner.MODEL_NAME and KlippaScanner.MODEL_LABELS_NAME
  • A timer has been added, to automatically capture recognized objects after a certain time has elapsed: KlippaScanner.TIMER_ENABLED and KlippaScanner.TIMER_DURATION
  • A success message with checkmark icon can be shown instead of a preview of the captured image using KlippaScanner.SUCCESS_MESSAGE and KlippaScanner.SUCCESS_PREVIEW_DURATION
  • When capturing using a model, padding can be added to the cropped area using KlippaScanner.CROP_PADDING_WIDTH and KlippaScanner.CROP_PADDING_HEIGHT

1.2.0

Changed

  • We upgraded our dependency on the Picasso library to version 2.71828.

1.1.6

Fixed

  • Changed minification to include our package name to prevent duplicate classes.

1.1.4

Fixed

  • Fix bug where you could press the next button when you didn’t create any image yet.

Changed

  • The minSdkVersion is now 14.

1.1.3

Fixed

  • Properly center the warning messages.

1.1.2

Added

  • Added option KlippaScanner.OUTPUT_FILENAME to set the output filename. By default it will be a random UUID string. There are 2 replacements available: %randomUUID% and %dateTime%, so you could do something like this: KlippaScanner-%randomUUID%-%dateTime%.
  • Added option KlippaScanner.IMAGE_LIMIT to set a maximum amount of files to be taken. After reaching the maximum the user will be shown a message that they reached the limit. The message is configured using KlippaScanner.IMAGE_LIMIT_REACHED_MESSAGE.

Changed

  • Shutter now directly shows after pressing the button.

1.1.1

Added

  • Added option KlippaScanner.PRIMARY_DARK_COLOR to set the colorPrimaryDark (the color in the status bar)

Changed

  • Color handling is now changed: KlippaScanner.PRIMARY_DARK_COLOR is used for the status bar, KlippaScanner.PRIMARY_COLOR for the app bar and KlippaScanner.ACCENT_COLOR for the controls.

Fixed

  • Give the camera control some more padding for when the status bar is hidden.

1.1.0

Added

  • Added options KlippaScanner.RESOLUTION_MAX_WIDTH and KlippaScanner.RESOLUTION_MAX_HEIGHT to set 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.
  • Added option KlippaScanner.OUTPUT_QUALITY to set the output quality (between 0-100) of the jpg encoder. You can see what the quality level does to the output quality/size here: https://sirv.sirv.com/Examples/example-coast.jpg?q=80 change the last value to see the quality change.

1.0.9

Changed

  • Upgrade dependency versions.

1.0.8

Fixed

  • Restore correct position in image list after deleting an image.

1.0.7

Added

  • Add next/previous arrows.

1.0.6

Fixed

  • Prevent crash when opening details when taking picture.
  • Fix when amount of images goes above 100.
  • Fix flashlight staying on when returning from SDK.