diff --git a/CHANGELOG.md b/CHANGELOG.md index 391da8bfaa..3fd1b6f559 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ [//]: <> (Add changes that not released yet into `Unreleased` section) [//]: <> (Comment `Unreleased` section if there are no changes) [//]: <> (## [Unreleased]) +## [3.3.1] - 2019-09-13 +### Fixed +- There was issue with ConstraintLayout's Flow related to [ClassNotFoundException androidx.constraintlayout.widget.helper.Flow](https://github.com/Adyen/adyen-android/issues/109) fixed +- Created method to save the state of an ActionComponent when the Activity gets destroyed to persist the `paymentData`. ## [3.3.0] - 2019-09-11 ### Added - Created SepaComponent diff --git a/README.md b/README.md index 3fea795bf0..561f6c9d27 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,11 @@ The Components are available through [jcenter][dl], you only need to add the Gra Import the Component module for the Payment Method you want to use by adding it to your `build.gradle` file. For example, for the Drop-in solution you should add: ```groovy -implementation "com.adyen.checkout:drop-in:3.3.0" +implementation "com.adyen.checkout:drop-in:3.3.1" ``` For a Credit Card component you should add: ```groovy -implementation "com.adyen.checkout:card-ui:3.3.0" -``` -For and iDeal component you should add: -```groovy -implementation "com.adyen.checkout:ideal-ui:3.3.0" +implementation "com.adyen.checkout:card-ui:3.3.1" ``` ## Drop-in @@ -115,23 +111,6 @@ cardComponent.observe(this@MainActivity, Observer { }) ``` -### Available Components - -You can find a list of currently available components in the `PaymentMethodTypes.SUPPORTED_PAYMENT_METHODS` with the corresponding payment method type. - -- Credit Card -- iDeal -- Dotpay -- MOLPay -- EPS -- Entercash -- Open banking - -You can also find the components that can handle the `action` object. - -- Redirect -- 3DS2 (Fingerprint and Challenge) - ## ProGuard If you use ProGuard or R8, the following rules should be enough to maintain all expected functionality. diff --git a/README_TEMPLATE.md b/README_TEMPLATE.md index fa131e0430..c2d97764f9 100644 --- a/README_TEMPLATE.md +++ b/README_TEMPLATE.md @@ -19,10 +19,6 @@ For a Credit Card component you should add: ```groovy implementation "com.adyen.checkout:card-ui:" ``` -For and iDeal component you should add: -```groovy -implementation "com.adyen.checkout:ideal-ui:" -``` ## Drop-in @@ -115,23 +111,6 @@ cardComponent.observe(this@MainActivity, Observer { }) ``` -### Available Components - -You can find a list of currently available components in the `PaymentMethodTypes.SUPPORTED_PAYMENT_METHODS` with the corresponding payment method type. - -- Credit Card -- iDeal -- Dotpay -- MOLPay -- EPS -- Entercash -- Open banking - -You can also find the components that can handle the `action` object. - -- Redirect -- 3DS2 (Fingerprint and Challenge) - ## ProGuard If you use ProGuard or R8, the following rules should be enough to maintain all expected functionality. diff --git a/RELEASE_NOTES b/RELEASE_NOTES index d68cc24bd2..4da8d0437a 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,4 +1,3 @@ diff --git a/base-v3/src/main/java/com/adyen/checkout/base/component/BaseActionComponent.java b/base-v3/src/main/java/com/adyen/checkout/base/component/BaseActionComponent.java index 22b31ea738..a0a5712d9e 100644 --- a/base-v3/src/main/java/com/adyen/checkout/base/component/BaseActionComponent.java +++ b/base-v3/src/main/java/com/adyen/checkout/base/component/BaseActionComponent.java @@ -14,7 +14,9 @@ import android.arch.lifecycle.LifecycleOwner; import android.arch.lifecycle.MutableLiveData; import android.arch.lifecycle.Observer; +import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import com.adyen.checkout.base.ActionComponent; import com.adyen.checkout.base.ActionComponentData; @@ -22,12 +24,18 @@ import com.adyen.checkout.base.model.payments.response.Action; import com.adyen.checkout.core.exeption.CheckoutException; import com.adyen.checkout.core.exeption.ComponentException; +import com.adyen.checkout.core.log.LogUtil; +import com.adyen.checkout.core.log.Logger; +import com.adyen.checkout.core.util.StringUtil; import org.json.JSONObject; import java.util.List; public abstract class BaseActionComponent extends AndroidViewModel implements ActionComponent { + private static final String TAG = LogUtil.getTag(); + + private static final String PAYMENT_DATA_KEY = "payment_data"; private final MutableLiveData mResultLiveData = new MutableLiveData<>(); @@ -72,6 +80,31 @@ public void observeErrors(@NonNull LifecycleOwner lifecycleOwner, @NonNull Obser mErrorMutableLiveData.observe(lifecycleOwner, observer); } + /** + * Call this method to save the current data of the Component to the Bundle from {@link Activity#onSaveInstanceState(Bundle)}. + * + * @param bundle The bundle to save the sate into. + */ + public void saveState(@Nullable Bundle bundle) { + if (bundle != null && StringUtil.hasContent(mPaymentData)) { + if (bundle.containsKey(PAYMENT_DATA_KEY)) { + Logger.d(TAG, "bundle already has paymentData, overriding"); + } + bundle.putString(PAYMENT_DATA_KEY, mPaymentData); + } + } + + /** + * Call this method to restore the current data of the Component from the savedInstanceState Bundle from {@link Activity#onCreate(Bundle)}}. + * + * @param bundle The bundle to restore the sate from. + */ + public void restoreState(@Nullable Bundle bundle) { + if (bundle != null && bundle.containsKey(PAYMENT_DATA_KEY) && !StringUtil.hasContent(mPaymentData)) { + mPaymentData = bundle.getString(PAYMENT_DATA_KEY); + } + } + protected abstract void handleActionInternal(@NonNull Activity activity, @NonNull Action action) throws ComponentException; protected void notifyDetails(@NonNull JSONObject details) throws ComponentException { diff --git a/build.gradle b/build.gradle index cdd549cee9..5b9388bc83 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,7 @@ allprojects { // just for example app, don't need to increment ext.version_code = 1 // The version_name format is "major.minor.patch(-(alpha|beta|rc)[0-9]{2}){0,1}" (e.g. 3.0.0, 3.1.1-alpha04 or 3.1.4-rc01 etc). - ext.version_name = "3.3.0" + ext.version_name = "3.3.1" // Code quality ext.version_ktlint = '0.34.2' diff --git a/drop-in/build.gradle b/drop-in/build.gradle index 7f65326529..a38548e5d7 100644 --- a/drop-in/build.gradle +++ b/drop-in/build.gradle @@ -64,7 +64,6 @@ dependencies { // Dependencies implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$version_kotlin" - implementation "com.android.support.constraint:constraint-layout:$version_constraint_layout" implementation "com.android.support:recyclerview-v7:$version_support_library" implementation "com.android.support:design:$version_support_library" } diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/ActionHandler.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/ActionHandler.kt index cbeb107916..60b83068c2 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/ActionHandler.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/ActionHandler.kt @@ -10,6 +10,7 @@ package com.adyen.checkout.dropin import android.arch.lifecycle.Observer import android.net.Uri +import android.os.Bundle import android.support.v4.app.FragmentActivity import com.adyen.checkout.adyen3ds2.Adyen3DS2Component import com.adyen.checkout.base.ActionComponentData @@ -47,6 +48,16 @@ class ActionHandler(activity: FragmentActivity, private val callback: DetailsReq } } + fun saveState(bundle: Bundle?) { + redirectComponent.saveState(bundle) + adyen3DS2Component.saveState(bundle) + } + + fun restoreState(bundle: Bundle?) { + redirectComponent.restoreState(bundle) + adyen3DS2Component.restoreState(bundle) + } + fun handleAction(activity: FragmentActivity, action: Action, sendResult: (String) -> Unit) { when { redirectComponent.canHandleAction(action) -> { diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/ui/LoadingActivity.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/ui/LoadingActivity.kt index cbd26ab701..f05bf2608b 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/ui/LoadingActivity.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/ui/LoadingActivity.kt @@ -117,6 +117,7 @@ class LoadingActivity : AppCompatActivity(), ActionHandler.DetailsRequestedInter } actionHandler = ActionHandler(this, this) + actionHandler.restoreState(savedInstanceState) if (savedInstanceState == null) { handleIntent(intent) @@ -134,6 +135,11 @@ class LoadingActivity : AppCompatActivity(), ActionHandler.DetailsRequestedInter } } + override fun onSaveInstanceState(outState: Bundle?) { + actionHandler.saveState(outState) + super.onSaveInstanceState(outState) + } + override fun onPause() { super.onPause() Logger.d(TAG, "onPause") diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/ui/component/ComponentDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/ui/component/ComponentDialogFragment.kt deleted file mode 100644 index 207ed7c22d..0000000000 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/ui/component/ComponentDialogFragment.kt +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2019 Adyen N.V. - * - * This file is open source and available under the MIT license. See the LICENSE file for more info. - * - * Created by arman on 2/7/2019. - */ - -package com.adyen.checkout.dropin.ui.component - -import android.arch.lifecycle.Observer -import android.content.DialogInterface -import android.os.Bundle -import android.support.design.widget.BottomSheetBehavior -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import com.adyen.checkout.base.ComponentError -import com.adyen.checkout.base.ComponentView -import com.adyen.checkout.base.PaymentComponent -import com.adyen.checkout.base.PaymentComponentState -import com.adyen.checkout.base.model.paymentmethods.PaymentMethod -import com.adyen.checkout.base.model.payments.request.PaymentMethodDetails -import com.adyen.checkout.core.exeption.CheckoutException -import com.adyen.checkout.core.log.LogUtil -import com.adyen.checkout.core.log.Logger -import com.adyen.checkout.dropin.DropInConfiguration -import com.adyen.checkout.dropin.R -import com.adyen.checkout.dropin.getComponentFor -import com.adyen.checkout.dropin.getViewFor -import com.adyen.checkout.dropin.ui.base.DropInBottomSheetDialogFragment -import kotlinx.android.synthetic.main.fragmentdialog_component.componentContainer -import kotlinx.android.synthetic.main.fragmentdialog_component.payButton -import kotlinx.android.synthetic.main.fragmentdialog_header.view.header - -class ComponentDialogFragment : DropInBottomSheetDialogFragment(), Observer> { - - companion object { - private val TAG = LogUtil.getTag() - - private const val PAYMENT_METHOD = "PAYMENT_METHOD" - private const val WAS_IN_EXPAND_STATUS = "WAS_IN_EXPAND_STATUS" - private const val DROP_IN_CONFIGURATION = "DROP_IN_CONFIGURATION" - - fun newInstance(paymentMethod: PaymentMethod, dropInConfiguration: DropInConfiguration, wasInExpandStatus: Boolean): ComponentDialogFragment { - val args = Bundle() - args.putParcelable(PAYMENT_METHOD, paymentMethod) - args.putBoolean(WAS_IN_EXPAND_STATUS, wasInExpandStatus) - args.putParcelable(DROP_IN_CONFIGURATION, dropInConfiguration) - - val componentDialogFragment = ComponentDialogFragment() - componentDialogFragment.arguments = args - - return componentDialogFragment - } - } - - private lateinit var paymentMethod: PaymentMethod - private lateinit var dropInConfiguration: DropInConfiguration - private lateinit var component: PaymentComponent - private lateinit var componentView: ComponentView - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - paymentMethod = arguments?.getParcelable(PAYMENT_METHOD) ?: throw IllegalArgumentException("Payment method is null") - dropInConfiguration = arguments?.getParcelable(DROP_IN_CONFIGURATION) ?: throw IllegalArgumentException("DropIn Configuration is null") - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - Logger.d(TAG, "onCreateView") - return inflater.inflate(R.layout.fragmentdialog_component, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - Logger.d(TAG, "onViewCreated") - super.onViewCreated(view, savedInstanceState) - attachComponent(paymentMethod) - view.header.setText(R.string.credit_card) - } - - override fun onBackPressed(): Boolean { - Logger.d(TAG, "onBackPressed") - protocol.showPaymentMethodsDialog(arguments?.getBoolean(WAS_IN_EXPAND_STATUS, false)!!) - return true - } - - override fun onCancel(dialog: DialogInterface?) { - super.onCancel(dialog) - Logger.d(TAG, "onCancel") - protocol.terminateDropIn() - } - - override fun onChanged(paymentComponentState: PaymentComponentState?) { - if (componentView.isConfirmationRequired) { - @Suppress("UsePropertyAccessSyntax") - payButton.isEnabled = paymentComponentState != null && paymentComponentState.isValid() - } else { - startPayment() - } - } - - private fun attachComponent(paymentMethod: PaymentMethod) { - try { - component = getComponentFor(this, paymentMethod, dropInConfiguration) - componentView = getViewFor(requireContext(), paymentMethod) - } catch (e: CheckoutException) { - handleError(ComponentError(e)) - return - } - - component.observe(this, this) - component.observeErrors(this, createErrorHandlerObserver()) - componentContainer.addView(componentView as View) - componentView.attach(component, this) - - if (componentView.isConfirmationRequired) { - payButton.setOnClickListener { - startPayment() - } - - setInitViewState(BottomSheetBehavior.STATE_EXPANDED) - (componentView as View).requestFocus() - } else { - payButton.visibility = View.GONE - } - } - - private fun startPayment() { - val componentState = component.state - try { - if (componentState != null) { - if (componentState.isValid) { - protocol.sendPaymentRequest(componentState.data) - } else { - throw CheckoutException("PaymentComponentState are not valid.") - } - } else { - throw CheckoutException("PaymentComponentState are null.") - } - } catch (e: CheckoutException) { - handleError(ComponentError(e)) - } - } - - private fun createErrorHandlerObserver(): Observer { - return Observer { - if (it != null) { - handleError(it) - } - } - } - - private fun handleError(componentError: ComponentError) { - Logger.e(TAG, componentError.errorMessage) - Toast.makeText(context, R.string.component_error, Toast.LENGTH_LONG).show() - - protocol.terminateDropIn() - } -} diff --git a/drop-in/src/main/res/layout/view_card_component_dropin.xml b/drop-in/src/main/res/layout/view_card_component_dropin.xml index f2e21f90fd..e119acb1f1 100644 --- a/drop-in/src/main/res/layout/view_card_component_dropin.xml +++ b/drop-in/src/main/res/layout/view_card_component_dropin.xml @@ -15,15 +15,13 @@ - + android:paddingBottom="@dimen/standard_half_margin"> - - - +