View on GitHub

sensibill-android-documentation

Customization

Functionality

CaptureStandaloneActivity Configuration

The behaviour of CaptureStandaloneActivity can be configured by passing a CaptureConfig configuration object as a Intent Extra with the tag CaptureStandaloneActivity.EXTRA_CAPTURE_CONFIG.

The configurable properties and their effects are below:

Name Description Type Default Value
defaultFlashMode Sets the default flash mode that will be used by the camera. .models.FlashMode* FLASH_MODE_AUTO
enableAutoCapture Enables the auto-capture feature. Boolean true
initialAutoCaptureState Sets the initial state of the auto-capture functionality (true represents ON).
Note: This flag only has an effect if enableAutoCapture is set to true.
Boolean true
enableBlurDetection Enables the blur detection feature.
When enabled, the capture flow will attempt to determine if a captured image is blurry and display a warning tothe user if so, prompting them to retake the blurry image.
Boolean true
enableFeedbackToast Enables a feedback toast message on the capture screen. Boolean true
enableGalleryImport Enables the ability for a user to select an image from their device’s image gallery. Boolean true
enableMultiPageCapture Allows the user to capture multiple distinct images in a single capture flow. Boolean true
maxPages Determines the maximum number of distinct images that can be captured in a single capture flow.
Note: This value is only used if enableMultiPageCapture is set to true
Int 3
enableLongCapture Enables the long receipt feature.
When enabled, the user is able to select a mode in which they can take a receipt image as two different parts: a top and a bottom. The images will then be stitched together.
Boolean true
enableCrop Enables the user to manually crop, or adjust the existing cropping on a captured image. Boolean true
enableSecureWindow Enables Android’s secure window feature for the capture flow. When enabled, the user will be unable to take screenshots or record the screen. Boolean false
attachLocationData Enables attaching GPS location data via EXIF to captured images.
In order for this feature to function properly, the consumer of this module must first request location permissions from the user before launching the capture activity.

Permissions Required:
- android.Manifest.permission.ACCESS_COARSE_LOCATION
- android.Manifest.permission.ACCESS_FINE_LOCATION

See the EXIF Data section for more detailed information on recorded EXIF data
Boolean false
compressForSensibillApi This flag only has an effect if using the Standalone Capture module directly
Enables built-in compression for captured images in order to make them compatible with the Sensibill APIs.

If using the standalone capture module to capture images to be used with the Sensibill SDK, or images to be otherwise uploaded to the Sensibill APIs please ensure this flag is set to true. When set to false, captured images will be returned at full size with no compression.

Default value: true
Boolean  

*(base module package: com.getsensibill.capturestandalone)

Example usage: Configuring and launching standalone capture such that the flash is always off, 5 images can be taken, long receipt capture is disabled, and the secure window feature is enabled, otherwise default behaviour is used. Kotlin

val config = CaptureConfig(
    allowFlashToggling = false,
    defaultFlashMode = FlashMode.FLASH_MODE_OFF,
    maxPages = 5,
    enableLongCapture = false,
    enableSecureWindow = true
)
val intent = Intent(this, CaptureStandaloneActivity::class.java).apply {
    putExtra(CaptureStandaloneActivity.EXTRA_CAPTURE_CONFIG, config)
}
startActivityForResult(intent, 0x05)

Java

CaptureConfig config = new CaptureConfig(
        false,                      // Allow flash toggling
        FlashMode.FLASH_MODE_OFF,   // Initial flash state
        true,                       // Auto-capture enabled
        true,                       // Initially auto-capture is enabled
        true,                       // Blur detection enabled
        true,                       // Feedback toasts enabled
        true,                       // Gallery import enabled
        true,                       // Multi-page capture is enabled
        5,                          // 5 pages will be captured
        false,                      // Long receipt capture disabled
        true,                       // Crop feature enabled
        true,                       // Secure window feature will be enabled
        true                        // Attach location data will be enabled
);
Intent intent = new Intent(this, CaptureStandaloneActivity.class);
intent.putExtra(CaptureStandaloneActivity.EXTRA_CAPTURE_CONFIG, config);
startActivityForResult(intent, 0x05);



EXIF Data

Captured Data

The Capture Standalone module records and attaches some EXIF data to the images that it outputs. The recorded data, as well as any applicable configurations are as follows.

When using the camera to capture an image:

When using gallery import:

Location Data Setup

In order for the Capture Standalone module to record location data, the following steps must be taken.

AndroidManifest.xml Permission Request

The integrating app’s AndroidManifest will automaticall inherit the permissions requested by the Capture Standalone module, but for verbosity it is recommended that you add the following permissions to your own AndroidManifest:

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

Note: If you do not wish to use the location data section of the standalone capture module, and your app did not previously request location permissions, the permissions can be removed from the manifest by adding the following to your AndroidManifest:

<!-- Integrators can remove the permissions by including the following: -->
<!-- Prevent the Sensibill SDK from causing the host app to require the location permission -->
<uses-permission
    android:name="android.permission.ACCESS_FINE_LOCATION"
    tools:node="remove" />
<uses-permission
    android:name="android.permission.ACCESS_COARSE_LOCATION"
    tools:node="remove" />
Runtime Permission Request

Before attempting to launch the Capture Standalone module, the integrating app must independently request the user for locaiton permissions. The Capture Standalone module will not make the location permission request itself, and will only gather location data if both the CaptureConfig.attachLocationData flag is set to true and the required location permissions are already granted.

See below for an example implementation. Kotlin:

class IntegratorActivity : AppCompatActivity() {
    // ...

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...

        if (savedInstanceState == null) {
            // Array of required permissions
            val permissions = arrayOf(
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_FINE_LOCATION
            )
            val hasPermissions = permissions
                // Check each permission
                .map { ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED }
                // We have required permissions if every check resolves to `true`
                .all { it }

            if (!hasPermissions) {
                ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE_LOCATION_PERMISSION)
            }
        // ...
    }

    // ...

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        when (requestCode) {
            REQUEST_CODE_LOCATION_PERMISSION -> {
                if (grantResults.any { it == PackageManager.PERMISSION_DENIED }) {
                    Timber.d("Location permission request denied")
                }
            }
            else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        }
    }

    // ...
}

Java:

public class IntegratorActivity extends AppCompatActivity {
    // ...

    @Override
    public void onCreate(Bundle savedInstanceState) {
        if (savedInstanceState == null) {
            String[] permissions = {
                    Manifest.permission.ACCESS_COARSE_LOCATION,
                    Manifest.permission.ACCESS_FINE_LOCATION
            };

            boolean hasPermissions = true;
            for (String permission : permissions) {
                boolean hasPermission = ActivityCompat.checkSelfPermission(getBaseContext(), permission) == PackageManager.PERMISSION_GRANTED;
                if (!hasPermission) {
                    hasPermissions = false;
                    break;
                }
            }

            if (!hasPermissions) {
                ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE_LOCATION_PERMISSION);
            }
        }
    }

    // ...

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CODE_LOCATION_PERMISSION:
                boolean requestsGranted = true;
                for (int grantResult : grantResults) {
                    if (grantResult == PackageManager.PERMISSION_DENIED) {
                        requestsGranted = false;
                        break;
                    }
                }

                if (!requestsGranted) {
                    Log.d("TAG", "Permission request denied");
                }

                break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    // ...
}
Configuration Flag

When creating the CaptureConfig object, the CaptureConfig.attachLocationData flag must be set to true




Styling

Theme Customization

Colour Attributes

Coloured components of the theme used in the standalone capture module can be customized. This can be achieved by creating a new style object in your app’s resources folder declaring a style named New.Theme.Sensibill, extending LegacyIntegration.New.Theme.Sensibill

Example: NOTE: It is very important that the stlye’s name and parent name match the example

<style name="New.Theme.Sensibill" parent="LegacyIntegration.New.Theme.Sensibill" >
    <item name="sb__brandPrimaryBackground">@color/your_colour_here</item>
    <item name="sb__brandPrimaryBackground2">@color/your_colour_here</item>
    <item name="sb__brandPrimaryForeground">@color/your_colour_here</item>

    <item name="sb__brandSecondaryBackground">@color/your_colour_here</item>
    <item name="sb__brandSecondaryForeground">@color/your_colour_here</item>

    <item name="sb__uiPrimaryBackground">@color/your_colour_here</item>
    <item name="sb__uiPrimaryForeground">@color/your_colour_here</item>
    <item name="sb__uiSecondaryBackground">@color/your_colour_here</item>
    <item name="sb__uiSecondaryForeground">@color/your_colour_here</item>

    <item name="sb__surfaceBackground">@color/your_colour_here</item>
    <item name="sb__surfaceForeground">@color/your_colour_here</item>

    <item name="sb__stateErrorBackground">@color/your_colour_here</item>
    <item name="sb__stateErrorForeground">@color/your_colour_here</item>
    <item name="sb__stateDisabledBackground">@color/your_colour_here</item>
    <item name="sb__stateDisabledForeground">@color/your_colour_here</item>
</style>


Text Appearances

The set of text appearances used by this module can be customized by overriding any or all the following text appearance styles: NOTE: As above, it is important that both the style name and parent exactly match those defined here

    <style name="TextAppearance.Sensibill" parent="Base.TextAppearance.Sensibill" />
    <style name="TextAppearance.Sensibill.H1" parent="Base.TextAppearance.Sensibill.H1" />
    <style name="TextAppearance.Sensibill.H2" parent="Base.TextAppearance.Sensibill.H2" />
    <style name="TextAppearance.Sensibill.H3" parent="Base.TextAppearance.Sensibill.H3" />
    <style name="TextAppearance.Sensibill.H4" parent="Base.TextAppearance.Sensibill.H4" />
    <style name="TextAppearance.Sensibill.Headline" parent="Base.TextAppearance.Sensibill.Headline" />
    <style name="TextAppearance.Sensibill.Body1" parent="Base.TextAppearance.Sensibill.Body1" />
    <style name="TextAppearance.Sensibill.Body2" parent="Base.TextAppearance.Sensibill.Body2" />
    <style name="TextAppearance.Sensibill.Button" parent="Base.TextAppearance.Sensibill.Button" />
    <style name="TextAppearance.Sensibill.Subtitle" parent="Base.TextAppearance.Sensibill.Subtitle" />
    <style name="TextAppearance.Sensibill.Caption" parent="Base.TextAppearance.Sensibill.Caption" />
    <style name="TextAppearance.Sensibill.Badge" parent="Base.TextAppearance.Sensibill.Badge" />


Component Styles

The base component styles used by this module can be customized by overriding any or all the following component styles: NOTE: As above, it is important that both the style name and parent exactly match those defined here

    <!--===== Buttons =====-->
    <!-- This shape appearance is used to define the background shape of Primary and Error buttons -->
    <style name="ShapeAppearance.Sensibill.RoundedButton" parent="Base.ShapeAppearance.Sensibill.RoundedButton" />

    <style name="Widget.Sensibill.Button.Primary" parent="Base.Widget.Sensibill.Button.Primary" />
    <style name="Widget.Sensibill.Button.Error" parent="Base.Widget.Sensibill.Button.Error" />
    <style name="Widget.Sensibill.Button.TextButton" parent="Base.Widget.Sensibill.Button.TextButton" />
    <style name="Widget.Sensibill.Button.TextButton.Small" parent="Base.Widget.Sensibill.Button.TextButton.Small" />
    <style name="Widget.Sensibill.Button.IconButton" parent="Base.Widget.Sensibill.Button.IconButton" />
    
    <!--===== AlertDialog =====-->
    <style name="AlertDialog.Sensibill.Theme" parent="Base.AlertDialog.Sensibill.Theme" />
    <style name="Widget.Sensibill.AlertDialog.TextButton" parent="Base.Widget.Sensibill.AlertDialog.TextButton" />



Additional Customization of Specific UI Elements

Additionally, style customizations can be applied to individual views in the standalone capture flow in a manner similar to what is defined above.

For example, the Preview page “Continue” button can be styled like so:

<style name="Widget.Sensibill.ReceiptPreview.Button.Continue" parent="Base.Widget.Sensibill.ReceiptPreview.Button.Continue">
    <item name="android:text">Finish</item>
    <item name="backgroundTint">#444</item>
</style>

This customization would change the the button to have a gray background, and read “Finish” instead of “Continue”.



Overriding OpenCV Libraries

Only available on v1.26.1+

We allow overriding the OpenCV libraries used within the standalone capture, in case there is an existing version used within the application. A new loadOpenCvBlock can be overridden before the OpenCvAnalyzer is initialized, allowing the integrator to load their own OpenCV libraries.

If you plan to use your own OpenCV libraries, exclude the opencv-for-capture module when importing the sensibill dependencies. For example:

implementation"com.getsensibill:sensibill-capture-standalone:X.X.X" {
    exclude group: 'com.getsensibill', module: 'opencv-for-capture'
}

Then load your own libraries using the OpenCvLoader.loadOpenCvBlock. This must be set prior to loading the standalone capture:

OpenCvLoader.loadOpenCvBlock = {
    System.loadLibrary("openCvLib")
}

Replace the "openCvLib" with the name of your OpenCV’s .so file(s). You may need to look into your generated project files to find them by name.

Note: this method does not guarantee compatibility with every OpenCV version, but exists to help resolve conflicts if different OpenCV versions are being used.