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:scanner:3.1.6'
                
            

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.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    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:3.1.6'
}
                
            

Starting the Klippa Scanner

As of KlippaScanner SDK Version 3.0.0 the implementation has changed. If you are using an 2.1.10 or earlier use the button below to switch to the original documentation.

To start the scanner, import our package:

                                    
// Add to the top of your file
import com.klippa.scanner.KlippaScannerBuilder
import com.klippa.scanner.KlippaScannerListener
import com.klippa.scanner.model.KlippaScannerResult
                                    
                                
                                    
// Add to the top of your file
import com.klippa.scanner.KlippaScannerBuilder;
import com.klippa.scanner.KlippaScannerListener;
import com.klippa.scanner.model.KlippaScannerResult;
                                    
                                

Then start our scanner Activity from your own Activity, be sure that you implemented KlippaScannerListener.

                                    
class MainActivity : AppCompatActivity(), KlippaScannerListener {
    // Call this method from a button press
    private fun startKlippaScanner() {
        // Launch the Klippa Scanner
        val license = "{your-license}"
        KlippaScannerBuilder(this, license)
            .startScanner(this)
    }

    override fun klippaScannerDidFinishScanningWithResult(result: KlippaScannerResult) {
        Log.w("KlippaScanner", "Finished with image count" + result.images.size)
    }

    override fun klippaScannerDidCancel() {
        Log.w("KlippaScanner", "canceled")
    }

    override fun klippaScannerDidFailWithException(exception: Exception) {
        Log.e("KlippaScanner", "Failed with error: " + exception.message)
    }
}
                                    
                                
                
public class MainActivity extends AppCompatActivity implements KlippaScannerListener {
    // Call this method from a button press
    private void startKlippaScanner() {
        // Launch the Klippa Scanner
        String license = "{your-license}";
        new KlippaScannerBuilder(this, license)
                .startScanner(this);
    }

    @Override
    public void klippaScannerDidFinishScanningWithResult(@NonNull KlippaScannerResult klippaScannerResult) {
        Log.w("KlippaScanner", "Finished with image count" + klippaScannerResult.getImages().size());
    }

    @Override
    public void klippaScannerDidCancel() {
        Log.w("KlippaScanner", "canceled");
    }

    @Override
    public void klippaScannerDidFailWithException(@NonNull Exception e) {
        Log.e("KlippaScanner", "Failed with error: " + e.getMessage());
    }
}
                
            
    
// 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();
        }

        // These values are also returned to reflect the button state when the scanner was closed.
        boolean createMultipleReceipts = data.getBooleanExtra(KlippaScanner.CREATE_MULTIPLE_RECEIPTS, false);
        boolean cropperWasEnabled = data.getBooleanExtra(KlippaScanner.CROP, false);
        String color = data.getStringExtra(KlippaScanner.COLOR);

    } 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, each customization can be added to the KlippaScanner(Builder) class using the functions below:

Customizing the colors

Note: when setting the colors the values should be a resource.

                                    
private val klippaColors = KlippaColors(
    // What the default color conversion will be (grayscale, original, enhanced).
    imageColorMode = KlippaImageColor.ORIGINAL,
    // The primary color of the interface. This is used for the app bar.
    primaryColor = android.R.color.holo_blue_bright,
    // The primary dark color of the interface. This is used for the status bar.
    primaryColorDark = android.R.color.holo_orange_dark,
    // The accent color of the interface. This is used for control elements.
    accentColor = android.R.color.holo_red_dark,
    // The overlay color (when using document detection).
    overlayColor = android.R.color.holo_blue_bright,
    // The color of the background of the warning message.
    warningColor = android.R.color.holo_red_light,
    // The color of the menu icons when they are enabled.
    iconDisabledColor = android.R.color.holo_red_dark,
    // The color of the menu icons when they are disabled.
    iconEnabledColor = android.R.color.darker_gray,
    // The color of the menu icons of the screen where you can review/edit the images.
    reviewIconColor = android.R.color.holo_red_dark)

// Call this from a button
private fun startKlippaScanner() {
    // Launch the Klippa Scanner
    val license = "{your-license}"
    KlippaScannerBuilder(this, license)
        .colors(klippaColors)
        .startScanner(this)
}
                                    
                                
        
KlippaColors klippaColors = new KlippaColors(
    // What the default color conversion will be (grayscale, original, enhanced).
    KlippaImageColor.ORIGINAL, // imageColorMode
    // The primary color of the interface. This is used for the app bar.
    android.R.color.holo_blue_bright, // primaryColor
    // The primary dark color of the interface. This is used for the status bar.
    android.R.color.holo_orange_dark, //primaryColorDark
    // The accent color of the interface. This is used for control elements.
    android.R.color.holo_red_dark, // accentColor
    // The overlay color (when using document detection).
    android.R.color.holo_blue_bright, // overlayColor
    // The color of the background of the warning message.
    android.R.color.holo_red_light, // warningColor
    // The color of the menu icons when they are enabled.
    android.R.color.holo_red_dark, // iconEnabledColor
    // The color of the menu icons when they are disabled.
    android.R.color.darker_gray, // iconDisabledColor
    // The color of the menu icons of the screen where you can review/edit the images.
    android.R.color.holo_red_dark // reviewIconColor
    );

// Call this from a button
private void startKlippaScanner() {
// Launch the Klippa Scanner
String license = "{your-license}";
new KlippaScannerBuilder(this, license)
        .colors(klippaColors)
        .startScanner(this);
}
        

                                

Instead of customizing colors through the setup you can also supply them through values. Add or edit the file app/src/main/res/values/colors.xml.

                            
<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>
                                                
                            
    
// 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));

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

Instead of customizing colors through the setup you can also supply them through values. Add or edit the file app/src/main/res/values/colors.xml.

    
<?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>
    

Customizing the messages

                
private val messages = KlippaMessages(
    // The warning message when someone should move closer to a document.
    moveCloserMessage = "Move closer to the document",
    // The message to display when the limit has been reached.
    imageLimitReached = "You have reached the limit",
    // After capture, show a checkmark preview with this success message, instead of a preview of the image.
    successMessage = "Success",
    // The warning message when the camera preview has to much motion to be able to automatically take a photo.
    imageMovingMessage = "Camera is moving too much",
    // The confirmation message shown when the cancel button is pressed on the review screen.
    cancelConfirmationMessage = "Delete photos and exit scanner?",
    // The warning message when device is held in landscape mode.
    orientationWarningMessage = "Hold your phone in portrait mode"
)

// Call this from a button
private fun startKlippaScanner() {
    // Launch the Klippa Scanner
    val license = "{your-license}"
    KlippaScanner(this, license)
        .messages(messages)
        .startScanner(this)
}
                
            
            
KlippaMessages messages = new KlippaMessages(
    // The warning message when someone should move closer to a document.
    "Move closer to the document", // moveCloserMessage
    // The message to display when the limit has been reached.
    "You have reached the limit", // imageLimitReached
    // After capture, show a checkmark preview with this success message, instead of a preview of the image.
    "Success", // successMessage
    // The warning message when the camera preview has to much motion to be able to automatically take a photo.
    "Camera is moving too much", // imageMovingMessage
    // The confirmation message shown when the cancel button is pressed on the review screen.
    "Delete photos and exit scanner?", // cancelConfirmationMessage
    // The warning message when device is held in landscape mode.
    "Hold your phone in portrait mode" // orientationWarningMessage
);

// Call this from a button
private void startKlippaScanner() {
    // Launch the Klippa Scanner
    String license = "{your-license}";
    new KlippaScanner(this, license)
        .messages(messages)
        .startScanner(this);
}
            
        

Instead of customizing messages through the setup you can also supply them through values. Add or edit the file app/src/main/res/values/strings.xml

            
<resources>
    <string name="klippa_zoom_message">Move closer to the document</string>
    <string name="klippa_image_limit_reached">You have reached the image limit</string>
    <string name="klippa_success_message">Success</string>
    <string name="klippa_image_moving_message">Moving too much</string>
    <string name="klippa_orientation_warning_message">Hold your phone in portrait mode</string>
    <string name="klippa_cancel_confirmation">Delete photos and exit scanner?</string>
</resources>
            
        
    
// 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");

// Users receive an orientation warning when device is not held in portrait mode, text can be set with:
klippaScannerIntent.putExtra(KlippaScanner.ORIENTATION_WARNING_MESSAGE, "Hold your phone in portrait mode");

// Ability to cancel the scanner completely on the review screen by pressing cancel, text can be set with:
klippaScannerIntent.putExtra(KlippaScanner.CANCEL_CONFIRMATION_MESSAGE, "Delete photos and exit scanner?");

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

// After capture, show a checkmark preview with this success message, instead of a preview of the image. (Requires that SUCCESS_PREVIEW_DURATION is also set to a value more than 0.0)
klippaScannerIntent.putExtra(KlippaScanner.SUCCESS_MESSAGE, "Success");
    

Instead of customizing messages through the setup you can also supply them through values. Add or edit the file app/src/main/res/values/strings.xml

            
<resources>
    <string name="klippa_zoom_message">Move closer to the document</string>
    <string name="klippa_image_limit_reached">You have reached the image limit</string>
    <string name="klippa_success_message">Success</string>
    <string name="klippa_image_moving_message">Moving too much</string>
    <string name="klippa_orientation_warning_message">Hold your phone in portrait mode</string>
    <string name="klippa_cancel_confirmation">Delete photos and exit scanner?</string>
</resources>

            
        

Customizing text in buttons

                                    
private val buttonTexts = KlippaButtonTexts(
    // The text inside of the delete button.
    deleteButtonText = "Delete",
    // The text inside of the retake button.
    retakeButtonText = "Retake",
    // The text inside of the cancel button.
    cancelButtonText = "Cancel",
    // The text inside of the cancel confirmation alert, confirming to cancel the scanner without a result.
    cancelAndDeleteImagesButtonText = "Delete photos and exit"
)

// Call this from a button
private fun startKlippaScanner() {
    // Launch the Klippa Scanner
    val license = "{your-license}"
    KlippaScanner(this, license)
        .buttonTexts(buttonTexts)
        .startScanner(this)
}
                                    
                                
                                    
KlippaButtonTexts buttonTexts = new KlippaButtonTexts(
    // The text inside of the delete button.
    "Delete", // deleteButtonText
    // The text inside of the retake button.
    "Retake", // retakeButtonText
    // The text inside of the cancel button.
    "Cancel", // cancelButtonText
    // The text inside of the cancel confirmation alert, confirming to cancel the scanner without a result.
    "Delete photos and exit" // cancelAndDeleteImagesButtonText
);

private void startKlippaScanner() {
    // Launch the Klippa Scanner
    String license = "{your-license}";
    new KlippaScanner(this, license)
        .buttonTexts(buttonTexts)
        .startScanner(this);
}
                                    
                                

Instead of customizing button texts through the setup you can also supply them through values. Add or edit the file app/src/main/res/values/strings.xml

            
<resources>
    <string name="klippa_delete_button_text">Delete Photo</string>
    <string name="klippa_retake_button_text">Retake Photo</string>
    <string name="klippa_cancel_button_text">Cancel</string>
    <string name="klippa_cancel_delete_images">Delete photos and exit</string>
    <string name="klippa_image_color_original">Original</string>
    <string name="klippa_image_color_grayscale">Grayscale</string>
    <string name="klippa_image_color_enhanced">Enhanced</string>
</resources>
            
        
    
// Deleting an image prompts an alert to confirm your action. Texts can be set with:
klippaScannerIntent.putExtra(KlippaScanner.DELETE_BUTTON_TEXT, "Delete");
klippaScannerIntent.putExtra(KlippaScanner.RETAKE_BUTTON_TEXT, "Retake");
klippaScannerIntent.putExtra(KlippaScanner.CANCEL_BUTTON_TEXT, "Cancel");

// The text inside of the cancel confirmation alert, confirming to cancel the scanner without a result.
klippaScannerIntent.putExtra(KlippaScanner.CANCEL_AND_DELETE_IMAGES_BUTTON_TEXT, "Delete photos & exit");
    

Instead of customizing button texts through the setup you can also supply them through values. Add or edit the file app/src/main/res/values/strings.xml

            
<resources>
    <string name="klippa_delete_button_text">Delete Photo</string>
    <string name="klippa_retake_button_text">Retake Photo</string>
    <string name="klippa_cancel_button_text">Cancel</string>
    <string name="klippa_cancel_delete_images">Delete photos and exit</string>
</resources>
            
        

Customizing the menu icons

                                    
private val menu = KlippaMenu(
    // Whether the crop mode (auto edge detection) should be enabled by default.
    isCropEnabled = true,
    // Whether to show the icon to enable "multi-document-mode"
    allowMultiDocumentsMode = true,
    // Whether the "multi-document-mode" should be enabled by default.
    isMultiDocumentsModeEnabled = true,
    // Whether to show the icon to enable the timer.
    allowTimer = true,
    // Whether the timer (auto take photo) should be enabled by default.
    isTimerEnabled = false,
    // Whether to show the rotate icon in the review screen.
    userCanRotateImage = false,
    // Whether to show the crop icon in the review screen.
    userCanCropManually = false,
    // Whether to show the color adjusting icon in the review screen.
    userCanChangeColorSetting = false,
    // Whether to automatically go to the review screen once the image limit has been reached.
    shouldGoToReviewScreenWhenImageLimitReached = false
)

// Call this from a button
private fun startKlippaScanner() {
    // Launch the Klippa Scanner
    val license = "{your-license}"
    KlippaScanner(this, license)
        .menu(menu)
        .startScanner(this)
}
                                    
                                
                                    
KlippaMenu menu = new KlippaMenu(
    // Whether the crop mode (auto edge detection) should be enabled by default.
    true, // isCropEnabled
    // Whether to show the icon to enable "multi-document-mode"
    true, // allowMultiDocumentsMode
    // Whether to show the icon to enable the timer.
    true, // allowTimer
    // Whether the timer (auto take photo) should be enabled by default.
    false, // isTimerEnabled
    // Whether to show the rotate icon in the review screen.
    false, // userCanRotateImage
    // Whether to show the crop icon in the review screen.
    false, // userCanCropManually
    // Whether to show the color adjusting icon in the review screen.
    false, // userCanChangeColorSetting
    // Whether to automatically go to the review screen once the image limit has been reached.
    false // shouldGoToReviewScreenWhenImageLimitReached
);

private void startKlippaScanner() {
    // Launch the Klippa Scanner
    String license = "{your-license}";
    new KlippaScanner(this, license)
        .menu(menu)
        .startScanner(this);
}
                                    
                                
    
// 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);

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

// Ability to hide timer button.
klippaScannerIntent.putExtra(KlippaScanner.ALLOW_TIMER, true);

// Ability to hide top navigation bar buttons inside of image preview screen.
klippaScannerIntent.putExtra(KlippaScanner.USER_CAN_ROTATE_IMAGE, false);
klippaScannerIntent.putExtra(KlippaScanner.USER_CAN_CROP_MANUALLY, false);
klippaScannerIntent.putExtra(KlippaScanner.USER_CAN_CHANGE_COLOR_SETTING, false);

// Whether going to the review screen automatically once the image limit has been reached. Default false.
klippaScannerIntent.putExtra(KlippaScanner.SHOULD_GO_TO_REVIEW_SCREEN_WHEN_IMAGE_LIMIT_REACHED, true);

    

Customizing the image attributes

                                    
private val imageAttributes = KlippaImageAttributes(
    // Define the max resolution of the output file.
    // It’s possible to set only one (width or height max resolution).
    // 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.
    resolutionMaxWidth = 3000,
    resolutionMaxHeight = 3000,
    // Set the output quality (between 0-100) of the jpg encoder. Default is 100.
    outputQuality = 95,
    // Where to put the image results.
    outputDirectory = "/sdcard/scanner",
    // The filename to use for the output images, supports replacement tokens %dateTime% and %randomUUID%.
    outputFileName= "KlippaScannerExample-%dateTime%-%randomUUID%",
    // To limit the amount of images that can be taken.
    imageLimit = 5,
    // To add extra horizontal and / or vertical padding to the cropped image.
    cropPadding = Size(100, 100),
    // The threshold sensitive the motion detection is. (lower value is higher sensitivity, default 50).
    imageMovingSensitivity = 50,
    // Whether to store the taken images to the phones gallery.
    storeImagesToGallery = true
)

// Call this from a button
private fun startKlippaScanner() {
    // Launch the Klippa Scanner
    val license = "{your-license}"
    KlippaScanner(this, license)
        .imageAttributes(imageAttributes)
        .startScanner(this)
}
                                    
                                
                                    
KlippaImageAttributes imageAttributes = new KlippaImageAttributes(
    // Define the max resolution of the output file.
    // It’s possible to set only one (width or height max resolution).
    // 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.
    3000, // resolutionMaxWidth
    3000, // resolutionMaxHeight
    // Set the output quality (between 0-100) of the jpg encoder. Default is 100.
    95, // outputQuality
    // Where to put the image results.
    "/sdcard/scanner", // outputDirectory
    // The filename to use for the output images, supports replacement tokens %dateTime% and %randomUUID%.
    "KlippaScannerExample-%dateTime%-%randomUUID%", // outputFileName
    // To limit the amount of images that can be taken.
    5, // imageLimit
    // To add extra horizontal and / or vertical padding to the cropped image.
    new Size(100, 100), // cropPadding
    // The threshold sensitive the motion detection is. (lower value is higher sensitivity, default 50).
    50, // imageMovingSensitivity
    // Whether to store the taken images to the phones gallery.
    true // storeImagesToGallery
);

private void startKlippaScanner() {
    // Launch the Klippa Scanner
    String license = "{your-license}";
    new KlippaScanner(this, license)
        .imageAttributes(imageAttributes)
        .startScanner(this);
}
                                    
                                
    
// 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);

// 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);

// Whether the camera automatically saves the images to the gallery. Default true.
klippaScannerIntent.putExtra(KlippaScanner.STORE_IMAGES_TO_GALLERY, false);

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

// 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);
    

Customizing the shutter button

                
private val shutterButton = KlippaShutterButton(
    // Whether to allow the shutter button. (If false the shutter button is greyed out)
    allowShutterButton = true,
    // Whether to hide the shutter button. (Only works if allowShutterButton is false)
    hideShutterButton = false
)

// Call this from a button
private fun startKlippaScanner() {
    // Launch the Klippa Scanner
    val license = "{your-license}"
    KlippaScanner(this, license)
        .shutterButton(shutterButton)
        .startScanner(this)
}
                
            
                                    
KlippaShutterButton shutterButton = new KlippaShutterButton(
    // Whether to allow the shutter button. (If false the shutter button is greyed out)
    true, // allowShutterButton
    // Whether to hide the shutter button. (Only works if allowShutterButton is false)
    false // hideShutterButton
);

private void startKlippaScanner() {
    // Launch the Klippa Scanner
    String license = "{your-license}";
    new KlippaScanner(this, license)
        .shutterButton(shutterButton)
        .startScanner(this);
}
                                    
                                
    
// 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);
    

Customizing the timers

        
private val durations = KlippaDurations(
    // The duration of the timer. (Only works if timer is enabled)
    timerDuration = 0.5,
    // Whether to show a success checkmark and how long.
    successPreviewDuration = 0.0,
    // Whether to show a image preview and how long. (Only works if successPreviewDuration is 0.0)
    previewDuration = 1.0
)

// Call this from a button
private fun startKlippaScanner() {
    // Launch the Klippa Scanner
    val license = "{your-license}"
    KlippaScanner(this, license)
        .durations(durations)
        .startScanner(this)
}
                                    
                                
            
KlippaDurations durations = new KlippaDurations(
    // The duration of the timer. (Only works if timer is enabled)
    0.5, // timerDuration
    // Whether to show a success checkmark and how long.
    0.0, // successPreviewDuration
    // Whether to show a image preview and how long. (Only works if successPreviewDuration is 0.0)
    1.0 // previewDuration
);

private void startKlippaScanner() {
    // Launch the Klippa Scanner
    String license = "{your-license}";
    new KlippaScanner(this, license)
        .durations(durations)
        .startScanner(this);
}
            
        

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

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

// Whether to show a success checkmark and how long.
klippaScannerIntent.putExtra(KlippaScanner.SUCCESS_PREVIEW_DURATION, 0.4);
    

Customizing the camera modes

The SDK supports three camera modes: KlippaSingleDocumentMode, KlippaMultipleDocumentMode and KlippaSegmentedDocumentMode:

KlippaSingleDocumentMode is used for scanning a single-page document.

KlippaMultipleDocumentMode is used for scanning a document that consists of multiple pages.

KlippaSegmentedDocumentMode is used for scanning a single-page document with multiple photo captures. The photos are stitched together to create a single long document. Suitable for scanning long receipts.

Each comes with its own set of instructions which can be customized and if null is supplied then no instructions are shown.
The SDK starts with KlippaSingleDocumentMode as default. Not all modes are required, the SDK allows to leave out the undesired camera modes as long as there is a minimum of 1 camera mode.

        
private val cameraModes = KlippaCameraModes(
    listOf(
        // The document mode for scanning a single-page document.
        KlippaSingleDocumentMode(
            name = "Single Document",
            Instructions(
                message = "Single Document Instructions",
                dismissHandler = {
                    Toast.makeText(this, "Single document mode instructions dismissed.", Toast.LENGTH_LONG).show()
                })
        ),
        // The document mode for scanning a document which consists of multiple pages.
        KlippaMultipleDocumentMode(
            name = "Multiple Document",
            Instructions(
                message = "Multiple Document Instructions",
                dismissHandler = {
                    Toast.makeText(this, "Multiple document mode instructions dismissed.", Toast.LENGTH_LONG).show()
                })
        ),
        // The document mode for scanning a single-page document with multiple photo captures. Suitable for scanning long receipts.
        KlippaSegmentedDocumentMode(
            name = "Segmented Document",
            Instructions(
                message = "Segmented Document Instructions",
                dismissHandler = {
                    Toast.makeText(this, "Segmented document mode instructions dismissed.", Toast.LENGTH_LONG).show()
                })
        )
    ),
    // The index to set which camera mode will be shown as default.
    startingIndex = 0
)


// Call this from a button
private fun startKlippaScanner() {
    // Launch the Klippa Scanner
    val license = "{your-license}"
    KlippaScanner(this, license)
        .cameraModes(cameraModes)
        .startScanner(this)
}
                                    
                                
            
List<KlippaDocumentMode> modes = Arrays.asList(
        // The document mode for scanning a single-page document.
        new KlippaSingleDocumentMode(
                "Single Document",
                new Instructions(
                        "Single Document Instructions",
                        () -> {
                            Toast.makeText(this, "Single document mode instructions dismissed.", Toast.LENGTH_LONG).show();
                            return null;
                        })
        ),
        // The document mode for scanning a document which consists of multiple pages.
        new KlippaMultipleDocumentMode(
                "Multiple Document",
                new Instructions(
                        "Multiple Document Instructions",
                        () -> {
                            Toast.makeText(this, "Multiple document mode instructions dismissed.", Toast.LENGTH_LONG).show();
                            return null;
                        })
        ),
        // The document mode for scanning a single-page document with multiple photo captures. Suitable for scanning long receipts.
        new KlippaSegmentedDocumentMode(
                "Segmented Document",
                new Instructions(
                        "Segmented Document Instructions",
                        () -> {
                            Toast.makeText(this, "Segmented document mode instructions dismissed.", Toast.LENGTH_LONG).show();
                            return null;
                        })
        )
);
// The index to set which camera mode will be shown as default.
int startingIndex = 0;
KlippaCameraModes cameraModes = new KlippaCameraModes(modes, startingIndex);

private void startKlippaScanner() {
    // Launch the Klippa Scanner
    String license = "{your-license}";
    new KlippaScanner(this, license)
        .cameraModes(cameraModes)
        .startScanner(this);
}
            
        

Calling the OCR API

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

For instructions, please visit the documentation page.

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: 10.5MB
  • armeabi-v7a.apk: 8MB
  • x86_64.apk: 26.7MB
  • x86.apk: 19.6MB

While creating a single APK file creates a file that is 64.8MB, since most phones are ARM, on average APK splitting will result in a 40MB smaller APK, in those cases the SDK will add between 23-26MB to the APK size.

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
3.1.6 implementation 'com.klippa:scanner:3.1.6' Download
3.1.5 implementation 'com.klippa:scanner:3.1.5' Download
3.1.4 implementation 'com.klippa:scanner:3.1.4' Download
3.1.3 implementation 'com.klippa:scanner:3.1.3' Download
3.1.2 implementation 'com.klippa:scanner:3.1.2' Download
3.1.1 implementation 'com.klippa:scanner:3.1.1' Download
3.1.0 implementation 'com.klippa:scanner:3.1.0' Download
3.0.3 implementation 'com.klippa:scanner:3.0.3' Download
3.0.2 implementation 'com.klippa:scanner:3.0.2' Download
3.0.1 implementation 'com.klippa:scanner:3.0.1' Download
3.0.0 implementation 'com.klippa:scanner:3.0.0' Download
2.1.11 implementation 'com.klippa:scanner:2.1.11' Download
2.1.10 implementation 'com.klippa:scanner:2.1.10' Download
2.1.9 implementation 'com.klippa:scanner:2.1.9' Download
2.1.8 implementation 'com.klippa:scanner:2.1.8' Download
2.1.7 implementation 'com.klippa:scanner:2.1.7' Download
2.1.6 implementation 'com.klippa:scanner:2.1.6' Download
2.1.5 implementation 'com.klippa:scanner:2.1.5' Download
2.1.4 implementation 'com.klippa:scanner:2.1.4' Download
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

3.1.6

  • Fixed issue with shutter button input lag.

3.1.5

  • Fixed issue where ModePicker did not hide correctly.

3.1.4

  • Fixed issue where navigation graph could cause conflicts.

3.1.3

Changed

  • Reverted kotlin & gradle version bumps as they were not backwards compatible, the next major version will include these changes again.

3.1.2

Fixed

  • Issue where the menu bar icons would not update correctly on API levels 34+.

Changed

  • Improved document detection.

3.1.1

Changed

  • name and instructions are now mutable directly inside of their corresponding KlippaDocumentMode.

3.1.0

Added

  • KlippaCameraModes which is used to setup what camera modes the user can pick from, optionally supply it with an startingIndex to preselect a camera mode.
  • Supported camera modes: KlippaSingleDocumentMode, KlippaMultipleDocumentMode, KlippaSegmentedDocumentMode.
  • groupId to KlippaImage; KlippaSingleDocumentMode adds the same groupId for each photo, KlippaMultipleDocumentMode and KlippaSegmentedDocumentMode add a unique groupId for each photo.
  • The ability to change/localize the name of each camera mode by setting it’s name.
  • The ability to show instructions for each camera mode by supplying it with Instructions.
  • klippaCameraModes to KlippaScannerBuilder, which can be used to setup different KlippaCameraModes.
  • Animations and other UX improvements.

Changed

  • build(context: Context) now returns Result<Intent, Exception> instead of only Intent.

3.0.3

Fixed

  • Issue where .kt files were not being de-compiled correctly.

3.0.2

Fixed

  • High memory usage during motion detection.
  • Issue where full storage exceptions were not being handled correctly.
  • Issue where scanner might fail to continue after being in the background.

Changed

  • The scanner now always finishes on the main thread.
  • Scanner now ends when there are insufficient permissions to launch.

3.0.1

Fixed

  • Issue where setting deleteButtonText or klippa_delete_button_text did not update the UI.

3.0.0

NOTE: This version introduces breaking changes, please see our documentation for new implementation.

Changed

  • Scanner setup requires KlippaScannerBuilder.
  • Scanner results are only sent through KlippaScannerListener.

2.1.11

Fixed

  • Issue where SDK could crash when processing a taken photo and SDK is moved to background.

2.1.10

Fixed

  • Issue where camera could fail to load on Android 13 (API Level 33).

2.1.9

Fixed

  • Issue where some UI components would fail to load.

2.1.8

Fixed

  • Issue where layout and drawable resources could cause conflicts.

2.1.7

Fixed

  • Issue where toolbar icons were not set correctly when editing an image.

Added

  • The ability to set DEFAULT_COLOR to KlippaScanner.DefaultColor.ENHANCED. This setting adds additional enhancements to the taken image.

2.1.6

Fixed

  • Issue where scanner would not restart after returning back from the review screen.

2.1.5

Fixed

  • Issue where thumbnail image was not showing correctly when using previewDuration.
  • Provide users with orientation warning when device is not held in portrait mode, text can be set with: orientationWarningMessage.

2.1.4

Fixed

  • Issue where the torch was flashing while image capturing, even if the environment is bright enough.
  • Issue with JavaDoc.

Added

  • Ability to set texts for deleteButtonText, retakeButtonText, cancelButtonText, cancelAndDeleteImagesButtonText and cancelConfirmationMessage.

Changed

  • Removed JCenter()

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

Fixed

  • Issue where some properties could not be accessed unexpectedly.

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.