Capture Flow with Fragments
In order to use the capture flow, follow these steps:
Request camera access.
Configure the capture feature using the
GiniCapture.Builder()
as in the documentation.Create the
GiniCaptureFragment
by callingcreateGiniCaptureFragment()
fromGiniCapture
. It is the entrance for Gini Capture SDK.For open-with feature, create the
GiniCaptureFragment
by callingGiniCapture.getInstance().createGiniCaptureFragmentForIntent(Context context, Intent intent, CreateGiniCaptureFragmentForIntentCallback captureIntentCallback)
to to create theGiniCaptureFragment
with the PDF/images from the intent.Set a listener (
GiniCaptureFragmentListener
) on the instance ofGiniCaptureFragment
via thesetListener()
instance method. This allows unsubscribing when the host fragment has finished it process (by returning a result through the listener).Hide your activity’s action bar. The Gini Capture SDK fragments show their own navigation bars.
Show the
GiniCaptureFragment
with your fragment manager.Handle the extraction results.
Send the final transfer summary values to Gini by calling
GiniCapture.sendTransferSummary()
method which will be used to improve the future extraction accuracyClean up the SDK by calling
GiniCapture.cleanup(Conext context)
which will release the resources used by SDK.Â
Follow these recommendations:
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).
Send transfer summary only after TAN verification.
You don’t need to implement any extra steps.
The diagram shows the interaction between your app and the SDK:
Gini Capture SDK returns one of the following results:
CaptureSDKResult.Success
A document was analysed and the extractions are available in the properties of the CaptureSDKResult.Success
object.
CaptureSDKResult.Cancel
The user canceled Gini Capture SDK.
CaptureSDKResult.Error
An error occurred and the details are available in the value
property of the CaptureSDKResult.Error
object.
CaptureSDKResult.Empty
Gini Capture SDK was able to extract information, but they were not payment related. Your app should proceed with enabling your user to enter the payment information manually.
CaptureSDKResult.EnterManually
The document analysis finished with no results or an error and the user clicked the Enter manually button on either the No Results Screen or the Error Screen. To enable manual entry of payment information, let your app prompt users for manual input.
Learn how to launch the capture flow and handle the results from the example:
class ClientExampleFragment :
Fragment(R.layout.fragment_client_example),
// Implement this listener in the fragment that wants to host the Gini Capture SDK
GiniCaptureFragmentListener {
private lateinit var permissionHandler: PermissionHandler
private var mFileImportCancellationToken: CancellationToken? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
requestCameraPermissionAndStartCaptureSDK()
} else {
// Set the listener on GiniCaptureFragment again after the fragment has been restored
val giniCaptureFragment =
requireActivity().supportFragmentManager.findFragmentByTag("fragment_host") as? GiniCaptureFragment
giniCaptureFragment?.setListener(this)
}
// Hide the action bar because the Gini Capture SDK fragments have their own action bars (navigation bars)
(requireActivity() as AppCompatActivity).supportActionBar?.hide()
}
private fun requestCameraPermissionAndStartCaptureSDK() {
permissionHandler = PermissionHandler(requireActivity())
lifecycleScope.launch {
if (permissionHandler.grantPermission(Manifest.permission.CAMERA)) {
// Always configure the Gini Capture SDK before starting it
configureCaptureSDK()
// If your activity was launched from another app via "open with" then
// pass the activity's intent to Gini Capture SDK
if (isIntentActionViewOrSend(requireActivity().intent) {
startCaptureSDKForIntent(requireActivity().intent)
} else {
// Otherwise you can start the Gini Capture SDK without any intent
startCaptureSDK()
}
} else {
// Inform user that camera permission is needed
}
}
}
// Configures the Gini Capture SDK (it is the same as activity flow but major versions in Gini Capture SDK may have different configuration)
private fun configureCaptureSDK() {
val clientId = // your Gini Pay API client ID
val clientSecret = // your Gini Pay API client secret
val documentMetadata = DocumentMetadata()
val networkService = GiniCaptureDefaultNetworkService
.builder(requireContext())
.setClientCredentials(
clientId,
clientSecret,
"example.com"
)
.setDocumentMetadata(documentMetadata)
.build()
GiniCapture.Builder()
.setGiniCaptureNetworkService(networkService)
.setFileImportEnabled(true)
.setDocumentImportEnabledFileTypes(DocumentImportEnabledFileTypes.PDF_AND_IMAGES)
.setQRCodeScanningEnabled(true)
.setFlashButtonEnabled(true)
.setMultiPageEnabled(true)
.build()
}
private fun startCaptureSDK() {
// Create the GiniCaptureFragment (the entrance for Gini Capture SDK)
val giniCaptureFragment = GiniCapture.createGiniCaptureFragment()
// Set the listener to receive the Gini Capture SDK's results
giniCaptureFragment.setListener(this)
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.fragment_host, giniCaptureFragment, "GiniCaptureFragment")
.addToBackStack(null)
.commit()
}
fun startCaptureSDKForIntent(openWithIntent: Intent) {
mFileImportCancellationToken = GiniCapture.getInstance().createGiniCaptureFragmentForIntent(
requireActivity(),
openWithIntent,
object : GiniCapture.CreateGiniCaptureFragmentForIntentCallback {
override fun callback(result: GiniCapture.CreateGiniCaptureFragmentForIntentResult?) {
when (result) {
is GiniCapture.CreateGiniCaptureFragmentForIntentResult.Success -> {
// Opening the file(s) from the intent and creating the GiniCaptureFragment finished
// Set the listener to receive the Gini Capture SDK's results
result.fragment.setListener(this@ClientCaptureSDKFragment)
// Show the CaptureFlowFragment for example via the fragment manager:
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.fragment_host, result.fragment, "GiniCaptureFragment")
.addToBackStack(null)
.commit()
}
is GiniCapture.CreateGiniCaptureFragmentForIntentResult.Error -> {
// There was an error in opening the file(s) from the intent
}
is GiniCapture.CreateGiniCaptureFragmentForIntentResult.Cancelled -> {
// Creating the CaptureFlowFragment was cancelled
}
}
}
})
}
override fun onDestroy() {
super.onDestroy()
if (mFileImportCancellationToken != null) {
mFileImportCancellationToken!!.cancel()
mFileImportCancellationToken = null
}
}
// Handle the results from Gini Capture SDK
override fun onFinishedWithResult(result: CaptureSDKResult) {
when (result) {
is CaptureSDKResult.Success -> {
// Handle extraction results (to proceed with the transaction)
handleExtractions(result.specificExtractions)
// After the user has seen (and maybe edited) the extractions and has executed the transfer
// please send the transfer summary to Gini with the final data approved by the user.
// The stopGiniCaptureSDKWithTransferSummary(...) method shows how to send the transfer summary.
}
is CaptureSDKResult.Error -> {
// Something went wrong when opening the file(s) from the intent or uploading the document
}
CaptureSDKResult.Empty -> {
// Handle empty result
}
CaptureSDKResult.Cancel -> {
// Process was cancelled by user
}
CaptureSDKResult.EnterManually -> {
// User wants to enter the invoice data manually
}
}
}
fun stopGiniCaptureSDKWithTransferSummary(paymentRecipient: String,
paymentReference: String,
paymentPurpose: String,
iban: String,
bic: String,
amount: Amount
) {
// After the user has seen and potentially corrected the extractions, send the final
// transfer summary values to Gini which will be used to improve the future extraction accuracy:
GiniCapture.sendTransferSummary(
paymentRecipient,
paymentReference,
paymentPurpose,
iban,
bic,
amount
)
// cleanup the capture SDK after sending the transfer summary
GiniCapture.cleanup(requireActivity())
}
private fun isIntentActionViewOrSend(intent: Intent): Boolean {
val action = intent.action
return Intent.ACTION_VIEW == action || Intent.ACTION_SEND == action || Intent.ACTION_SEND_MULTIPLE == action
}
}