Integration

Android Manifest

The Gini Capture SDK uses the camera therefore the camera permission is required:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="...">

    <uses-permission android:name="android.permission.CAMERA" />

</manifest>

Note

Make sure you request the camera permission before starting the SDK.

Requirements Check

We recommend running our runtime requirements check first before launching the Gini Capture SDK to ensure the device is capable of taking pictures of adequate quality.

Simply run GiniCaptureRequirements.checkRequirements() and inspect the returned RequirementsReport for the result:

Note

On Android 6.0 and later the camera permission is required before checking the requirements.

final RequirementsReport report = GiniCaptureRequirements.checkRequirements(this);
if (!report.isFulfilled()) {
    final StringBuilder stringBuilder = new StringBuilder();
    report.getRequirementReports().forEach(requirementReport -> {
        if (!requirementReport.isFulfilled()) {
            stringBuilder.append(requirementReport.getRequirementId());
            stringBuilder.append(": ");
            stringBuilder.append(requirementReport.getDetails());
            stringBuilder.append("\n");
        }
    });
    Toast.makeText(this, "Requirements not fulfilled:\n" + stringBuilder,
            Toast.LENGTH_LONG).show();
}

Configuration

Configuration and interaction is done using the GiniCapture singleton.

To configure and create a new instance use the GiniCapture.Builder returned by GiniCapture.newInstance(). The builder creates a new GiniCapture singleton which you will need to destroy later with GiniCapture.cleanup(). This will also free up any used resources.

You must call GiniCapture.cleanup() after the user has seen (and potentially corrected) the extractions. You need to pass the updated extraction values to cleanup(). If the SDK didn’t return any extractions you can pass in empty strings.

Failing to call GiniCapture.cleanup() will throw an IllegalStateException when GiniCapture.newInstance() is called again.

To view all the configuration options see the documentation of GiniCapture.Builder.

Information about the configurable features are available on the Features page and UI customization options can be viewed in the Customization Guide.

Tablet Support

The Gini Capture SDK can be used on tablets, too. Some UI elements adapt to the larger screen to offer the best user experience for tablet users.

Many tablets with at least 8MP cameras don’t have an LED flash. Therefore we don’t require flash for tablets. For this reason the extraction quality on those tablets might be lower compared to smartphones.

On tablets landscape orientation is also supported (smartphones are portrait only). We advise you to test your integration on tablets in both orientations.

In landscape the camera screen’s UI displays the camera trigger button on the right side of the screen. Users can reach the camera trigger more easily this way. The camera preview along with the document corner guides are shown in landscape to match the device’s orientation.

Other UI elements on all the screens maintain their relative position and the screen layouts are scaled automatically to fit the current orientation.

Networking

Communication with the Gini Bank API is not part of the Gini Capture SDK in order to allow you the freedom to use a networking implementation of your own choosing.

Note

You should have received Gini Bank API client credentials from us. Please get in touch with us in case you don’t have them. Without credentials you won’t be able to use the Gini Bank API.

We provide the GiniCaptureNetworkService interface which is used to upload, analyze and delete documents. See the reference documentation for details.

Default Implementation

The quickest way to add networking is to use the Gini Capture Network Library.

To use it add the gini-capture-network-lib dependency to your app’s build.gradle:

dependencies {
    ...
    implementation 'net.gini.android:gini-capture-sdk-default-network:3.0.0'
}

For the Gini Capture SDK to be aware of the default implementation create an instance and pass it to the builder of GiniCapture:

GiniCaptureDefaultNetworkService networkService =
    GiniCaptureDefaultNetworkService.builder((Context) this)
        .setClientCredentials(myClientId, myClientSecret, myEmailDomain)
        .build();

GiniCapture.newInstance()
    .setGiniCaptureNetworkService(networkService)
    .build();

The default implementation follows the builder pattern. See the documentation of GiniCaptureDefaultNetworkService.Builder for configuration options.

Retrieve the Analyzed Document

You can call GiniCaptureDefaultNetworkService.getAnalyzedGiniApiDocument() after the Gini Capture SDK has returned extractions to your application. It returns the Gini Bank API document which was created when the user uploaded an image or pdf for analysis.

When extractions were retrieved without using the Gini Bank API, then it will return null. For example when the extractions came from an EPS QR Code.

Note

Make sure to call it before calling GiniCapture.cleanup(). Otherwise the analyzed document won’t be available anymore.

Custom Implementation

You can also provide your own networking by implementing the GiniCaptureNetworkService interface. Pass your instances to the builder of GiniCapture as shown above.

You may also use the Gini Bank API Library for Android or implement communication with the Gini Bank API yourself.

Cleanup and Sending Feedback

Your app should clean up the SDK and provide feedback for the extractions the Gini Bank API delivered. Feedback should be sent only for the extractions the user has seen and accepted (or corrected).

void stopGiniCaptureSDK() {
    // After the user has seen and potentially corrected the extractions
    // cleanup the SDK while passing in the final extraction values
    // which will be used as feedback to improve the future extraction accuracy:
    GiniCapture.cleanup((Context) this,
            paymentRecipient,
            paymentReference,
            paymentPurpose,
            iban,
            bic,
            amount
        )
}

We provide a sample test case here to verify that extraction feedback sending works. You may use it along with the example pdf and json files as a starting point to write your own test case.

The sample test case is based on the Bank API documentation’s recommended steps for testing extraction feedback sending.

For additional information about feedback see the Gini Bank API documentation.

Capturing documents

To launch the Gini Capture SDK you only need to:

  1. Request camera access,

  2. Configure a new instance of GiniCapture,

  3. Launch the CameraActivity,

  4. Handle the extraction results,

  5. Cleanup the SDK by calling GiniCapture.cleanup() while also providing the required extraction feedback to improve the future extraction accuracy. You don’t need to implement any extra steps, just follow the recommendations below:

    • Please provide values for all necessary fields, including those that were not extracted.
    • Provide the final data approved by the user (and not the initially extracted only).
    • Do cleanup after TAN verification.

The following diagram shows the interaction between your app and the SDK:

Diagram of interaction between your app and the SDK

Note

Check out the example app to see how an integration could look like.

The CameraActivity can return with the following result codes:

  • Activity.RESULT_OK

    Document was analyzed and the extractions are available in the EXTRA_OUT_EXTRACTIONS result extra. It contains a Bundle with the extraction labels as keys and GiniCaptureSpecificExtraction parcelables as values.

  • Activity.RESULT_CANCELED

    User has canceled the Gini Capture SDK.

  • CameraActivity.RESULT_ERROR

    An error occured and the details are available in the EXTRA_OUT_ERROR result extra. It contains a parcelable extra of type GiniCaptureError detailing what went wrong.

  • CameraActivity.RESULT_ENTER_MANUALLY

    The document analysis finished with no results or an error and the user clicked the “Enter manually” button.

The following example shows how to launch the Gini Capture SDK and how to handle the results:

void launchGiniCapture() {
    // Make sure camera permission has been already granted at this point.

    // Check that the device fulfills the requirements.
    RequirementsReport report = GiniCaptureRequirements.checkRequirements((Context) this);
    if (!report.isFulfilled()) {
        handleUnfulfilledRequirements(report);
        return;
    }

    // Instantiate the networking implementations.
    GiniCaptureNetworkService networkService = ...

    // Configure GiniCapture and create a new singleton instance.
    GiniCapture.newInstance()
            .setGiniCaptureNetworkService(networkService)
            ...
            .build();

    // Launch the CameraActivity and wait for the result.
    Intent intent = new Intent(this, CameraActivity.class);
    startActivityForResult(intent, GINI_CAPTURE_REQUEST);
}

@Override
protected void onActivityResult(final int requestCode, final int resultCode,
        final Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == GINI_CAPTURE_REQUEST) {
        switch (resultCode) {
            case Activity.RESULT_CANCELED:
                GiniCapture.cleanup(this, "", "",
                            "", "","", Amount.EMPTY);
                break;

            case Activity.RESULT_OK:
                // Retrieve the extractions
                Bundle extractionsBundle = data.getBundleExtra(
                        CameraActivity.EXTRA_OUT_EXTRACTIONS);

                // Retrieve the extractions from the extractionsBundle
                Map<String, GiniCaptureSpecificExtraction> extractions = new HashMap<>();
                for (String extractionLabel : extractionsBundle.keySet()) {
                    GiniCaptureSpecificExtraction extraction = extractionsBundle.getParcelable(extractionLabel);
                    extractions.put(extractionLabel, extraction);
                }
                handleExtractions(extractions);

                break;

            case CameraActivity.RESULT_ERROR:
                // Something went wrong, retrieve and handle the error
                final GiniCaptureError error = data.getParcelableExtra(
                        CameraActivity.EXTRA_OUT_ERROR);
                if (error != null) {
                    handleError(error);
                }
                GiniCapture.cleanup(this, "", "",
                        "", "","", Amount.EMPTY);
                break;

            case CameraActivity.RESULT_ENTER_MANUALLY:
                handleEnterManually();
                GiniCapture.cleanup(this, "", "",
                        "", "","", Amount.EMPTY);
                break;
        }
    }
}

void stopGiniCaptureSDK() {
    // After the user has seen and potentially corrected the extractions
    // cleanup the SDK while passing in the final extraction values
    // which will be used as feedback to improve the future extraction accuracy:
    GiniCapture.cleanup((Context) this,
            paymentRecipient,
            paymentReference,
            paymentPurpose,
            iban,
            bic,
            amount
        )
}