diff --git a/MIGRATING.md b/MIGRATING.md index a6f125191..ecf3285ad 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -1,5 +1,28 @@ ## Migration Guides +### Migrating from versions < v1.7.1 +Function `addRule` is now deprecated. It's still can be used, but **Android Studio** will highlight it. + +**Before:** + +``` +view.addRule( + VGSInfoRule.ValidationBuilder() + .setRegex("^[0-9]{5}(?:-[0-9]{4})?\$") + .build() +) +``` + +**Now:** + +``` +view.setRule( + VGSInfoRule.ValidationBuilder() + .setRegex("^[0-9]{5}(?:-[0-9]{4})?\$") + .build() +) +``` + ### Migrating from versions < v1.6.14 Field state will be valid if no validation rules defined. diff --git a/app/build.gradle b/app/build.gradle index ec5422e0d..24fb74dc6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,6 +20,7 @@ android { buildConfigField("String", "VAULT_ID", "\"${getLocalProperty("VGS_VAULT_ID")}\"") buildConfigField("String", "PATH", "\"${getLocalProperty("VGS_PATH")}\"") + buildConfigField("String", "ROUTE_ID", "\"${getLocalProperty("VGS_ROUTE_ID")}\"") } buildTypes { @@ -53,6 +54,11 @@ android { } } +repositories { + + maven { url "https://jitpack.io" } +} + dependencies { implementation project(":vgscollect") implementation project(":vgscollect-cardio") @@ -61,9 +67,11 @@ dependencies { implementation libs.androidx.core.ktx implementation libs.androidx.constraintlayout implementation libs.androidx.multidex - implementation libs.material + implementation 'androidx.preference:preference-ktx:1.2.0' + implementation 'com.github.kbiakov:CodeView-Android:1.3.2' + debugImplementation debugLibs.leakcanary testImplementation testLibs.junit diff --git a/app/src/androidTest/java/com/verygoodsecurity/demoapp/tests/flows/ActivityCaseInstrumentedTest.kt b/app/src/androidTest/java/com/verygoodsecurity/demoapp/tests/flows/ActivityCaseInstrumentedTest.kt index 2c6820f69..591645165 100644 --- a/app/src/androidTest/java/com/verygoodsecurity/demoapp/tests/flows/ActivityCaseInstrumentedTest.kt +++ b/app/src/androidTest/java/com/verygoodsecurity/demoapp/tests/flows/ActivityCaseInstrumentedTest.kt @@ -8,7 +8,7 @@ import androidx.test.espresso.ViewInteraction import androidx.test.espresso.action.ViewActions.* import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.intent.Intents.* -import androidx.test.espresso.intent.matcher.IntentMatchers.* +import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent import androidx.test.espresso.matcher.ViewMatchers.* import androidx.test.ext.junit.rules.activityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -196,14 +196,18 @@ class ActivityCaseInstrumentedTest { } private fun startMainScreen() { - val startWithActivityBtn = onView(withId(R.id.startWithActivityBtn)) - .check(matches(isDisplayed())) - - onView(withId(R.id.userVault)).perform( + onView(withId(R.id.tiedVaultId)).perform( typeText(Utils.DEFAULT_TENANT_ID), closeSoftKeyboard() ) - onView(withId(R.id.userPath)).perform(typeText(Utils.DEFAULT_PATH), closeSoftKeyboard()) + onView(withId(R.id.tiedPath)).perform( + typeText(Utils.DEFAULT_PATH), + closeSoftKeyboard() + ) + + val startWithActivityBtn = onView(withId(R.id.llCollectActivityFlow)) + .perform(scrollTo()) + .check(matches(isDisplayed())) performClick(startWithActivityBtn) } diff --git a/app/src/androidTest/java/com/verygoodsecurity/demoapp/tests/flows/FragmentCaseInstrumentedTest.kt b/app/src/androidTest/java/com/verygoodsecurity/demoapp/tests/flows/FragmentCaseInstrumentedTest.kt index f7fe759f6..ff5f6ecc0 100644 --- a/app/src/androidTest/java/com/verygoodsecurity/demoapp/tests/flows/FragmentCaseInstrumentedTest.kt +++ b/app/src/androidTest/java/com/verygoodsecurity/demoapp/tests/flows/FragmentCaseInstrumentedTest.kt @@ -211,11 +211,12 @@ class FragmentCaseInstrumentedTest { } private fun startMainScreen() { - val startWithActivityBtn = onView(withId(R.id.startWithFragmentBtn)) - .check(matches(isDisplayed())) + onView(withId(R.id.tiedVaultId)).perform(typeText(Utils.DEFAULT_TENANT_ID)) + onView(withId(R.id.tiedPath)).perform(typeText(Utils.DEFAULT_PATH), closeSoftKeyboard()) - onView(withId(R.id.userVault)).perform(typeText(Utils.DEFAULT_TENANT_ID)) - onView(withId(R.id.userPath)).perform(typeText(Utils.DEFAULT_PATH), closeSoftKeyboard()) + val startWithActivityBtn = onView(withId(R.id.llCollectFragmentFlow)) + .perform(scrollTo()) + .check(matches(isDisplayed())) startWithActivityBtn.perform(click()) } diff --git a/app/src/androidTest/java/com/verygoodsecurity/demoapp/tests/flows/ViewPagerCaseInstrumentedTest.kt b/app/src/androidTest/java/com/verygoodsecurity/demoapp/tests/flows/ViewPagerCaseInstrumentedTest.kt index 93d97f3c8..6ec5ff367 100644 --- a/app/src/androidTest/java/com/verygoodsecurity/demoapp/tests/flows/ViewPagerCaseInstrumentedTest.kt +++ b/app/src/androidTest/java/com/verygoodsecurity/demoapp/tests/flows/ViewPagerCaseInstrumentedTest.kt @@ -4,6 +4,7 @@ import android.widget.DatePicker import androidx.test.espresso.Espresso.onView import androidx.test.espresso.ViewInteraction import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.scrollTo import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.PickerActions import androidx.test.espresso.matcher.ViewMatchers.* @@ -179,7 +180,8 @@ class ViewPagerCaseInstrumentedTest { } private fun startMainScreen() { - val startWithActivityBtn = onView(withId(R.id.startWithViewPagerBtn)) + val startWithActivityBtn = onView(withId(R.id.llCollectViewPagerFlow)) + .perform(scrollTo()) .check(matches(isDisplayed())) startWithActivityBtn.perform(click()) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1b331b287..d285e5474 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,7 +11,8 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/AppTheme"> + android:theme="@style/AppTheme" + tools:replace="android:allowBackup"> - + android:label="Collect (Activity)" /> + + + + + + android:label="Collect (Fragment)" /> + android:label="Collect (ViewPager)" /> by lazy { - val envArr = arrayOf( - Environment.SANDBOX.rawValue.toUpperCase(), - Environment.LIVE.rawValue.toUpperCase() - ) - val layout = android.R.layout.simple_spinner_item - val spinnerArrayAdapter = ArrayAdapter(this, layout, envArr) - - spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - - spinnerArrayAdapter - } +class StartActivity : AppCompatActivity(R.layout.activity_start) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_start) - - setupSpinner() - setupUI() - - startWithActivityBtn?.setOnClickListener(this) - startWithFragmentBtn?.setOnClickListener(this) - startWithViewPagerBtn?.setOnClickListener(this) - } - - private fun setupUI() { - userVault?.setText(BuildConfig.VAULT_ID) - userPath?.setText(BuildConfig.PATH) - - environmentSpinner.setSelection(BuildConfig.ENVIRINMENT.ordinal) + setupViews() } - private fun setupSpinner() { - environmentSpinner.adapter = spinnerAdapter - } - - override fun onClick(v: View) { - when(v.id) { - R.id.startWithActivityBtn -> startInteractionWithActivity() - R.id.startWithFragmentBtn -> startInteractionWithFragment() - R.id.startWithViewPagerBtn -> startInteractionWithViewPager() + private fun setupViews() { + tiedVaultId?.setText(BuildConfig.VAULT_ID) + tiedPath?.setText(BuildConfig.PATH) + llTokenizationFlow?.setOnClickListener { + startActivity(TokenizationActivity::class.java) + } + llCollectActivityFlow?.setOnClickListener { + startActivity(VGSCollectActivity::class.java) + } + llCollectFragmentFlow?.setOnClickListener { + startActivity(VGSCollectFragmentActivity::class.java) + } + llCollectViewPagerFlow?.setOnClickListener { + startActivity(VGSViewPagerActivity::class.java) } } - private fun startInteractionWithActivity() { - val intent = prepareIntent(VGSCollectActivity::class.java) - startActivity(intent) + private fun startActivity(activity: Class) { + startActivity(Intent(this, activity).apply { + putExtra(KEY_BUNDLE_VAULT_ID, tiedVaultId.text.toString()) + putExtra(KEY_BUNDLE_PATH, tiedPath.text.toString()) + putExtra(KEY_BUNDLE_ENVIRONMENT, getEnvironment()) + }) } - private fun startInteractionWithFragment() { - val intent = prepareIntent(VGSCollectFragmentActivity::class.java) - startActivity(intent) + private fun getEnvironment() = when (mbGroupEnvironment.checkedButtonId) { + R.id.mbSandbox -> SANDBOX + R.id.mbLive -> LIVE + else -> throw IllegalArgumentException("Not implemented") } - private fun startInteractionWithViewPager() { - val intent = prepareIntent(VGSViewPagerActivity::class.java) - startActivity(intent) - } + companion object { - private fun prepareIntent(componentClass: Class):Intent { - return Intent(this, componentClass).apply { - val vaultId = userVault.text.toString() - val path = userPath.text.toString() - val env = environmentSpinner.selectedItemPosition + private const val SANDBOX = "sandbox" + private const val LIVE = "live" - putExtra(VAULT_ID, vaultId) - putExtra(ENVIROMENT, env) - putExtra(PATH, path) - } + const val KEY_BUNDLE_VAULT_ID = "user_vault_id" + const val KEY_BUNDLE_ENVIRONMENT = "user_env" + const val KEY_BUNDLE_PATH = "user_path" } } \ No newline at end of file diff --git a/app/src/main/java/com/verygoodsecurity/demoapp/activity_case/VGSCollectActivity.kt b/app/src/main/java/com/verygoodsecurity/demoapp/activity_case/VGSCollectActivity.kt index 9a2ba22c0..ca6056bf2 100644 --- a/app/src/main/java/com/verygoodsecurity/demoapp/activity_case/VGSCollectActivity.kt +++ b/app/src/main/java/com/verygoodsecurity/demoapp/activity_case/VGSCollectActivity.kt @@ -143,7 +143,7 @@ class VGSCollectActivity : AppCompatActivity(), VgsCollectResponseListener, View .build() - cardNumberField.addRule(rule) + cardNumberField.setRule(rule) } private fun addCustomBrands() { @@ -229,14 +229,14 @@ class VGSCollectActivity : AppCompatActivity(), VgsCollectResponseListener, View } private fun retrieveSettings() { - VGSCollectLogger.logLevel = VGSCollectLogger.Level.WARN + VGSCollectLogger.logLevel = VGSCollectLogger.Level.DEBUG val bndl = intent?.extras - vault_id = bndl?.getString(StartActivity.VAULT_ID, "") ?: "" - path = bndl?.getString(StartActivity.PATH, "/") ?: "" + vault_id = bndl?.getString(StartActivity.KEY_BUNDLE_VAULT_ID, "") ?: "" + path = bndl?.getString(StartActivity.KEY_BUNDLE_PATH, "/") ?: "" - val envId = bndl?.getInt(StartActivity.ENVIROMENT, 0) ?: 0 + val envId = bndl?.getInt(StartActivity.KEY_BUNDLE_ENVIRONMENT, 0) ?: 0 env = Environment.values()[envId] vgsForm = VGSCollect.Builder(this, vault_id) @@ -329,6 +329,7 @@ class VGSCollectActivity : AppCompatActivity(), VgsCollectResponseListener, View is VGSResponse.SuccessResponse -> responseContainerView.text = "Code: ${response.successCode}" is VGSResponse.ErrorResponse -> responseContainerView.text = response.toString() + else -> return } } diff --git a/app/src/main/java/com/verygoodsecurity/demoapp/fragment_case/PaymentFragment.kt b/app/src/main/java/com/verygoodsecurity/demoapp/fragment_case/PaymentFragment.kt index 799ee53e6..58436cce0 100644 --- a/app/src/main/java/com/verygoodsecurity/demoapp/fragment_case/PaymentFragment.kt +++ b/app/src/main/java/com/verygoodsecurity/demoapp/fragment_case/PaymentFragment.kt @@ -26,7 +26,7 @@ import com.verygoodsecurity.vgscollect.widget.PersonNameEditText import com.verygoodsecurity.vgscollect.widget.VGSCardNumberEditText import kotlinx.android.synthetic.main.activity_collect_demo.* -class PaymentFragment: Fragment(), VgsCollectResponseListener, OnFieldStateChangeListener, +class PaymentFragment : Fragment(), VgsCollectResponseListener, OnFieldStateChangeListener, View.OnClickListener { companion object { @@ -35,8 +35,8 @@ class PaymentFragment: Fragment(), VgsCollectResponseListener, OnFieldStateChang const val PATH = "path" } - private lateinit var vault_id:String - private lateinit var path:String + private lateinit var vault_id: String + private lateinit var path: String private lateinit var env: Environment private lateinit var vgsForm: VGSCollect @@ -46,10 +46,10 @@ class PaymentFragment: Fragment(), VgsCollectResponseListener, OnFieldStateChang private var cardHolderField: InputFieldView? = null private var cardExpDateField: InputFieldView? = null - private var responseContainerView:TextView? = null - private var stateContainerView:TextView? = null - private var previewCardNumber:TextView? = null - private var previewCardBrand:ImageView? = null + private var responseContainerView: TextView? = null + private var stateContainerView: TextView? = null + private var previewCardNumber: TextView? = null + private var previewCardBrand: ImageView? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -69,7 +69,7 @@ class PaymentFragment: Fragment(), VgsCollectResponseListener, OnFieldStateChang } override fun onOptionsItemSelected(item: MenuItem): Boolean { - when(item.itemId) { + when (item.itemId) { R.id.scan_card -> scanCard() R.id.details_item -> addDetailsFragment() else -> return super.onOptionsItemSelected(item) @@ -96,7 +96,8 @@ class PaymentFragment: Fragment(), VgsCollectResponseListener, OnFieldStateChang intent.putExtra(ScanActivity.SCAN_CONFIGURATION, scanSettings) - startActivityForResult(intent, + startActivityForResult( + intent, VGSCollectFragmentActivity.USER_SCAN_REQUEST_CODE ) } @@ -104,7 +105,7 @@ class PaymentFragment: Fragment(), VgsCollectResponseListener, OnFieldStateChang private fun retrieveAttributes() { arguments?.let { vault_id = it.getString(VAULT_ID, "") - path = it.getString(PATH,"/") + path = it.getString(PATH, "/") val envId = it.getInt(ENVIROMENT, 0) env = Environment.values()[envId] @@ -135,10 +136,10 @@ class PaymentFragment: Fragment(), VgsCollectResponseListener, OnFieldStateChang cardExpDateField = view?.findViewById(R.id.cardExpDateField) vgsForm.bindView(cardExpDateField) - responseContainerView = view?.findViewById(R.id.responseContainerView) - stateContainerView = view?.findViewById(R.id.stateContainerView) - previewCardNumber = view?.findViewById(R.id.previewCardNumber) - previewCardBrand = view?.findViewById(R.id.previewCardBrand) + responseContainerView = view?.findViewById(R.id.responseContainerView) + stateContainerView = view?.findViewById(R.id.stateContainerView) + previewCardNumber = view?.findViewById(R.id.previewCardNumber) + previewCardBrand = view?.findViewById(R.id.previewCardBrand) view?.findViewById(R.id.submitBtn)?.setOnClickListener(this) view?.findViewById(R.id.attachBtn)?.setOnClickListener(this) @@ -161,19 +162,18 @@ class PaymentFragment: Fragment(), VgsCollectResponseListener, OnFieldStateChang when (response) { is VGSResponse.SuccessResponse -> responseContainerView?.text = response.toString() is VGSResponse.ErrorResponse -> responseContainerView?.text = response.toString() + else -> return } } override fun onStateChange(state: FieldState) { - when(state) { - is FieldState.CardNumberState -> handleCardNumberState(state) - } + if (state is FieldState.CardNumberState) handleCardNumberState(state) refreshAllStates() } private fun handleCardNumberState(state: FieldState.CardNumberState) { previewCardNumber?.text = state.number - if(state.cardBrand == CardType.VISA.name) { + if (state.cardBrand == CardType.VISA.name) { previewCardBrand?.setImageResource(R.drawable.ic_custom_visa) } else { previewCardBrand?.setImageResource(state.drawableBrandResId) @@ -190,7 +190,7 @@ class PaymentFragment: Fragment(), VgsCollectResponseListener, OnFieldStateChang } override fun onClick(v: View) { - when(v.id) { + when (v.id) { R.id.attachBtn -> attachFile() R.id.submitBtn -> submitData() } @@ -216,23 +216,27 @@ class PaymentFragment: Fragment(), VgsCollectResponseListener, OnFieldStateChang vgsForm.asyncSubmit(request) } - private fun setEnabledResponseHeader(isEnabled:Boolean) { - if(isEnabled) { + private fun setEnabledResponseHeader(isEnabled: Boolean) { + if (isEnabled) { attachBtn.setTextColor( - ContextCompat.getColor(requireActivity(), - R.color.state_active - )) + ContextCompat.getColor( + requireActivity(), + R.color.state_active + ) + ) } else { responseContainerView?.text = "" attachBtn.setTextColor( - ContextCompat.getColor(requireActivity(), - R.color.state_unactive - )) + ContextCompat.getColor( + requireActivity(), + R.color.state_unactive + ) + ) } } - private fun setStateLoading(state:Boolean) { - if(state) { + private fun setStateLoading(state: Boolean) { + if (state) { progressBar?.visibility = View.VISIBLE submitBtn?.isEnabled = false attachBtn?.isEnabled = false @@ -244,7 +248,7 @@ class PaymentFragment: Fragment(), VgsCollectResponseListener, OnFieldStateChang } private fun attachFile() { - if(vgsForm.getFileProvider().getAttachedFiles().isEmpty()) { + if (vgsForm.getFileProvider().getAttachedFiles().isEmpty()) { vgsForm.getFileProvider().attachFile("attachments.file") } else { vgsForm.getFileProvider().detachAll() diff --git a/app/src/main/java/com/verygoodsecurity/demoapp/fragment_case/VGSCollectFragmentActivity.kt b/app/src/main/java/com/verygoodsecurity/demoapp/fragment_case/VGSCollectFragmentActivity.kt index acad27129..6e5b84d31 100644 --- a/app/src/main/java/com/verygoodsecurity/demoapp/fragment_case/VGSCollectFragmentActivity.kt +++ b/app/src/main/java/com/verygoodsecurity/demoapp/fragment_case/VGSCollectFragmentActivity.kt @@ -42,9 +42,9 @@ class VGSCollectFragmentActivity: AppCompatActivity() { private fun retrieveSettings() { val bndl = intent?.extras - vault_id = bndl?.getString(StartActivity.VAULT_ID, "")?:"" - path = bndl?.getString(StartActivity.PATH,"/")?:"" + vault_id = bndl?.getString(StartActivity.KEY_BUNDLE_VAULT_ID, "")?:"" + path = bndl?.getString(StartActivity.KEY_BUNDLE_PATH,"/")?:"" - envId = bndl?.getInt(StartActivity.ENVIROMENT, 0)?:0 + envId = bndl?.getInt(StartActivity.KEY_BUNDLE_ENVIRONMENT, 0)?:0 } } \ No newline at end of file diff --git a/app/src/main/java/com/verygoodsecurity/demoapp/tokenization/TokenizationActivity.kt b/app/src/main/java/com/verygoodsecurity/demoapp/tokenization/TokenizationActivity.kt new file mode 100644 index 000000000..55229e397 --- /dev/null +++ b/app/src/main/java/com/verygoodsecurity/demoapp/tokenization/TokenizationActivity.kt @@ -0,0 +1,321 @@ +package com.verygoodsecurity.demoapp.tokenization + +import android.animation.LayoutTransition +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.util.Log +import android.view.Menu +import android.view.MenuItem +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.view.isVisible +import androidx.preference.PreferenceManager +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.textview.MaterialTextView +import com.verygoodsecurity.api.cardio.ScanActivity +import com.verygoodsecurity.demoapp.R +import com.verygoodsecurity.demoapp.StartActivity +import com.verygoodsecurity.demoapp.activity_case.VGSCollectActivity +import com.verygoodsecurity.demoapp.tokenization.settings.TokenizationSettingsActivity +import com.verygoodsecurity.vgscollect.core.VGSCollect +import com.verygoodsecurity.vgscollect.core.VgsCollectResponseListener +import com.verygoodsecurity.vgscollect.core.model.network.VGSResponse +import com.verygoodsecurity.vgscollect.core.model.state.tokenization.VGSVaultAliasFormat +import com.verygoodsecurity.vgscollect.core.model.state.tokenization.VGSVaultStorageType +import com.verygoodsecurity.vgscollect.view.InputFieldView +import com.verygoodsecurity.vgscollect.widget.VGSTextInputLayout +import io.github.kbiakov.codeview.adapters.Options +import io.github.kbiakov.codeview.highlight.ColorThemeData +import io.github.kbiakov.codeview.highlight.SyntaxColors +import kotlinx.android.synthetic.main.activity_tokenization.* +import kotlinx.android.synthetic.main.code_example_layout.* +import org.json.JSONObject +import kotlin.properties.Delegates + +class TokenizationActivity : + AppCompatActivity(R.layout.activity_tokenization), + InputFieldView.OnTextChangedListener, VgsCollectResponseListener { + + private val defaultHintTextColor by lazy { ContextCompat.getColor(this, R.color.fiord) } + private val defaultInputBackgroundColor by lazy { + ContextCompat.getColor(this, R.color.fiord_20) + } + private val errorHintTextColor by lazy { ContextCompat.getColor(this, R.color.brown) } + private val errorInputBackgroundColor by lazy { + ContextCompat.getColor(this, R.color.vanillaIce) + } + + private var collect: VGSCollect? = null + + private var response: String? by Delegates.observable(null) { _, _, new -> + mbReset.isVisible = !new.isNullOrBlank() + updateCodeExample(response) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + initCollect() + initViews() + } + + override fun onResume() { + super.onResume() + configureTokenization() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.tokenization_menu, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.scan -> { + scanCard() + true + } + R.id.settings -> { + openSettings() + true + } + else -> super.onOptionsItemSelected(item) + } + } + + @Deprecated("Deprecated in Java") + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + @Suppress("DEPRECATION") + super.onActivityResult(requestCode, resultCode, data) + collect?.onActivityResult(requestCode, resultCode, data) + } + + override fun onDestroy() { + super.onDestroy() + collect?.onDestroy() + collect = null + } + + override fun onTextChange(view: InputFieldView, isEmpty: Boolean) { + val (title, layout) = (when (view.id) { + R.id.vgsTiedCardHolder -> mtvCardHolderHint to vgsTilCardHolder + R.id.vgsTiedCardNumber -> mtvCardNumberHint to vgsTilCardNumber + R.id.vgsTiedExpiry -> mtvExpiryHint to vgsTilExpiry + R.id.vgsTiedCvc -> mtvCvcHint to vgsTilCvc + else -> throw IllegalArgumentException("Not implemented.") + }) + setInputValid(title, layout) + } + + override fun onResponse(response: VGSResponse?) { + Log.d(this::class.java.simpleName, response.toString()) + setLoading(false) + this.response = response?.body + if (response is VGSResponse.ErrorResponse) { + showSnackBar("Code: ${response.code}, ${response.localizeMessage}") + } + } + + private fun initCollect() { + with(intent?.extras) { + collect = VGSCollect( + this@TokenizationActivity, + this?.getString(StartActivity.KEY_BUNDLE_VAULT_ID) ?: "", + this?.getString(StartActivity.KEY_BUNDLE_ENVIRONMENT) ?: "" + ) + collect?.addOnResponseListeners(this@TokenizationActivity) + } + } + + private fun initViews() { + clRoot.layoutTransition.enableTransitionType(LayoutTransition.CHANGING) + bindViews() + configureTokenization() + initTextChangeListener() + initClickListeners() + initCodeExampleView() + updateCodeExample(null) + } + + private fun bindViews() { + collect?.bindView(vgsTiedCardHolder) + collect?.bindView(vgsTiedCardNumber) + collect?.bindView(vgsTiedExpiry) + collect?.bindView(vgsTiedCvc) + } + + private fun configureTokenization() { + val preferences = PreferenceManager.getDefaultSharedPreferences(this) + vgsTiedCardHolder.setEnabledTokenization( + preferences.getBoolean( + getString(R.string.tokenization_card_holder_enabled_key), + true + ) + ) + preferences.getString(getString(R.string.tokenization_card_holder_storage_key), null)?.let { + vgsTiedCardHolder.setVaultStorageType(parseStorage(it)) + } + preferences.getString(getString(R.string.tokenization_card_holder_alias_format_key), null) + ?.let { + vgsTiedCardHolder.setVaultAliasFormat(parseAliasFormat(it)) + } + + preferences.getString(getString(R.string.tokenization_card_number_alias_format_key), null) + ?.let { + vgsTiedCardNumber.setVaultAliasFormat(parseAliasFormat(it)) + } + + vgsTiedExpiry.setEnabledTokenization( + preferences.getBoolean( + getString(R.string.tokenization_expiry_enabled_key), + true + ) + ) + preferences.getString(getString(R.string.tokenization_expiry_storage_key), null)?.let { + vgsTiedExpiry.setVaultStorageType(parseStorage(it)) + } + preferences.getString(getString(R.string.tokenization_expiry_alias_format_key), null) + ?.let { + vgsTiedExpiry.setVaultAliasFormat(parseAliasFormat(it)) + } + } + + private fun initTextChangeListener() { + vgsTiedCardHolder.addOnTextChangeListener(this) + vgsTiedCardNumber.addOnTextChangeListener(this) + vgsTiedExpiry.addOnTextChangeListener(this) + vgsTiedCvc.addOnTextChangeListener(this) + } + + private fun initClickListeners() { + mbTokenize.setOnClickListener { + runIfInputsValid { + tokenize() + } + } + ivCopyCodeExample?.setOnClickListener { copyResponseToClipboard() } + mbReset.setOnClickListener { resetView() } + } + + private fun initCodeExampleView() { + val syntaxColor = ContextCompat.getColor(this, R.color.veryLightGray) + val bgColor = ContextCompat.getColor(this, R.color.blackPearl) + val lineNumberColor = ContextCompat.getColor(this, R.color.nobel) + cvResponse.setOptions( + Options( + context = this.applicationContext, + theme = ColorThemeData( + SyntaxColors( + string = syntaxColor, + punctuation = syntaxColor, + ), + numColor = lineNumberColor, + bgContent = bgColor, + bgNum = bgColor, + noteColor = syntaxColor, + ) + ) + ) + cvResponse.alpha = 1f + } + + private fun updateCodeExample(response: String?) { + cvResponse.setCode(formatJson(response)) + } + + private fun tokenize() { + setLoading(true) + collect?.tokenize() + } + + private fun copyResponseToClipboard() { + val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + clipboard.setPrimaryClip(ClipData.newPlainText("", formatJson(response))) + showSnackBar("Tokenized card response copied.") + } + + private fun resetView() { + response = null + } + + private fun scanCard() { + val intent = Intent(this, ScanActivity::class.java).apply { + putExtra(ScanActivity.SCAN_CONFIGURATION, hashMapOf().apply { + this[vgsTiedCardNumber?.getFieldName()] = ScanActivity.CARD_NUMBER + this[vgsTiedCardHolder?.getFieldName()] = ScanActivity.CARD_HOLDER + this[vgsTiedExpiry?.getFieldName()] = ScanActivity.CARD_EXP_DATE + this[vgsTiedCvc?.getFieldName()] = ScanActivity.CARD_CVC + }) + } + @Suppress("DEPRECATION") + startActivityForResult(intent, VGSCollectActivity.USER_SCAN_REQUEST_CODE) + } + + private fun openSettings() { + TokenizationSettingsActivity.start(this) + } + + private fun runIfInputsValid(action: () -> Unit) { + var isValid = true + if (vgsTiedCardHolder.getState()?.isValid == false) { + setInputInvalid(mtvCardHolderHint, vgsTilCardHolder) + isValid = false + } + if (vgsTiedCardNumber.getState()?.isValid == false) { + setInputInvalid(mtvCardNumberHint, vgsTilCardNumber) + isValid = false + } + if (vgsTiedExpiry.getState()?.isValid == false) { + setInputInvalid(mtvExpiryHint, vgsTilExpiry) + isValid = false + } + if (vgsTiedCvc.getState()?.isValid == false) { + setInputInvalid(mtvCvcHint, vgsTilCvc) + isValid = false + } + if (isValid) action.invoke() + } + + private fun setInputValid(title: MaterialTextView, layout: VGSTextInputLayout) { + title.setTextColor(defaultHintTextColor) + layout.setBoxBackgroundColor(defaultInputBackgroundColor) + } + + private fun setInputInvalid(title: MaterialTextView, layout: VGSTextInputLayout) { + title.setTextColor(errorHintTextColor) + layout.setBoxBackgroundColor(errorInputBackgroundColor) + } + + private fun setLoading(isLoading: Boolean) { + viewOverlay.isVisible = isLoading + progressBar?.isVisible = isLoading + } + + private fun showSnackBar(message: String) { + Snackbar.make(findViewById(android.R.id.content), message, Snackbar.LENGTH_SHORT).apply { + anchorView = mbTokenize + animationMode = Snackbar.ANIMATION_MODE_SLIDE + }.show() + } + + private fun formatJson(json: String?): String = try { + JSONObject(json ?: "").toString(4) + } catch (e: Exception) { + "" + } + + private fun parseStorage(storage: String): VGSVaultStorageType = when (storage) { + VGSVaultStorageType.PERSISTENT.name -> VGSVaultStorageType.PERSISTENT + VGSVaultStorageType.VOLATILE.name -> VGSVaultStorageType.VOLATILE + else -> throw IllegalArgumentException("Not implemented!") + } + + private fun parseAliasFormat(format: String): VGSVaultAliasFormat = when (format) { + VGSVaultAliasFormat.UUID.name -> VGSVaultAliasFormat.UUID + VGSVaultAliasFormat.FPE_SIX_T_FOUR.name -> VGSVaultAliasFormat.FPE_SIX_T_FOUR + VGSVaultAliasFormat.NUM_LENGTH_PRESERVING.name -> VGSVaultAliasFormat.NUM_LENGTH_PRESERVING + else -> throw IllegalArgumentException("Not implemented!") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/verygoodsecurity/demoapp/tokenization/settings/TokenizationSettingsActivity.kt b/app/src/main/java/com/verygoodsecurity/demoapp/tokenization/settings/TokenizationSettingsActivity.kt new file mode 100644 index 000000000..e38d4a786 --- /dev/null +++ b/app/src/main/java/com/verygoodsecurity/demoapp/tokenization/settings/TokenizationSettingsActivity.kt @@ -0,0 +1,39 @@ +package com.verygoodsecurity.demoapp.tokenization.settings + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.MenuItem +import androidx.appcompat.app.AppCompatActivity +import com.verygoodsecurity.demoapp.R + +class TokenizationSettingsActivity : AppCompatActivity(R.layout.activity_tokenization_settings) { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + showSettingsFragment() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + finish() + return true + } + return super.onOptionsItemSelected(item) + } + + private fun showSettingsFragment() { + supportFragmentManager + .beginTransaction() + .add(R.id.fcvRoot, TokenizationSettingsFragment.create()) + .commit() + } + + companion object { + + fun start(context: Context) { + context.startActivity(Intent(context, TokenizationSettingsActivity::class.java)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/verygoodsecurity/demoapp/tokenization/settings/TokenizationSettingsFragment.kt b/app/src/main/java/com/verygoodsecurity/demoapp/tokenization/settings/TokenizationSettingsFragment.kt new file mode 100644 index 000000000..5bd50698f --- /dev/null +++ b/app/src/main/java/com/verygoodsecurity/demoapp/tokenization/settings/TokenizationSettingsFragment.kt @@ -0,0 +1,77 @@ +package com.verygoodsecurity.demoapp.tokenization.settings + +import android.content.SharedPreferences +import android.os.Bundle +import androidx.annotation.StringRes +import androidx.fragment.app.Fragment +import androidx.preference.CheckBoxPreference +import androidx.preference.ListPreference +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import com.verygoodsecurity.demoapp.R + +class TokenizationSettingsFragment : PreferenceFragmentCompat(), + SharedPreferences.OnSharedPreferenceChangeListener { + + private var cbpHolderNameEnabled: CheckBoxPreference? = null + private var ltHolderNameStorage: ListPreference? = null + private var ltHolderNameAliasFormat: ListPreference? = null + private var cbpExpiryEnabled: CheckBoxPreference? = null + private var ltExpiryStorage: ListPreference? = null + private var ltExpiryAliasFormat: ListPreference? = null + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.tokenization_settings) + preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) + initPreferences() + } + + override fun onSharedPreferenceChanged(p0: SharedPreferences?, p1: String?) { + if (p0 == null) { + return + } + when (p1) { + cbpHolderNameEnabled?.key -> setCardHolderPrefsEnabled(p0.getBoolean(p1, true)) + cbpExpiryEnabled?.key -> setExpiryPrefsEnabled(p0.getBoolean(p1, true)) + } + } + + private fun initPreferences() { + cbpHolderNameEnabled = findPreference(R.string.tokenization_card_holder_enabled_key) + ltHolderNameStorage = findPreference(R.string.tokenization_card_holder_storage_key) + ltHolderNameAliasFormat = findPreference(R.string.tokenization_card_holder_alias_format_key) + setCardHolderPrefsEnabled(getBoolean(R.string.tokenization_card_holder_enabled_key, true)) + + cbpExpiryEnabled = findPreference(R.string.tokenization_expiry_enabled_key) + ltExpiryStorage = findPreference(R.string.tokenization_expiry_storage_key) + ltExpiryAliasFormat = findPreference(R.string.tokenization_expiry_alias_format_key) + setExpiryPrefsEnabled(getBoolean(R.string.tokenization_expiry_enabled_key, true)) + } + + private fun setCardHolderPrefsEnabled(isEnabled: Boolean) { + ltHolderNameStorage?.isVisible = isEnabled + ltHolderNameAliasFormat?.isVisible = isEnabled + } + + private fun setExpiryPrefsEnabled(isEnabled: Boolean) { + ltExpiryStorage?.isVisible = isEnabled + ltExpiryAliasFormat?.isVisible = isEnabled + } + + companion object { + + fun create(): Fragment = TokenizationSettingsFragment() + } +} + +private fun PreferenceFragmentCompat.getBoolean(@StringRes key: Int, defValue: Boolean): Boolean { + return getBoolean(getString(key), defValue) +} + +private fun PreferenceFragmentCompat.getBoolean(key: String, defValue: Boolean): Boolean { + return preferenceManager?.sharedPreferences?.getBoolean(key, defValue) ?: defValue +} + +private fun PreferenceFragmentCompat.findPreference(@StringRes id: Int): T? { + return findPreference(getString(id)) +} \ No newline at end of file diff --git a/app/src/main/java/com/verygoodsecurity/demoapp/viewpager_case/VGSViewPagerActivity.kt b/app/src/main/java/com/verygoodsecurity/demoapp/viewpager_case/VGSViewPagerActivity.kt index 5468a6377..3d9793777 100644 --- a/app/src/main/java/com/verygoodsecurity/demoapp/viewpager_case/VGSViewPagerActivity.kt +++ b/app/src/main/java/com/verygoodsecurity/demoapp/viewpager_case/VGSViewPagerActivity.kt @@ -2,7 +2,6 @@ package com.verygoodsecurity.demoapp.viewpager_case import android.content.Intent import android.os.Bundle -import android.util.Log import android.view.View import android.widget.Toast import androidx.appcompat.app.AppCompatActivity @@ -20,19 +19,19 @@ import com.verygoodsecurity.vgscollect.view.card.CardType import com.verygoodsecurity.vgscollect.widget.VGSTextInputLayout import kotlinx.android.synthetic.main.activity_viewpager_collect_demo.* -class VGSViewPagerActivity:AppCompatActivity(), VgsCollectResponseListener, View.OnClickListener { +class VGSViewPagerActivity : AppCompatActivity(), VgsCollectResponseListener, View.OnClickListener { companion object { const val USER_SCAN_REQUEST_CODE = 0x7 } - private lateinit var vault_id:String - private lateinit var path:String + private lateinit var vault_id: String + private lateinit var path: String private lateinit var env: Environment private lateinit var vgsForm: VGSCollect - private lateinit var adapter:VGSPageAdapter + private lateinit var adapter: VGSPageAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -65,10 +64,10 @@ class VGSViewPagerActivity:AppCompatActivity(), VgsCollectResponseListener, View private fun retrieveSettings() { val bndl = intent?.extras - vault_id = bndl?.getString(StartActivity.VAULT_ID, "")?:"" - path = bndl?.getString(StartActivity.PATH,"/")?:"" + vault_id = bndl?.getString(StartActivity.KEY_BUNDLE_VAULT_ID, "") ?: "" + path = bndl?.getString(StartActivity.KEY_BUNDLE_PATH, "/") ?: "" - val envId = bndl?.getInt(StartActivity.ENVIROMENT, 0)?:0 + val envId = bndl?.getInt(StartActivity.KEY_BUNDLE_ENVIRONMENT, 0) ?: 0 env = Environment.values()[envId] vgsForm = VGSCollect(this, vault_id, env) @@ -94,7 +93,7 @@ class VGSViewPagerActivity:AppCompatActivity(), VgsCollectResponseListener, View cardValid = state.isValid previewCardNumber?.text = (state as FieldState.CardNumberState).number - if(state.cardBrand == CardType.VISA.name) { + if (state.cardBrand == CardType.VISA.name) { previewCardBrand?.setImageResource(R.drawable.ic_custom_visa) } else { previewCardBrand?.setImageResource(state.drawableBrandResId) @@ -129,8 +128,11 @@ class VGSViewPagerActivity:AppCompatActivity(), VgsCollectResponseListener, View override fun onResponse(response: VGSResponse?) { when (response) { - is VGSResponse.SuccessResponse -> Toast.makeText(this, "Success", Toast.LENGTH_LONG).show()//responseContainerView.text = response.toString() - is VGSResponse.ErrorResponse -> Toast.makeText(this, "Error", Toast.LENGTH_LONG).show()//responseContainerView.text = response.toString() + is VGSResponse.SuccessResponse -> Toast.makeText(this, "Success", Toast.LENGTH_LONG) + .show()//responseContainerView.text = response.toString() + is VGSResponse.ErrorResponse -> Toast.makeText(this, "Error", Toast.LENGTH_LONG) + .show()//responseContainerView.text = response.toString() + else -> return } } @@ -145,15 +147,15 @@ class VGSViewPagerActivity:AppCompatActivity(), VgsCollectResponseListener, View } override fun onClick(v: View) { - when(v.id) { + when (v.id) { R.id.backBtn -> turnBackPage() R.id.nextBtn -> turnPageOn() } } private fun turnBackPage() { - val position = viewPager.currentItem-1 - if(position < 0) { + val position = viewPager.currentItem - 1 + if (position < 0) { viewPager?.setCurrentItem(0, false) } else { viewPager?.setCurrentItem(position, true) @@ -162,7 +164,7 @@ class VGSViewPagerActivity:AppCompatActivity(), VgsCollectResponseListener, View nextBtn?.setText("Next") nextBtn?.icon = AppCompatResources.getDrawable(this, R.drawable.ic_arrow_right) - backBtn?.visibility = if(position == 0) { + backBtn?.visibility = if (position == 0) { View.INVISIBLE } else { View.VISIBLE @@ -175,23 +177,26 @@ class VGSViewPagerActivity:AppCompatActivity(), VgsCollectResponseListener, View var cardExpDateValid = false private fun turnPageOn() { - val position = viewPager.currentItem+1 - val isValid:Boolean = when(position) { + val position = viewPager.currentItem + 1 + val isValid: Boolean = when (position) { 1 -> cardValid 2 -> cardHolderValid 3 -> cvcValid && cardExpDateValid else -> false } - if(isValid) { + if (isValid) { backBtn?.visibility = View.VISIBLE - if(position == adapter.itemCount-1) { + if (position == adapter.itemCount - 1) { nextBtn?.setText("Submit") nextBtn?.icon = null } when { - position > adapter.itemCount -> viewPager?.setCurrentItem(viewPager.currentItem, false) + position > adapter.itemCount -> viewPager?.setCurrentItem( + viewPager.currentItem, + false + ) position == adapter.itemCount -> submitData() else -> viewPager?.setCurrentItem(position, true) } diff --git a/app/src/main/res/color/invisible_input_border.xml b/app/src/main/res/color/invisible_input_border.xml new file mode 100644 index 000000000..b3991cc26 --- /dev/null +++ b/app/src/main/res/color/invisible_input_border.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-ldrtl/ic_actionbar_logo.xml b/app/src/main/res/drawable-ldrtl/ic_actionbar_logo.xml new file mode 100644 index 000000000..a2298dc02 --- /dev/null +++ b/app/src/main/res/drawable-ldrtl/ic_actionbar_logo.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_card_preview.xml b/app/src/main/res/drawable/bg_card_preview.xml index 56918d657..9a7ab9a7b 100644 --- a/app/src/main/res/drawable/bg_card_preview.xml +++ b/app/src/main/res/drawable/bg_card_preview.xml @@ -1,6 +1,6 @@ - + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_custom_btn.xml b/app/src/main/res/drawable/bg_custom_btn.xml new file mode 100644 index 000000000..1f340322b --- /dev/null +++ b/app/src/main/res/drawable/bg_custom_btn.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/copy_code_example_ic.xml b/app/src/main/res/drawable/copy_code_example_ic.xml new file mode 100644 index 000000000..7c87f24c5 --- /dev/null +++ b/app/src/main/res/drawable/copy_code_example_ic.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_actionbar_logo.xml b/app/src/main/res/drawable/ic_actionbar_logo.xml new file mode 100644 index 000000000..01472c003 --- /dev/null +++ b/app/src/main/res/drawable/ic_actionbar_logo.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_content_copy_16.xml b/app/src/main/res/drawable/ic_baseline_content_copy_16.xml new file mode 100644 index 000000000..3560ba252 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_content_copy_16.xml @@ -0,0 +1,11 @@ + + + + diff --git a/app/src/main/res/drawable/ic_baseline_credit_card_24.xml b/app/src/main/res/drawable/ic_baseline_credit_card_24.xml new file mode 100644 index 000000000..ab7a47b2c --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_credit_card_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_date_range_24.xml b/app/src/main/res/drawable/ic_baseline_date_range_24.xml new file mode 100644 index 000000000..8dc9e69af --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_date_range_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_person_24.xml b/app/src/main/res/drawable/ic_baseline_person_24.xml new file mode 100644 index 000000000..98730cd90 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_person_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_settings_24.xml b/app/src/main/res/drawable/ic_baseline_settings_24.xml new file mode 100644 index 000000000..51c774f75 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_settings_24.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vgs_logo_white.xml b/app/src/main/res/drawable/ic_vgs_logo_white.xml new file mode 100644 index 000000000..9b26cc41a --- /dev/null +++ b/app/src/main/res/drawable/ic_vgs_logo_white.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_collect_demo.xml b/app/src/main/res/layout/activity_collect_demo.xml index 560f8b42c..77d7fc273 100644 --- a/app/src/main/res/layout/activity_collect_demo.xml +++ b/app/src/main/res/layout/activity_collect_demo.xml @@ -13,10 +13,10 @@ + android:paddingStart="@dimen/margin_padding_material_medium" + android:paddingTop="@dimen/margin_padding_material_small" + android:paddingEnd="@dimen/margin_padding_material_medium" + android:paddingBottom="@dimen/margin_padding_material_medium"> + android:paddingStart="@dimen/margin_padding_material_medium" + android:paddingTop="@dimen/margin_padding_material_medium" + android:paddingEnd="@dimen/margin_padding_material_medium"> @@ -47,7 +47,7 @@ android:layout_gravity="center_horizontal" android:layout_marginStart="10dp" android:layout_marginEnd="10dp" - app:cardCornerRadius="@dimen/half_default_margin"> + app:cardCornerRadius="@dimen/margin_padding_material_small"> @@ -228,19 +228,19 @@ android:id="@+id/submitBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/default_margin" + android:layout_marginEnd="@dimen/margin_padding_material_medium" android:text="@string/collect_activity_submit_btn" android:textColor="@android:color/white" android:textSize="14dp" app:backgroundTint="@color/colorAccent" - app:cornerRadius="@dimen/default_margin" /> + app:cornerRadius="@dimen/margin_padding_material_medium" /> @@ -281,7 +281,7 @@ @@ -310,24 +310,24 @@ + android:padding="@dimen/margin_padding_material_medium"> diff --git a/app/src/main/res/layout/activity_start.xml b/app/src/main/res/layout/activity_start.xml index e10fc2e47..c8280f64c 100644 --- a/app/src/main/res/layout/activity_start.xml +++ b/app/src/main/res/layout/activity_start.xml @@ -1,82 +1,259 @@ - + android:layout_height="match_parent" + android:overScrollMode="never"> - - + + + + - + android:layout_height="wrap_content" + app:boxBackgroundColor="@color/fiord_20" + app:boxStrokeColor="@color/invisible_input_border" + app:errorEnabled="false" + app:hintEnabled="false"> - - + + + + + - + app:boxBackgroundColor="@color/fiord_20" + app:boxStrokeColor="@color/invisible_input_border" + app:errorEnabled="false" + app:hintEnabled="false"> - + + - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_tokenization.xml b/app/src/main/res/layout/activity_tokenization.xml new file mode 100644 index 000000000..737ced2d5 --- /dev/null +++ b/app/src/main/res/layout/activity_tokenization.xml @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_tokenization_settings.xml b/app/src/main/res/layout/activity_tokenization_settings.xml new file mode 100644 index 000000000..8e842e56c --- /dev/null +++ b/app/src/main/res/layout/activity_tokenization_settings.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_viewpager_collect_demo.xml b/app/src/main/res/layout/activity_viewpager_collect_demo.xml index f368ae7aa..6bf4aabdb 100644 --- a/app/src/main/res/layout/activity_viewpager_collect_demo.xml +++ b/app/src/main/res/layout/activity_viewpager_collect_demo.xml @@ -21,10 +21,10 @@ android:layout_width="match_parent" android:layout_height="220dp" android:background="#22000000" - android:paddingTop="@dimen/default_margin" - android:paddingStart="@dimen/half_default_margin" - android:paddingEnd="@dimen/half_default_margin" - android:paddingBottom="@dimen/half_default_margin"> + android:paddingTop="@dimen/margin_padding_material_medium" + android:paddingStart="@dimen/margin_padding_material_small" + android:paddingEnd="@dimen/margin_padding_material_small" + android:paddingBottom="@dimen/margin_padding_material_small"> + android:layout_marginBottom="@dimen/margin_padding_material_small"> @@ -28,10 +28,10 @@ app:hintAnimationEnabled="false" app:hintEnabled="false" app:boxStrokeColor="#969696" - app:boxCornerRadius="@dimen/half_default_margin" - android:layout_marginTop="@dimen/default_margin" - android:layout_marginStart="@dimen/default_margin" - android:layout_marginEnd="@dimen/default_margin"> + app:boxCornerRadius="@dimen/margin_padding_material_small" + android:layout_marginTop="@dimen/margin_padding_material_medium" + android:layout_marginStart="@dimen/margin_padding_material_medium" + android:layout_marginEnd="@dimen/margin_padding_material_medium"> + android:paddingStart="@dimen/margin_padding_material_medium" + android:paddingEnd="@dimen/margin_padding_material_medium" + android:layout_marginStart="@dimen/margin_padding_material_medium" + android:layout_marginEnd="@dimen/margin_padding_material_medium"/> @@ -53,10 +53,10 @@ app:layout_constraintStart_toStartOf="@+id/cardCVCFieldLay" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="@dimen/half_default_margin" - android:layout_marginEnd="@dimen/half_default_margin" - android:layout_marginTop="@dimen/default_margin" - android:layout_marginBottom="@dimen/default_margin" + android:layout_marginStart="@dimen/margin_padding_material_small" + android:layout_marginEnd="@dimen/margin_padding_material_small" + android:layout_marginTop="@dimen/margin_padding_material_medium" + android:layout_marginBottom="@dimen/margin_padding_material_medium" android:text="CVC"/> + app:boxCornerRadius="@dimen/margin_padding_material_small" + android:layout_marginTop="@dimen/margin_padding_material_medium" + android:layout_marginStart="@dimen/margin_padding_material_medium" + android:layout_marginEnd="@dimen/margin_padding_material_medium"> + android:paddingStart="@dimen/margin_padding_material_medium" + android:paddingEnd="@dimen/margin_padding_material_medium" + android:layout_marginStart="@dimen/margin_padding_material_medium" + android:layout_marginEnd="@dimen/margin_padding_material_medium"/> \ No newline at end of file diff --git a/app/src/main/res/layout/card_holder_page.xml b/app/src/main/res/layout/card_holder_page.xml index 6ebd9e5dc..baf3e7e4e 100644 --- a/app/src/main/res/layout/card_holder_page.xml +++ b/app/src/main/res/layout/card_holder_page.xml @@ -9,10 +9,10 @@ + android:layout_marginStart="@dimen/margin_padding_material_medium" + android:layout_marginEnd="@dimen/margin_padding_material_medium"> + android:layout_marginStart="@dimen/margin_padding_material_medium" + android:layout_marginEnd="@dimen/margin_padding_material_medium"/> \ No newline at end of file diff --git a/app/src/main/res/layout/card_number_page.xml b/app/src/main/res/layout/card_number_page.xml index fbf59c6ac..bcf61d47c 100644 --- a/app/src/main/res/layout/card_number_page.xml +++ b/app/src/main/res/layout/card_number_page.xml @@ -9,10 +9,10 @@ + app:boxCornerRadius="@dimen/margin_padding_material_small" + android:layout_marginStart="@dimen/margin_padding_material_medium" + android:layout_marginEnd="@dimen/margin_padding_material_medium"> diff --git a/app/src/main/res/layout/code_example_layout.xml b/app/src/main/res/layout/code_example_layout.xml new file mode 100644 index 000000000..68814b290 --- /dev/null +++ b/app/src/main/res/layout/code_example_layout.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fields_gravity.xml b/app/src/main/res/layout/fields_gravity.xml index 442d0a256..c88f18121 100644 --- a/app/src/main/res/layout/fields_gravity.xml +++ b/app/src/main/res/layout/fields_gravity.xml @@ -8,7 +8,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:padding="@dimen/default_margin"> + android:padding="@dimen/margin_padding_material_medium"> + android:paddingStart="@dimen/margin_padding_material_medium" + android:paddingEnd="@dimen/margin_padding_material_medium" + android:paddingTop="@dimen/margin_padding_material_small" + android:paddingBottom="@dimen/margin_padding_material_medium"> @@ -23,19 +23,19 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:paddingTop="@dimen/default_margin" - android:paddingStart="@dimen/default_margin" - android:paddingEnd="@dimen/default_margin"> + android:paddingTop="@dimen/margin_padding_material_medium" + android:paddingStart="@dimen/margin_padding_material_medium" + android:paddingEnd="@dimen/margin_padding_material_medium"> + android:layout_marginBottom="@dimen/margin_padding_material_medium"/> + android:layout_marginBottom="@dimen/margin_padding_material_small"> @@ -220,7 +220,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:layout_marginTop="@dimen/default_margin" + android:layout_marginTop="@dimen/margin_padding_material_medium" android:gravity="end"> + app:cornerRadius="@dimen/margin_padding_material_medium"/> @@ -270,7 +270,7 @@ android:layout_width="12dp" android:layout_height="match_parent" android:src="@drawable/ic_attach_file" - android:layout_marginEnd="@dimen/half_default_margin"/> + android:layout_marginEnd="@dimen/margin_padding_material_small"/> + android:layout_marginTop="@dimen/margin_padding_material_small" /> + android:padding="@dimen/margin_padding_material_medium"> + android:paddingLeft="@dimen/margin_padding_material_medium"/> + android:paddingLeft="@dimen/margin_padding_material_medium"/> + android:paddingStart="@dimen/margin_padding_material_medium"/> + android:paddingStart="@dimen/margin_padding_material_medium"/> @@ -119,7 +119,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" app:singleLine="true" android:background="#22ff0000" app:text="text_test_text_test_text_test_text_test_text_test_text_test_" - android:paddingRight="@dimen/default_margin"/> + android:paddingRight="@dimen/margin_padding_material_medium"/> + android:paddingRight="@dimen/margin_padding_material_medium"/> + android:paddingEnd="@dimen/margin_padding_material_medium"/> + android:paddingEnd="@dimen/margin_padding_material_medium"/> @@ -181,7 +181,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:background="#22ff0000" app:singleLine="true" app:text="text_test_text_test_text_test_text_test_text_test_text_test_" - android:paddingTop="@dimen/default_margin"/> + android:paddingTop="@dimen/margin_padding_material_medium"/> + android:paddingTop="@dimen/margin_padding_material_medium"/> @@ -212,7 +212,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:background="#22ff0000" app:text="text_test_text_test_text_test_text_test_text_test_text_test_" app:hint="padding Bottom" - android:paddingBottom="@dimen/default_margin"/> + android:paddingBottom="@dimen/margin_padding_material_medium"/> + android:paddingBottom="@dimen/margin_padding_material_medium"/> @@ -242,8 +242,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:background="#22ff0000" app:text="text_test_text_test_text_test_text_test_text_test_text_test_" app:hint="padding start top" - android:paddingTop="@dimen/default_margin" - android:paddingStart="@dimen/default_margin" + android:paddingTop="@dimen/margin_padding_material_medium" + android:paddingStart="@dimen/margin_padding_material_medium" /> @@ -276,8 +276,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:background="#22ff0000" app:text="text_test_text_test_text_test_text_test_text_test_text_test_" app:hint="padding end bottom" - android:paddingBottom="@dimen/default_margin" - android:paddingEnd="@dimen/default_margin" + android:paddingBottom="@dimen/margin_padding_material_medium" + android:paddingEnd="@dimen/margin_padding_material_medium" /> @@ -311,8 +311,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:background="#22ff0000" app:text="text_test_text_test_text_test_text_test_text_test_text_test_" app:hint="padding top bottom" - android:paddingBottom="@dimen/default_margin" - android:paddingTop="@dimen/default_margin" + android:paddingBottom="@dimen/margin_padding_material_medium" + android:paddingTop="@dimen/margin_padding_material_medium" /> @@ -345,8 +345,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:background="#22ff0000" app:text="text_test_text_test_text_test_text_test_text_test_text_test_" app:hint="padding left right" - android:paddingLeft="@dimen/default_margin" - android:paddingRight="@dimen/default_margin" + android:paddingLeft="@dimen/margin_padding_material_medium" + android:paddingRight="@dimen/margin_padding_material_medium" /> @@ -379,10 +379,10 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:background="#22ff0000" app:text="text_test_text_test_text_test_text_test_text_test_text_test_" app:hint="padding all" - android:paddingLeft="@dimen/default_margin" - android:paddingRight="@dimen/default_margin" - android:paddingBottom="@dimen/default_margin" - android:paddingTop="@dimen/default_margin" + android:paddingLeft="@dimen/margin_padding_material_medium" + android:paddingRight="@dimen/margin_padding_material_medium" + android:paddingBottom="@dimen/margin_padding_material_medium" + android:paddingTop="@dimen/margin_padding_material_medium" /> diff --git a/app/src/main/res/layout/input_layout_card_num_test.xml b/app/src/main/res/layout/input_layout_card_num_test.xml index 32b245d1f..d2b2599f7 100644 --- a/app/src/main/res/layout/input_layout_card_num_test.xml +++ b/app/src/main/res/layout/input_layout_card_num_test.xml @@ -4,20 +4,20 @@ android:id="@+id/state" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_margin="@dimen/default_margin"> + android:layout_margin="@dimen/margin_padding_material_medium"> + android:paddingRight="@dimen/margin_padding_material_medium"> @@ -153,8 +153,8 @@ @@ -186,8 +186,8 @@ @@ -409,8 +409,8 @@ @@ -464,8 +464,8 @@ @@ -484,8 +484,8 @@ @@ -505,8 +505,8 @@ @@ -538,8 +538,8 @@ @@ -555,8 +555,8 @@ @@ -572,8 +572,8 @@ @@ -590,8 +590,8 @@ @@ -627,8 +627,8 @@ @@ -663,8 +663,8 @@ @@ -681,8 +681,8 @@ @@ -716,8 +716,8 @@ @@ -733,8 +733,8 @@ @@ -751,8 +751,8 @@ @@ -770,8 +770,8 @@ @@ -787,8 +787,8 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/layout_code_view.xml b/app/src/main/res/layout/layout_code_view.xml new file mode 100644 index 000000000..e5b612e58 --- /dev/null +++ b/app/src/main/res/layout/layout_code_view.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/text_input_layout_modes.xml b/app/src/main/res/layout/text_input_layout_modes.xml index 331c4a63d..588db26f7 100644 --- a/app/src/main/res/layout/text_input_layout_modes.xml +++ b/app/src/main/res/layout/text_input_layout_modes.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="#110000ff" - android:layout_margin="@dimen/default_margin"> + android:layout_margin="@dimen/margin_padding_material_medium"> + android:padding="@dimen/margin_padding_material_small"> + android:padding="@dimen/margin_padding_material_small"> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 036d09bc5..b92d83cef 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 036d09bc5..b92d83cef 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 000000000..fd6828025 --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,14 @@ + + + + + PERSISTENT + VOLATILE + + + + UUID + FPE_SIX_T_FOUR + NUM_LENGTH_PRESERVING + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index e9ca0cdcf..c51aae6a7 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,5 +1,5 @@ - + #27A386 #188269 #26A6D4 @@ -16,5 +16,15 @@ #C5E1EB @color/colorAccent - #000 + #4b5d69 + #204b5d69 + #22343e + #145ff5 + #0036c1 + #E6E6E6 + #AD3333 + #E3D3D3 + #181F27 + #CCCCCC + #999999 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 66978393c..aa6f54223 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -1,7 +1,10 @@ - 16dp - 8dp + 32dp + 24dp + 16dp + 8dp + 4dp 48dp 12dp diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml deleted file mode 100644 index c5d5899fd..000000000 --- a/app/src/main/res/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #FFFFFF - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 900777360..cf8d99464 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,33 @@ VGS Collect Demo + + Very Good Security + + Vault id + Path (Only for collect flow) + + Environment: + Sandbox + Live + + Start + Tokenization + Description (TODO) + + Collect (Activity) + Description (TODO) + + Collect (Fragment) + Description (TODO) + + Collect (ViewPager) + Description (TODO) + + Scan + Settings + + + Scan Card Collecting credit card data submit Attach file @@ -17,11 +45,12 @@ Postal code City - Vault ID - path - Start with Activity - Start with Fragment - Start with ViewPager - - Scan Card + + key_card_holder_tokenization_enabled + key_card_holder_tokenization_storage + key_card_holder_tokenization_alias_format + key_card_number_tokenization_alias_format + key_expiry_tokenization_enabled + key_expiry_tokenization_storage + key_expiry_tokenization_alias_format diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 41ee19cfb..ce7f44d81 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,22 +1,56 @@ - + - + + + + + + + - + + + + - diff --git a/app/src/main/res/xml/tokenization_settings.xml b/app/src/main/res/xml/tokenization_settings.xml new file mode 100644 index 000000000..727981549 --- /dev/null +++ b/app/src/main/res/xml/tokenization_settings.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index c69c8851b..5b2fb2d3b 100644 --- a/build.gradle +++ b/build.gradle @@ -22,8 +22,8 @@ allprojects { } plugins.withId("com.vanniktech.maven.publish") { - mavenPublish { - sonatypeHost = "S01" + mavenPublishing { + publishToMavenCentral("S01") } } } diff --git a/docs/index.html b/docs/index.html index b10d5f766..e1864cecc 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,486 +1,509 @@ + - + vgscollect - + - - -