diff --git a/RELEASE-ORDER.md b/RELEASE-ORDER.md index 541f9b678e..337e78cb88 100644 --- a/RELEASE-ORDER.md +++ b/RELEASE-ORDER.md @@ -32,3 +32,4 @@ Release order for :bank-sdk:sdk 3.6.0: 3. :capture-sdk:sdk 3.6.0 4. :capture-sdk:default-network 3.6.0 5. :bank-sdk:sdk 3.6.0 + diff --git a/bank-api-library/library/src/androidTest/java/net/gini/android/bank/api/ExtractionFeedbackIntegrationTest.kt b/bank-api-library/library/src/androidTest/java/net/gini/android/bank/api/TransferSummaryIntegrationTest.kt similarity index 98% rename from bank-api-library/library/src/androidTest/java/net/gini/android/bank/api/ExtractionFeedbackIntegrationTest.kt rename to bank-api-library/library/src/androidTest/java/net/gini/android/bank/api/TransferSummaryIntegrationTest.kt index 96a523e7b6..c3acf2dfce 100644 --- a/bank-api-library/library/src/androidTest/java/net/gini/android/bank/api/ExtractionFeedbackIntegrationTest.kt +++ b/bank-api-library/library/src/androidTest/java/net/gini/android/bank/api/TransferSummaryIntegrationTest.kt @@ -22,7 +22,7 @@ import java.util.* @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) -class ExtractionFeedbackIntegrationTest { +class TransferSummaryIntegrationTest { private lateinit var giniBankAPI: GiniBankAPI private val moshi = Moshi.Builder().build() diff --git a/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/ExtractionsActivity.kt b/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/ExtractionsActivity.kt index cbd3728cbd..4d5de5e14f 100644 --- a/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/ExtractionsActivity.kt +++ b/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/ExtractionsActivity.kt @@ -19,7 +19,7 @@ import com.google.android.material.textfield.TextInputLayout import dagger.hilt.android.AndroidEntryPoint import net.gini.android.bank.sdk.GiniBank import net.gini.android.bank.sdk.exampleapp.R -import net.gini.android.bank.sdk.exampleapp.core.di.GiniCaptureNetworkServiceDebugDisabled +import net.gini.android.bank.sdk.exampleapp.core.di.GiniCaptureNetworkServiceDebugEnabled import net.gini.android.bank.sdk.exampleapp.databinding.ActivityExtractionsBinding import net.gini.android.capture.Amount import net.gini.android.capture.AmountCurrency @@ -31,7 +31,7 @@ import javax.inject.Inject /** * Displays the Pay5 extractions: paymentRecipient, iban, bic, amount and paymentReference. * - * A menu item is added to send feedback. + * A menu item is added to send transfer summary. */ @AndroidEntryPoint @@ -40,12 +40,20 @@ class ExtractionsActivity : AppCompatActivity(), ExtractionsAdapter.ExtractionsA private var mExtractions: MutableMap = hashMapOf() private lateinit var mExtractionsAdapter: ExtractionsAdapter - @Inject @GiniCaptureNetworkServiceDebugDisabled + + @Inject + @GiniCaptureNetworkServiceDebugEnabled lateinit var defaultNetworkService: GiniCaptureDefaultNetworkService // {extraction name} to it's {entity name} - private val editableSpecificExtractions = hashMapOf("paymentRecipient" to "companyname", "paymentReference" to "reference", - "paymentPurpose" to "text", "iban" to "iban", "bic" to "bic", "amountToPay" to "amount") + private val editableSpecificExtractions = hashMapOf( + "paymentRecipient" to "companyname", + "paymentReference" to "reference", + "paymentPurpose" to "text", + "iban" to "iban", + "bic" to "bic", + "amountToPay" to "amount" + ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -68,8 +76,8 @@ class ExtractionsActivity : AppCompatActivity(), ExtractionsAdapter.ExtractionsA } override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { - R.id.feedback -> { - sendFeedbackAndClose(binding) + R.id.transfer_summary -> { + sendTransferSummaryAndClose(binding) true } @@ -98,26 +106,32 @@ class ExtractionsActivity : AppCompatActivity(), ExtractionsAdapter.ExtractionsA editableSpecificExtractions.forEach { if (!mExtractions.containsKey(it.key)) { mExtractions[it.key] = GiniCaptureSpecificExtraction( - it.key, "", - it.value, null, emptyList()) + it.key, "", it.value, null, emptyList() + ) } } - adapter = ExtractionsAdapter(getSortedExtractions(mExtractions), this@ExtractionsActivity, editableSpecificExtractions.keys.toList()).also { + adapter = ExtractionsAdapter( + getSortedExtractions(mExtractions), + this@ExtractionsActivity, + editableSpecificExtractions.keys.toList() + ).also { mExtractionsAdapter = it } setOnTouchListener { _, _ -> performClick() - val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager + val inputMethodManager = + getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager inputMethodManager.hideSoftInputFromWindow(windowToken, 0) } } } - private fun getSortedExtractions(extractions: Map): List = extractions.toSortedMap().values.toList() + private fun getSortedExtractions(extractions: Map): List = + extractions.toSortedMap().values.toList() - private fun sendFeedbackAndClose(binding: ActivityExtractionsBinding) { - // Feedback should be sent only for the user visible fields. Non-visible fields should be filtered out. + private fun sendTransferSummaryAndClose(binding: ActivityExtractionsBinding) { + // Transfer summary should be sent only for the user visible fields. Non-visible fields should be filtered out. // In a real application the user input should be used as the new value. var amount = mExtractions["amountToPay"]?.value ?: "" @@ -131,10 +145,14 @@ class ExtractionsActivity : AppCompatActivity(), ExtractionsAdapter.ExtractionsA amount = Amount.EMPTY.amountToPay() } - GiniBank.releaseCapture(applicationContext, paymentRecipient, paymentReference, paymentPurpose, iban, bic, Amount( - BigDecimal(amount.removeSuffix(":EUR")), AmountCurrency.EUR) + GiniBank.sendTransferSummary( + paymentRecipient, paymentReference, paymentPurpose, iban, bic, Amount( + BigDecimal(amount.removeSuffix(":EUR")), AmountCurrency.EUR + ) ) + GiniBank.releaseCapture(applicationContext) + finish() } @@ -151,25 +169,30 @@ class ExtractionsActivity : AppCompatActivity(), ExtractionsAdapter.ExtractionsA companion object { const val EXTRA_IN_EXTRACTIONS = "EXTRA_IN_EXTRACTIONS" - fun getStartIntent(context: Context, extractionsBundle: Map): Intent = - Intent(context, ExtractionsActivity::class.java).apply { - putExtra(EXTRA_IN_EXTRACTIONS, Bundle().apply { - extractionsBundle.map { putParcelable(it.key, it.value) } - }) - } + fun getStartIntent( + context: Context, extractionsBundle: Map + ): Intent = Intent(context, ExtractionsActivity::class.java).apply { + putExtra(EXTRA_IN_EXTRACTIONS, Bundle().apply { + extractionsBundle.map { putParcelable(it.key, it.value) } + }) + } } } -private class ExtractionsAdapter(var extractions: List, - var listener: ExtractionsAdapterInterface? = null, - val editableSpecificExtractions: List) : RecyclerView.Adapter() { +private class ExtractionsAdapter( + var extractions: List, + var listener: ExtractionsAdapterInterface? = null, + val editableSpecificExtractions: List +) : RecyclerView.Adapter() { interface ExtractionsAdapterInterface { fun valueChanged(key: String, value: String) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ExtractionsViewHolder { - val holder = ExtractionsViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_extraction, parent, false)) + val holder = ExtractionsViewHolder( + LayoutInflater.from(parent.context).inflate(R.layout.item_extraction, parent, false) + ) holder.mTextValue.addTextChangedListener { listener?.valueChanged(holder.mTextInputLayout.hint.toString(), it.toString()) } diff --git a/bank-sdk/example-app/src/main/res/menu/menu_extractions.xml b/bank-sdk/example-app/src/main/res/menu/menu_extractions.xml index 636224fc8e..aba6b7720d 100644 --- a/bank-sdk/example-app/src/main/res/menu/menu_extractions.xml +++ b/bank-sdk/example-app/src/main/res/menu/menu_extractions.xml @@ -2,7 +2,7 @@ = - InjectedViewAdapterInstance(ImageOnboardingIllustrationAdapter(R.drawable.gbs_digital_invoice_list_image, R.string.gbs_digital_invoice_onboarding_text_1)) + InjectedViewAdapterInstance( + ImageOnboardingIllustrationAdapter( + R.drawable.gbs_digital_invoice_list_image, + R.string.gbs_digital_invoice_onboarding_text_1 + ) + ) var digitalInvoiceOnboardingIllustrationAdapter: OnboardingIllustrationAdapter set(value) { digitalInvoiceOnboardingIllustrationAdapterInstance = InjectedViewAdapterInstance(value) @@ -116,9 +121,7 @@ object GiniBank { fun setCaptureConfiguration(captureConfiguration: CaptureConfiguration) { check(giniCapture == null) { "Gini Capture already configured. Call releaseCapture() before setting a new configuration." } GiniBank.captureConfiguration = captureConfiguration - GiniCapture.newInstance() - .applyConfiguration(captureConfiguration) - .build() + GiniCapture.newInstance().applyConfiguration(captureConfiguration).build() giniCapture = GiniCapture.getInstance() } @@ -127,19 +130,49 @@ object GiniBank { */ fun setCaptureConfiguration(context: Context, captureConfiguration: CaptureConfiguration) { GiniBank.captureConfiguration = captureConfiguration - GiniCapture.newInstance(context) - .applyConfiguration(captureConfiguration) - .build() + GiniCapture.newInstance(context).applyConfiguration(captureConfiguration).build() giniCapture = GiniCapture.getInstance() } + + /** + * Provides transfer summary to Gini. + * + * Please provide the required transfer summary to improve the future extraction accuracy. + * + * Follow the recommendations below: + * - Make sure to call this method before calling [releaseCapture] if the user has completed TAN verification. + * - 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 the transfer summary after TAN verification and provide the extraction values the user has used. + * + * @param paymentRecipient payment receiver + * @param paymentReference ID based on Client ID (Kundennummer) and invoice ID (Rechnungsnummer) + * @param paymentPurpose statement what this payment is for + * @param iban international bank account + * @param bic bank identification code + * @param amount accepts extracted amount and currency + */ + fun sendTransferSummary( + paymentRecipient: String, + paymentReference: String, + paymentPurpose: String, + iban: String, + bic: String, + amount: Amount + ) { + GiniCapture.sendTransferSummary( + paymentRecipient, paymentReference, paymentPurpose, iban, bic, amount + ) + } + /** * Frees up resources used by the capture flow. * - * Please provide the required extraction feedback to improve the future extraction accuracy. - * Please follow the recommendations below: + * Please provide the required transfer summary to improve the future extraction accuracy. + * Follow the recommendations below: * - * - Please provide values for all necessary fields, including those that were not extracted. + * - 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.to clean up and provide the extraction values the user has used. * @@ -150,7 +183,13 @@ object GiniBank { * @param iban international bank account * @param bic bank identification code * @param amount accepts extracted amount and currency + * + * @deprecated Use [sendTransferSummary] to provide the required transfer summary first (if the user has completed TAN verification) and then [releaseCapture] to let the SDK free up used resources. */ + @Deprecated( + "Please use sendTransferSummary() to provide the required transfer summary first (if the user has completed TAN verification) and then releaseCapture() to let the SDK free up used resources.", + ReplaceWith("releaseCapture(context)") + ) fun releaseCapture( context: Context, paymentRecipient: String, @@ -159,24 +198,38 @@ object GiniBank { iban: String, bic: String, amount: Amount + ) { + sendTransferSummary( + paymentRecipient, paymentReference, paymentPurpose, iban, bic, amount + ) + releaseCapture(context) + } + + + /** + * Frees up resources used by the capture flow. + * + * @param context Android context + * + */ + fun releaseCapture( + context: Context ) { GiniCapture.cleanup( - context, - paymentRecipient, - paymentReference, - paymentPurpose, - iban, - bic, - amount + context ) captureConfiguration = null giniCapture = null - digitalInvoiceOnboardingNavigationBarBottomAdapter = DefaultDigitalInvoiceOnboardingNavigationBarBottomAdapter() - digitalInvoiceHelpNavigationBarBottomAdapter = DefaultDigitalInvoiceHelpNavigationBarBottomAdapter() + digitalInvoiceOnboardingNavigationBarBottomAdapter = + DefaultDigitalInvoiceOnboardingNavigationBarBottomAdapter() + digitalInvoiceHelpNavigationBarBottomAdapter = + DefaultDigitalInvoiceHelpNavigationBarBottomAdapter() - digitalInvoiceOnboardingIllustrationAdapter = ImageOnboardingIllustrationAdapter(R.drawable.gbs_digital_invoice_list_image, - R.string.gbs_digital_invoice_onboarding_text_1) + digitalInvoiceOnboardingIllustrationAdapter = ImageOnboardingIllustrationAdapter( + R.drawable.gbs_digital_invoice_list_image, + R.string.gbs_digital_invoice_onboarding_text_1 + ) digitalInvoiceNavigationBarBottomAdapter = DefaultDigitalInvoiceNavigationBarBottomAdapter() } @@ -213,16 +266,12 @@ object GiniBank { * @throws IllegalStateException if the capture feature was not configured. */ fun startCaptureFlowForIntent( - resultLauncher: ActivityResultLauncher, - context: Context, - intent: Intent + resultLauncher: ActivityResultLauncher, context: Context, intent: Intent ): CancellationToken { giniCapture.let { capture -> check(capture != null) { "Capture feature is not configured. Call setCaptureConfiguration before starting the flow." } return capture.createIntentForImportedFiles( - intent, - context, - getImportFileCallback(resultLauncher) + intent, context, getImportFileCallback(resultLauncher) ) } } @@ -255,9 +304,9 @@ object GiniBank { return when (val paymentRequestResource = api.documentManager.getPaymentRequest(id)) { is Resource.Cancelled -> throw Exception("Cancelled") is Resource.Error -> throw Exception( - paymentRequestResource.message, - paymentRequestResource.exception + paymentRequestResource.message, paymentRequestResource.exception ) + is Resource.Success -> paymentRequestResource.data } } @@ -275,8 +324,7 @@ object GiniBank { * @throws AmountParsingException If the amount string could not be parsed */ suspend fun resolvePaymentRequest( - requestId: String, - resolvePaymentInput: ResolvePaymentInput + requestId: String, resolvePaymentInput: ResolvePaymentInput ): ResolvedPayment { val api = giniApi check(api != null) { "Gini Api is not set" } @@ -286,9 +334,9 @@ object GiniBank { )) { is Resource.Cancelled -> throw Exception("Cancelled") is Resource.Error -> throw Exception( - resolvedPaymentResource.message, - resolvedPaymentResource.exception + resolvedPaymentResource.message, resolvedPaymentResource.exception ) + is Resource.Success -> resolvedPaymentResource.data } } diff --git a/capture-sdk/default-network/src/androidTest/java/net/gini/android/capture/network/ExtractionFeedbackIntegrationTest.kt b/capture-sdk/default-network/src/androidTest/java/net/gini/android/capture/network/TransferSummaryIntegrationTest.kt similarity index 97% rename from capture-sdk/default-network/src/androidTest/java/net/gini/android/capture/network/ExtractionFeedbackIntegrationTest.kt rename to capture-sdk/default-network/src/androidTest/java/net/gini/android/capture/network/TransferSummaryIntegrationTest.kt index e4d5a283ef..e4da8da0d9 100644 --- a/capture-sdk/default-network/src/androidTest/java/net/gini/android/capture/network/ExtractionFeedbackIntegrationTest.kt +++ b/capture-sdk/default-network/src/androidTest/java/net/gini/android/capture/network/TransferSummaryIntegrationTest.kt @@ -22,7 +22,6 @@ import net.gini.android.capture.network.model.GiniCaptureSpecificExtraction import net.gini.android.capture.network.test.ExtractionsFixture import net.gini.android.capture.network.test.bankAPIDocumentWithId import net.gini.android.capture.network.test.fromJsonAsset -import org.junit.After import org.junit.Assert import org.junit.Before import org.junit.Test @@ -39,7 +38,7 @@ import kotlin.coroutines.resumeWithException */ @RunWith(AndroidJUnit4::class) -class ExtractionFeedbackIntegrationTest { +class TransferSummaryIntegrationTest { private lateinit var networkService: GiniCaptureDefaultNetworkService private lateinit var giniBankAPI: GiniBankAPI @@ -98,12 +97,12 @@ class ExtractionFeedbackIntegrationTest { val amountToPay = extractionsBundle.getParcelable("amountToPay") amountToPay!!.value = "950.00:EUR" - // When releasing capture we need to provide the values the user has used for + // Before cleaning up the capture we need to provide the values the user has used for // creating the transaction. // Supposing the user changed the amountToPay from "995.00:EUR" to "950.00:EUR" // we need to pass in the changed value. For the other extractions we can pass in // the original values since the user did not edit them. - GiniCapture.cleanup(getApplicationContext(), + GiniCapture.sendTransferSummary( extractionsBundle.getParcelable("paymentRecipient")!!.value, "", // Payment reference was not shown to the user and can be left empty extractionsBundle.getParcelable("paymentPurpose")!!.value, @@ -112,6 +111,9 @@ class ExtractionFeedbackIntegrationTest { Amount(BigDecimal("950.00"), AmountCurrency.EUR) ) + // Now we can clean up the capture + GiniCapture.cleanup(getApplicationContext()) + // Wait a little for the feedback sending to complete delay(2_000) diff --git a/capture-sdk/sdk/src/main/java/net/gini/android/capture/GiniCapture.java b/capture-sdk/sdk/src/main/java/net/gini/android/capture/GiniCapture.java index f950b3381b..cc142f3e86 100644 --- a/capture-sdk/sdk/src/main/java/net/gini/android/capture/GiniCapture.java +++ b/capture-sdk/sdk/src/main/java/net/gini/android/capture/GiniCapture.java @@ -1,13 +1,12 @@ package net.gini.android.capture; -import static net.gini.android.capture.internal.util.FileImportValidator.FILE_SIZE_LIMIT; - -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; - import android.content.Context; import android.content.Intent; -import net.gini.android.capture.analysis.AnalysisActivity; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + import net.gini.android.capture.camera.view.CameraNavigationBarBottomAdapter; import net.gini.android.capture.camera.view.DefaultCameraNavigationBarBottomAdapter; import net.gini.android.capture.help.HelpItem; @@ -22,21 +21,15 @@ import net.gini.android.capture.logging.ErrorLoggerListener; import net.gini.android.capture.network.Error; import net.gini.android.capture.network.GiniCaptureNetworkCallback; +import net.gini.android.capture.network.GiniCaptureNetworkService; +import net.gini.android.capture.network.model.GiniCaptureCompoundExtraction; import net.gini.android.capture.network.model.GiniCaptureSpecificExtraction; +import net.gini.android.capture.onboarding.OnboardingPage; import net.gini.android.capture.onboarding.view.DefaultOnboardingNavigationBarBottomAdapter; import net.gini.android.capture.onboarding.view.OnboardingIllustrationAdapter; import net.gini.android.capture.onboarding.view.OnboardingNavigationBarBottomAdapter; import net.gini.android.capture.review.multipage.view.DefaultReviewNavigationBarBottomAdapter; import net.gini.android.capture.review.multipage.view.ReviewNavigationBarBottomAdapter; -import net.gini.android.capture.view.CustomLoadingIndicatorAdapter; -import net.gini.android.capture.view.DefaultLoadingIndicatorAdapter; -import net.gini.android.capture.view.DefaultOnButtonLoadingIndicatorAdapter; -import net.gini.android.capture.view.InjectedViewAdapterInstance; -import net.gini.android.capture.view.NavigationBarTopAdapter; -import net.gini.android.capture.view.DefaultNavigationBarTopAdapter; -import net.gini.android.capture.network.GiniCaptureNetworkService; -import net.gini.android.capture.network.model.GiniCaptureCompoundExtraction; -import net.gini.android.capture.onboarding.OnboardingPage; import net.gini.android.capture.tracking.AnalysisScreenEvent; import net.gini.android.capture.tracking.CameraScreenEvent; import net.gini.android.capture.tracking.Event; @@ -44,6 +37,12 @@ import net.gini.android.capture.tracking.OnboardingScreenEvent; import net.gini.android.capture.tracking.ReviewScreenEvent; import net.gini.android.capture.util.CancellationToken; +import net.gini.android.capture.view.CustomLoadingIndicatorAdapter; +import net.gini.android.capture.view.DefaultLoadingIndicatorAdapter; +import net.gini.android.capture.view.DefaultNavigationBarTopAdapter; +import net.gini.android.capture.view.DefaultOnButtonLoadingIndicatorAdapter; +import net.gini.android.capture.view.InjectedViewAdapterInstance; +import net.gini.android.capture.view.NavigationBarTopAdapter; import net.gini.android.capture.view.OnButtonLoadingIndicatorAdapter; import org.jetbrains.annotations.NotNull; @@ -55,9 +54,9 @@ import java.util.List; import java.util.Map; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static net.gini.android.capture.internal.util.FileImportValidator.FILE_SIZE_LIMIT; /** * Created by Alpar Szotyori on 22.02.2018. @@ -71,14 +70,15 @@ *

To create and configure a singleton instance use the {@link #newInstance(Context)} method and the * returned {@link Builder}. * - *

Use the {@link #cleanup(Context, String, String, String, String, String, Amount)} method to clean up the SDK - * while also providing the required extraction feedback to improve the future extraction accuracy. + *

Please first provide the transfer summary via the {@link #sendTransferSummary(String, String, String, String, String, Amount)} method. + * The transfer summary is used to improve the future extraction accuracy. + * Then, use the {@link #cleanup(Context)} method to clean up the SDK. + * * Please 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.to clean up and provide the extraction values the user has used.
  • + *
  • Send the transfer summary after TAN verification and provide the extraction values the user has used.
  • *
*/ public class GiniCapture { @@ -154,17 +154,16 @@ public static synchronized boolean hasInstance() { * Configure and create a new instance using the returned {@link Builder}. * * @return a new {@link Builder} - * @throws IllegalStateException when an instance already exists. Call {@link #cleanup(Context, String, String, String, String, String, Amount)} + * @throws IllegalStateException when an instance already exists. Call {@link #cleanup(Context)} * before trying to create a new instance * @deprecated Please use {@link #newInstance(Context)} which allows instance recreation without having to - * call {@link #cleanup(Context, String, String, String, String, String, Amount)} first. + * call {@link #cleanup(Context)} first. */ @NonNull @Deprecated public static synchronized Builder newInstance() { if (sInstance != null) { - throw new IllegalStateException("An instance was already created. " - + "Call GiniCapture.cleanup() before creating a new instance."); + throw new IllegalStateException("An instance was already created. Call GiniCapture.cleanup() before creating a new instance."); } return new Builder(); } @@ -172,8 +171,8 @@ public static synchronized Builder newInstance() { /** * Configure and create a new instance using the returned {@link Builder}. * - * @return a new {@link Builder} * @param context Android context + * @return a new {@link Builder} */ @NonNull public static synchronized Builder newInstance(final Context context) { @@ -181,37 +180,31 @@ public static synchronized Builder newInstance(final Context context) { if (sInstance.mNetworkRequestsManager != null) { sInstance.mNetworkRequestsManager.cleanup(); } - doActualCleanUp(context); + cleanup(context); } return new Builder(); } + /** - * Destroys the {@link GiniCapture} instance and frees up used resources. - * - *

Please provide the required extraction feedback to improve the future extraction accuracy. - * Please follow the recommendations below: + * Provides transfer summary to Gini. * + *

Please provide the required transfer summary to improve the future extraction accuracy. + * Follow the recommendations below: *

    - *
  • Please provide values for all necessary fields, including those that were not extracted.
  • + *
  • Make sure to call this method before calling {@link #cleanup(Context)} if the user has completed TAN verification.
  • + *
  • 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.to clean up and provide the extraction values the user has used.
  • + *
  • Send the transfer summary after TAN verification and provide the extraction values the user has used.
  • *
* - * @param context Android context * @param paymentRecipient payment receiver * @param paymentReference ID based on Client ID (Kundennummer) and invoice ID (Rechnungsnummer) - * @param paymentPurpose statement what this payment is for - * @param iban international bank account - * @param bic bank identification code - * @param amount accepts extracted amount and currency + * @param paymentPurpose statement what this payment is for + * @param iban international bank account + * @param bic bank identification code + * @param amount accepts extracted amount and currency */ - public static synchronized void cleanup(@NonNull final Context context, - @NonNull final String paymentRecipient, - @NonNull final String paymentReference, - @NonNull final String paymentPurpose, - @NonNull final String iban, - @NonNull final String bic, - @NonNull final Amount amount) { + public static synchronized void sendTransferSummary(@NonNull final String paymentRecipient, @NonNull final String paymentReference, @NonNull final String paymentPurpose, @NonNull final String iban, @NonNull final String bic, @NonNull final Amount amount) { if (sInstance == null) { return; @@ -219,58 +212,83 @@ public static synchronized void cleanup(@NonNull final Context context, Map extractionMap = new HashMap<>(); - extractionMap.put("amountToPay", new GiniCaptureSpecificExtraction("amountToPay", amount.amountToPay(), - "amount", null, emptyList())); + extractionMap.put("amountToPay", new GiniCaptureSpecificExtraction("amountToPay", amount.amountToPay(), "amount", null, emptyList())); - extractionMap.put("paymentRecipient", new GiniCaptureSpecificExtraction("paymentRecipient", paymentRecipient, - "companyname", null, emptyList())); + extractionMap.put("paymentRecipient", new GiniCaptureSpecificExtraction("paymentRecipient", paymentRecipient, "companyname", null, emptyList())); - extractionMap.put("paymentReference", new GiniCaptureSpecificExtraction("paymentReference", paymentReference, - "reference", null, emptyList())); + extractionMap.put("paymentReference", new GiniCaptureSpecificExtraction("paymentReference", paymentReference, "reference", null, emptyList())); - extractionMap.put("paymentPurpose", new GiniCaptureSpecificExtraction("paymentPurpose", paymentPurpose, - "reference", null, emptyList())); + extractionMap.put("paymentPurpose", new GiniCaptureSpecificExtraction("paymentPurpose", paymentPurpose, "reference", null, emptyList())); - extractionMap.put("iban", new GiniCaptureSpecificExtraction("iban", iban, - "iban", null, emptyList())); + extractionMap.put("iban", new GiniCaptureSpecificExtraction("iban", iban, "iban", null, emptyList())); - extractionMap.put("bic", new GiniCaptureSpecificExtraction("bic", bic, - "bic", null, emptyList())); + extractionMap.put("bic", new GiniCaptureSpecificExtraction("bic", bic, "bic", null, emptyList())); // Test fails here if for some reason mGiniCaptureNetworkService is null // Added null checking to fix test fail -> or figure out something else final GiniCapture oldInstance = sInstance; - if (oldInstance.mGiniCaptureNetworkService != null) - oldInstance.mGiniCaptureNetworkService.sendFeedback(extractionMap, - oldInstance.mInternal.getCompoundExtractions(), new GiniCaptureNetworkCallback() { - @Override - public void failure(Error error) { - if (oldInstance.mNetworkRequestsManager != null) { - oldInstance.mNetworkRequestsManager.cleanup(); - } - } - - @Override - public void success(Void result) { - if (oldInstance.mNetworkRequestsManager != null) { - oldInstance.mNetworkRequestsManager.cleanup(); - } - } - - @Override - public void cancelled() { - if (oldInstance.mNetworkRequestsManager != null) { - oldInstance.mNetworkRequestsManager.cleanup(); - } - } - }); - - doActualCleanUp(context); + if (oldInstance.mGiniCaptureNetworkService != null) { + oldInstance.mGiniCaptureNetworkService.sendFeedback(extractionMap, oldInstance.mInternal.getCompoundExtractions(), new GiniCaptureNetworkCallback() { + @Override + public void failure(Error error) { + if (oldInstance.mNetworkRequestsManager != null) { + oldInstance.mNetworkRequestsManager.cleanup(); + } + } + + @Override + public void success(Void result) { + if (oldInstance.mNetworkRequestsManager != null) { + oldInstance.mNetworkRequestsManager.cleanup(); + } + } + + @Override + public void cancelled() { + if (oldInstance.mNetworkRequestsManager != null) { + oldInstance.mNetworkRequestsManager.cleanup(); + } + } + }); + } + } - private static void doActualCleanUp(Context context) { + /** + * Destroys the {@link GiniCapture} instance and frees up used resources. + * + *

Please provide the required transfer summary to improve the future extraction accuracy. + * Follow the recommendations below: + * + *

    + *
  • 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.to clean up and provide the extraction values the user has used.
  • + *
+ * + * @param context Android context + * @param paymentRecipient payment receiver + * @param paymentReference ID based on Client ID (Kundennummer) and invoice ID (Rechnungsnummer) + * @param paymentPurpose statement what this payment is for + * @param iban international bank account + * @param bic bank identification code + * @param amount accepts extracted amount and currency + * @deprecated Use {@link #sendTransferSummary(String, String, String, String, String, Amount)} to provide the required transfer summary first (if the user has completed TAN verification) and then {@link #cleanup(Context)} to let the SDK free up used resources. + * + */ + + @Deprecated + public static synchronized void cleanup(@NonNull final Context context, @NonNull final String paymentRecipient, @NonNull final String paymentReference, @NonNull final String paymentPurpose, @NonNull final String iban, @NonNull final String bic, @NonNull final Amount amount) { + sendTransferSummary(paymentRecipient, paymentReference, paymentPurpose, iban, bic, amount); + cleanup(context); + } + + /** + * Destroys the {@link GiniCapture} instance and frees up used resources. + */ + public static void cleanup(Context context) { sInstance.mDocumentDataMemoryCache.clear(); sInstance.mPhotoMemoryCache.clear(); sInstance.mInternal.setUpdatedCompoundExtractions(emptyMap()); @@ -296,8 +314,7 @@ private GiniCapture(@NonNull final Builder builder) { mDocumentDataMemoryCache = new DocumentDataMemoryCache(); mPhotoMemoryCache = new PhotoMemoryCache(mDocumentDataMemoryCache); mImageDiskStore = new ImageDiskStore(); - mNetworkRequestsManager = mGiniCaptureNetworkService != null ? new NetworkRequestsManager( - mGiniCaptureNetworkService, mDocumentDataMemoryCache) : null; + mNetworkRequestsManager = mGiniCaptureNetworkService != null ? new NetworkRequestsManager(mGiniCaptureNetworkService, mDocumentDataMemoryCache) : null; mImageMultiPageDocumentMemoryStore = new ImageMultiPageDocumentMemoryStore(); mGiniCaptureFileImport = new GiniCaptureFileImport(this); mInternal = new Internal(this); @@ -307,9 +324,7 @@ private GiniCapture(@NonNull final Builder builder) { mIsFlashOnByDefault = builder.isFlashOnByDefault(); mEventTracker = builder.getEventTracker(); mCustomHelpItems = builder.getCustomHelpItems(); - mErrorLogger = new ErrorLogger(builder.getGiniErrorLoggerIsOn(), - builder.getGiniCaptureNetworkService(), - builder.getCustomErrorLoggerListener()); + mErrorLogger = new ErrorLogger(builder.getGiniErrorLoggerIsOn(), builder.getGiniCaptureNetworkService(), builder.getCustomErrorLoggerListener()); mImportedFileSizeBytesLimit = builder.getImportedFileSizeBytesLimit(); navigationBarTopAdapterInstance = builder.getNavigationBarTopAdapterInstance(); onboardingNavigationBarBottomAdapterInstance = builder.getOnboardingNavigationBarBottomAdapterInstance(); @@ -495,9 +510,7 @@ public boolean isFlashOnByDefault() { * @return a {@link CancellationToken} for cancelling the import process */ @NonNull - public CancellationToken createIntentForImportedFiles(@NonNull final Intent intent, - @NonNull final Context context, - @NonNull final AsyncCallback callback) { + public CancellationToken createIntentForImportedFiles(@NonNull final Intent intent, @NonNull final Context context, @NonNull final AsyncCallback callback) { return mGiniCaptureFileImport.createIntentForImportedFiles(intent, context, callback); } @@ -649,8 +662,7 @@ public EntryPoint getEntryPoint() { public static class Builder { private GiniCaptureNetworkService mGiniCaptureNetworkService; - private DocumentImportEnabledFileTypes mDocumentImportEnabledFileTypes = - DocumentImportEnabledFileTypes.NONE; + private DocumentImportEnabledFileTypes mDocumentImportEnabledFileTypes = DocumentImportEnabledFileTypes.NONE; private boolean mFileImportEnabled; private boolean mQRCodeScanningEnabled; private boolean mOnlyQRCodeScanningEnabled; @@ -660,7 +672,7 @@ public static class Builder { private boolean mMultiPageEnabled; private boolean mIsSupportedFormatsHelpScreenEnabled = true; private boolean mFlashButtonEnabled; - + private boolean mIsFlashOnByDefault = true; private EventTracker mEventTracker = new EventTracker() { @@ -709,10 +721,7 @@ public void build() { private void checkNetworkingImplementations() { if (mGiniCaptureNetworkService == null) { - LOG.warn("GiniCaptureNetworkService instance not set. " - + "Relying on client to perform network calls." - + "You may provide a GiniCaptureNetworkService instance with " - + "GiniCapture.newInstance().setGiniCaptureNetworkService()"); + LOG.warn("GiniCaptureNetworkService instance not set. " + "Relying on client to perform network calls." + "You may provide a GiniCaptureNetworkService instance with " + "GiniCapture.newInstance().setGiniCaptureNetworkService()"); } } @@ -729,8 +738,7 @@ private void checkNetworkingImplementations() { * @return the {@link Builder} instance */ @NonNull - public Builder setShouldShowOnboardingAtFirstRun( - final boolean shouldShowOnboardingAtFirstRun) { + public Builder setShouldShowOnboardingAtFirstRun(final boolean shouldShowOnboardingAtFirstRun) { mShouldShowOnboardingAtFirstRun = shouldShowOnboardingAtFirstRun; return this; } @@ -742,8 +750,7 @@ public Builder setShouldShowOnboardingAtFirstRun( * @return the {@link Builder} instance */ @NonNull - public Builder setCustomOnboardingPages( - @NonNull final ArrayList onboardingPages) { // NOPMD - ArrayList required (Bundle) + public Builder setCustomOnboardingPages(@NonNull final ArrayList onboardingPages) { // NOPMD - ArrayList required (Bundle) mOnboardingPages = onboardingPages; return this; } @@ -804,8 +811,7 @@ GiniCaptureNetworkService getGiniCaptureNetworkService() { * @return the {@link Builder} instance */ @NonNull - public Builder setGiniCaptureNetworkService( - @NonNull final GiniCaptureNetworkService giniCaptureNetworkService) { + public Builder setGiniCaptureNetworkService(@NonNull final GiniCaptureNetworkService giniCaptureNetworkService) { mGiniCaptureNetworkService = giniCaptureNetworkService; return this; } @@ -825,8 +831,7 @@ DocumentImportEnabledFileTypes getDocumentImportEnabledFileTypes() { * @return the {@link Builder} instance */ @NonNull - public Builder setDocumentImportEnabledFileTypes( - @NonNull final DocumentImportEnabledFileTypes documentImportEnabledFileTypes) { + public Builder setDocumentImportEnabledFileTypes(@NonNull final DocumentImportEnabledFileTypes documentImportEnabledFileTypes) { mDocumentImportEnabledFileTypes = documentImportEnabledFileTypes; return this; }