Sat | <Ch-12343> | Hyperverge Selfie (#864)
* started... * release 1.1.1 * Shashidhara | Update find ifsc visibility based on bank name validation * back press issue * minor changes * pre populate loan data from edit bank account * added contract * selfie hyperverge * error handlding * added git sttaus * added keys into gradle * added keys into docker file * intro screen hide after 1st time * removed instrauction Co-authored-by: Shashidhara Gopal <shashidhara.gopal@navi.com>
This commit is contained in:
committed by
GitHub Enterprise
parent
ecd6e1c043
commit
97573e6288
@@ -13,6 +13,8 @@ ARG RELEASE_KEY_ALIAS
|
||||
ARG NEXUS_URL
|
||||
ARG NEXUS_USERNAME
|
||||
ARG NEXUS_PASSWORD
|
||||
ARG HYPERVERGE_APP_KEY
|
||||
ARG HYPERVERGE_APP_ID
|
||||
|
||||
ENV WORK_DIR="/android/navi" \
|
||||
ANDROID_APK_DIR="app/build/outputs/apk"
|
||||
@@ -39,7 +41,7 @@ RUN bash -c " \
|
||||
\
|
||||
elif [ $FLAVOR = PROD ] ; then \
|
||||
./gradlew clean \
|
||||
assembleProd -PBASE_URL=${BASE_URL} -PUXCAM_KEY=${UXCAM_KEY} -PRAZORPAY_KEY=${RAZORPAY_KEY} -PMOENGAGE_KEY=${MOENGAGE_KEY} -PAPPSFLYER_KEY=${APPSFLYER_KEY} -PFLAVOR=${FLAVOR} -PRELEASE_STORE_PASSWORD=${RELEASE_STORE_PASSWORD} -PRELEASE_KEY_PASSWORD=${RELEASE_KEY_PASSWORD} -PRELEASE_KEY_ALIAS=${RELEASE_KEY_ALIAS} ; \
|
||||
assembleProd -PBASE_URL=${BASE_URL} -PUXCAM_KEY=${UXCAM_KEY} -PRAZORPAY_KEY=${RAZORPAY_KEY} -PMOENGAGE_KEY=${MOENGAGE_KEY} -PAPPSFLYER_KEY=${APPSFLYER_KEY} -PFLAVOR=${FLAVOR} -PRELEASE_STORE_PASSWORD=${RELEASE_STORE_PASSWORD} -PRELEASE_KEY_PASSWORD=${RELEASE_KEY_PASSWORD} -PRELEASE_KEY_ALIAS=${RELEASE_KEY_ALIAS} -PHYPERVERGE_APP_KEY=${HYPERVERGE_APP_KEY} -PHYPERVERGE_APP_ID=${HYPERVERGE_APP_ID} ; \
|
||||
\
|
||||
else echo 'ERROR: Flavor not mentioned' ; \
|
||||
fi ;"
|
||||
|
||||
@@ -83,6 +83,8 @@ android {
|
||||
buildConfigField 'String', 'MOENGAGE_KEY', formatString('2PDJ4M6TDY7ADQ5N5LU48H9Y')
|
||||
buildConfigField 'String', 'APPSFLYER_KEY', formatString('SwphSVn7YVzwCFFoAQNDKX')
|
||||
buildConfigField 'String', 'UXCAM_KEY', formatString('ddo6o2imqy6y3so')
|
||||
buildConfigField 'String', 'HYPERVERGE_APP_KEY', formatString('c9b1e034f7c8961a3f5b')
|
||||
buildConfigField 'String', 'HYPERVERGE_APP_ID', formatString('2c007b')
|
||||
}
|
||||
dev {
|
||||
applicationId "com.naviapp.dev"
|
||||
@@ -92,15 +94,19 @@ android {
|
||||
buildConfigField "String", "MOENGAGE_KEY", formatString('2PDJ4M6TDY7ADQ5N5LU48H9Y')
|
||||
buildConfigField "String", "APPSFLYER_KEY", formatString('SwphSVn7YVzwCFFoAQNDKX')
|
||||
buildConfigField 'String', 'UXCAM_KEY', formatString('ddo6o2imqy6y3so')
|
||||
buildConfigField 'String', 'HYPERVERGE_APP_KEY', formatString('c9b1e034f7c8961a3f5b')
|
||||
buildConfigField 'String', 'HYPERVERGE_APP_ID', formatString('2c007b')
|
||||
}
|
||||
prod {
|
||||
dimension "app"
|
||||
if (project.hasProperty('BASE_URL') && project.hasProperty('RAZORPAY_KEY') && project.hasProperty('MOENGAGE_KEY') && project.hasProperty('APPSFLYER_KEY') && project.hasProperty('UXCAM_KEY')) {
|
||||
if (project.hasProperty('BASE_URL') && project.hasProperty('RAZORPAY_KEY') && project.hasProperty('MOENGAGE_KEY') && project.hasProperty('APPSFLYER_KEY') && project.hasProperty('UXCAM_KEY') && project.hasProperty('HYPERVERGE_APP_KEY') && project.hasProperty('HYPERVERGE_APP_ID')) {
|
||||
buildConfigField 'String', 'BASE_URL', formatString("$BASE_URL")
|
||||
buildConfigField 'String', 'RAZORPAY_KEY', formatString("$RAZORPAY_KEY")
|
||||
buildConfigField 'String', 'MOENGAGE_KEY', formatString("$MOENGAGE_KEY")
|
||||
buildConfigField 'String', 'APPSFLYER_KEY', formatString("$APPSFLYER_KEY")
|
||||
buildConfigField 'String', 'UXCAM_KEY', formatString("$UXCAM_KEY")
|
||||
buildConfigField 'String', 'HYPERVERGE_APP_KEY', formatString("$HYPERVERGE_APP_KEY")
|
||||
buildConfigField 'String', 'HYPERVERGE_APP_ID', formatString("$HYPERVERGE_APP_ID")
|
||||
}
|
||||
}
|
||||
mockServer {
|
||||
@@ -111,6 +117,8 @@ android {
|
||||
buildConfigField "String", "MOENGAGE_KEY", formatString('2PDJ4M6TDY7ADQ5N5LU48H9Y')
|
||||
buildConfigField "String", "APPSFLYER_KEY", formatString('SwphSVn7YVzwCFFoAQNDKX')
|
||||
buildConfigField "String", "UXCAM_KEY", formatString('ddo6o2imqy6y3so')
|
||||
buildConfigField 'String', 'HYPERVERGE_APP_KEY', formatString('c9b1e034f7c8961a3f5b')
|
||||
buildConfigField 'String', 'HYPERVERGE_APP_ID', formatString('2c007b')
|
||||
}
|
||||
}
|
||||
packagingOptions {
|
||||
@@ -288,6 +296,14 @@ dependencies {
|
||||
|
||||
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.71"
|
||||
|
||||
implementation('co.hyperverge:hypersnapsdk:3.1.3@aar', {
|
||||
transitive = true
|
||||
exclude group: 'com.google.android.gms'
|
||||
exclude group: 'co.hyperverge', module: 'hypersnapsdk-instructions'
|
||||
exclude group: 'co.hyperverge', module: 'hypersnapsdk-qrscanner'
|
||||
exclude group: 'co.hyperverge', module: 'hypersnap-pdfconverter'
|
||||
})
|
||||
}
|
||||
|
||||
task executeTestsOnBrowserStack {
|
||||
@@ -371,15 +387,21 @@ task executeTestsOnBrowserStack {
|
||||
sleep(Duration.ofSeconds(10).toMillis())
|
||||
} else {
|
||||
def anyErrors = buildStatuses.any { it.status == "error" }
|
||||
if(anyErrors) throw GradleException("Error executing Tests")
|
||||
if (anyErrors) throw GradleException("Error executing Tests")
|
||||
|
||||
def anyTestStatusMissing = buildStatuses.any { it.devices["${device}"].test_status == null }
|
||||
if(anyTestStatusMissing) throw GradleException("Error getting Test Status. Test Status is null")
|
||||
def anyTestStatusMissing = buildStatuses.any {
|
||||
it.devices["${device}"].test_status == null
|
||||
}
|
||||
if (anyTestStatusMissing) throw GradleException("Error getting Test Status. Test Status is null")
|
||||
|
||||
def anyTestFailed = buildStatuses.any { it.devices["${device}"].test_status.FAILED > 0 }
|
||||
def failedBuilds = buildStatuses.findAll { it.devices["${device}"].test_status.FAILED > 0 }.collect { it.build_id }
|
||||
def anyTestFailed = buildStatuses.any {
|
||||
it.devices["${device}"].test_status.FAILED > 0
|
||||
}
|
||||
def failedBuilds = buildStatuses.findAll {
|
||||
it.devices["${device}"].test_status.FAILED > 0
|
||||
}.collect { it.build_id }
|
||||
println(failedBuilds)
|
||||
if(anyTestFailed) throw new GradleException("Tests failing")
|
||||
if (anyTestFailed) throw new GradleException("Tests failing")
|
||||
|
||||
waitForResult = false
|
||||
}
|
||||
|
||||
6
app/proguard-rules.pro
vendored
6
app/proguard-rules.pro
vendored
@@ -237,3 +237,9 @@
|
||||
#for UXCam
|
||||
-keep class com.uxcam.** { *; }
|
||||
-dontwarn com.uxcam.**
|
||||
|
||||
#for selfie : hyperverge
|
||||
-dontwarn co.hyperverge.**
|
||||
-keepclassmembers class * implements javax.net.ssl.SSLSocketFactory {
|
||||
private javax.net.ssl.SSLSocketFactory delegate;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<application
|
||||
android:name=".app.NaviApplication"
|
||||
android:allowBackup="false"
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="@xml/backup_descriptor"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
package com.naviapp.analytics
|
||||
|
||||
import co.hyperverge.hypersnapsdk.HyperSnapSDK
|
||||
import co.hyperverge.hypersnapsdk.objects.HyperSnapParams
|
||||
import com.naviapp.BuildConfig
|
||||
import com.naviapp.analytics.moengage.MoengageUtil
|
||||
import com.naviapp.app.NaviApplication
|
||||
@@ -20,6 +22,12 @@ object NaviTrackEvent {
|
||||
FcmAnalyticsUtil.analytics.init(appContext)
|
||||
MoengageUtil.init(appContext)
|
||||
AppsFlyerUtil.instance.init(appContext)
|
||||
HyperSnapSDK.init(
|
||||
appContext,
|
||||
BuildConfig.HYPERVERGE_APP_ID,
|
||||
BuildConfig.HYPERVERGE_APP_KEY,
|
||||
HyperSnapParams.Region.India
|
||||
)
|
||||
}
|
||||
|
||||
fun startScreen(screenName: String?, isNeededForAppsflyer: Boolean = false) {
|
||||
@@ -59,7 +67,7 @@ object NaviTrackEvent {
|
||||
|
||||
fun setLatLongAttribute(location: UserLocation?) = MoengageUtil.setLatLongAttribute(location)
|
||||
|
||||
fun trackAppInstall(){
|
||||
fun trackAppInstall() {
|
||||
MoengageUtil.appInstallOrUpdate(true)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.naviapp.getloan.bankdetailsautodebit.fragments.BankDetailsAutoDebitFr
|
||||
import com.naviapp.getloan.bankdetailsautodebit.viewmodels.EnachSharedVM
|
||||
import com.naviapp.getloan.common.CustomerSupportFragment
|
||||
import com.naviapp.getloan.kyc.fragments.KycFragment
|
||||
import com.naviapp.getloan.kyc.listeners.SelfieCaptureListener
|
||||
import com.naviapp.getloan.kyc.models.AadhaarDetails
|
||||
import com.naviapp.getloan.kyc.models.AadhaarVerificationData
|
||||
import com.naviapp.getloan.kyc.viewmodels.KycSharedVM
|
||||
@@ -31,8 +32,12 @@ import com.naviapp.getloan.loanagreement.fragments.LoanAgreementFragment
|
||||
import com.naviapp.getloan.loandetails.fragments.LoanDetailsFragment
|
||||
import com.naviapp.manager.PermissionsManager
|
||||
import com.naviapp.models.SubPageStatusType
|
||||
import com.naviapp.models.request.SelfieSetting
|
||||
import com.naviapp.models.request.SelfieUploadRequestData
|
||||
import com.naviapp.models.response.DisbursementDetailsResponse
|
||||
import com.naviapp.network.ApiConstants
|
||||
import com.naviapp.selfiecapture.SelfieErrorData
|
||||
import com.naviapp.selfiecapture.SelfieVerificationHelper
|
||||
import com.naviapp.useridentification.activities.LoanEligibilityLoaderActivity.Companion.OFFER
|
||||
import com.naviapp.useridentification.common.FormProgressBar
|
||||
import com.naviapp.utils.Constants
|
||||
@@ -43,7 +48,9 @@ import org.json.JSONObject
|
||||
import timber.log.Timber
|
||||
|
||||
class GetLoanActivity : BaseActivity(), FragmentInteractionListener,
|
||||
FormProgressBar.FormProgressListener, NaviPermissionsListener, View.OnClickListener {
|
||||
FormProgressBar.FormProgressListener, NaviPermissionsListener, View.OnClickListener,
|
||||
SelfieCaptureListener {
|
||||
|
||||
private lateinit var binding: ActivityGetLoanBinding
|
||||
private var disbursementDetailsData: DisbursementDetailsResponse? = null
|
||||
private val kycSharedVM by lazy { ViewModelProvider(this).get(KycSharedVM::class.java) }
|
||||
@@ -54,10 +61,11 @@ class GetLoanActivity : BaseActivity(), FragmentInteractionListener,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
)
|
||||
private var currentScreen: String = ""
|
||||
private var selfieVerificationHelper: SelfieVerificationHelper? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
selfieVerificationHelper = SelfieVerificationHelper()
|
||||
initializeData(savedInstanceState)
|
||||
binding = DataBindingUtil.setContentView(this, R.layout.activity_get_loan)
|
||||
initUi()
|
||||
@@ -319,6 +327,20 @@ class GetLoanActivity : BaseActivity(), FragmentInteractionListener,
|
||||
.show(supportFragmentManager, CustomerSupportFragment.TAG)
|
||||
}
|
||||
|
||||
override fun onSelfiCaptureClick(data: SelfieSetting?) {
|
||||
selfieVerificationHelper?.startFaceCapture(this, this, data)
|
||||
}
|
||||
|
||||
override fun onSelfieSuccess(data: SelfieUploadRequestData, imageUri: String?) {
|
||||
kycSharedVM.setSelfieRquestData(data)
|
||||
}
|
||||
|
||||
override fun onSelfieError(data: SelfieErrorData?) {
|
||||
data?.let {
|
||||
kycSharedVM.setSelfieErrorData(data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(view: View?) {
|
||||
when (view?.id) {
|
||||
R.id.help_iv -> openHelpInfo()
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.RadioGroup
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
@@ -29,17 +30,12 @@ import com.naviapp.databinding.KycFragmentBinding
|
||||
import com.naviapp.digio.AadhaarVerificationHelper
|
||||
import com.naviapp.errors.activities.ErrorActivity
|
||||
import com.naviapp.errors.utils.getGetAadhaarCancelOrFailData
|
||||
import com.naviapp.firebasedb.AADHAR_VERIFICATION
|
||||
import com.naviapp.firebasedb.FirebaseDataHelper
|
||||
import com.naviapp.firebasedb.FirebaseDataReceiveListener
|
||||
import com.naviapp.firebasedb.FirebaseResponse
|
||||
import com.naviapp.firebasedb.FirebaseStatusType
|
||||
import com.naviapp.firebasedb.*
|
||||
import com.naviapp.firebasedb.FirebaseStatusType.SUCCESS
|
||||
import com.naviapp.firebasedb.SELFIE
|
||||
import com.naviapp.firebasedb.UPDATE_KYC_DETAILS
|
||||
import com.naviapp.getloan.activities.GetLoanActivity.Companion.BANK_DETAILS_SCREEN
|
||||
import com.naviapp.getloan.kyc.activities.SelfieCaptureActivity
|
||||
import com.naviapp.getloan.kyc.listeners.DocumentUploadListener
|
||||
import com.naviapp.getloan.kyc.listeners.SelfieCaptureListener
|
||||
import com.naviapp.getloan.kyc.models.AadhaarDetails
|
||||
import com.naviapp.getloan.kyc.models.AadhaarVerificationData
|
||||
import com.naviapp.getloan.kyc.utils.getKycDocuments
|
||||
@@ -49,18 +45,21 @@ import com.naviapp.models.RedirectPageStatus
|
||||
import com.naviapp.models.UiStatusValue
|
||||
import com.naviapp.models.request.Address
|
||||
import com.naviapp.models.request.KycRequest
|
||||
import com.naviapp.models.request.SelfieSetting
|
||||
import com.naviapp.models.request.SelfieUploadRequestData
|
||||
import com.naviapp.network.ApiConstants
|
||||
import com.naviapp.network.ApiErrorTagType
|
||||
import com.naviapp.network.models.GenericErrorResponse
|
||||
import com.naviapp.utils.*
|
||||
import com.naviapp.utils.Constants.MIN_ADDRESS_SIZE
|
||||
import com.naviapp.utils.Constants.SHOULD_SET_PROGRESS
|
||||
import kotlinx.android.synthetic.main.kyc_address_layout.view.no_rb
|
||||
import kotlinx.android.synthetic.main.kyc_address_layout.view.yes_rb
|
||||
import kotlinx.android.synthetic.main.kyc_address_layout.view.*
|
||||
import timber.log.Timber
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
|
||||
|
||||
class KycFragment : BaseFragment(), RadioGroup.OnCheckedChangeListener, View.OnClickListener,
|
||||
View.OnFocusChangeListener,
|
||||
DocumentUploadListener {
|
||||
@@ -75,6 +74,8 @@ class KycFragment : BaseFragment(), RadioGroup.OnCheckedChangeListener, View.OnC
|
||||
private var listener: FragmentInteractionListener? = null
|
||||
private var permissionsListener: NaviPermissionsListener? = null
|
||||
private val analyticsEventTracker = NaviAnalytics.naviAnalytics.Kyc()
|
||||
private var selfieCaptureListener: SelfieCaptureListener? = null
|
||||
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
@@ -182,6 +183,14 @@ class KycFragment : BaseFragment(), RadioGroup.OnCheckedChangeListener, View.OnC
|
||||
observerKycDetailUpdateStatus()
|
||||
observeSelfieUploadStatus()
|
||||
observeAdhaarVerificationData()
|
||||
observeSelfieSetting()
|
||||
}
|
||||
|
||||
private fun observeSelfieSetting() {
|
||||
viewModel.selfieSetting.observeNullable(this) {
|
||||
hideLoader()
|
||||
handleSelectSelfie(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun asyncErrorObserver() {
|
||||
@@ -292,6 +301,20 @@ class KycFragment : BaseFragment(), RadioGroup.OnCheckedChangeListener, View.OnC
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sharedVM.selfieRequestData.observeNullable(this) { data ->
|
||||
data?.let {
|
||||
if (it.imageUrl.isNullOrBlank().not())
|
||||
handleSelfieImage(it.imageUrl.orEmpty(), it)
|
||||
}
|
||||
}
|
||||
|
||||
sharedVM.selfieErrorData.observeNonNull(this) {
|
||||
hideLoader()
|
||||
if (it.isCameraPermission.orFalse()) {
|
||||
activity?.toast(R.string.selfie_permission_error, Toast.LENGTH_LONG)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observerAdhaarUploadStatus() {
|
||||
@@ -362,19 +385,40 @@ class KycFragment : BaseFragment(), RadioGroup.OnCheckedChangeListener, View.OnC
|
||||
private fun handleSelfieCapture(bundle: Bundle) {
|
||||
if (isDead(activity)) return
|
||||
bundle.getString(Constants.DATA)?.let {
|
||||
try {
|
||||
val inputStream: FileInputStream? = activity?.openFileInput(it)
|
||||
val bitmapImage = BitmapFactory.decodeStream(inputStream)
|
||||
inputStream?.close()
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
bitmapImage.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
|
||||
handleSelfieImage(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSelfieImage(
|
||||
url: String,
|
||||
requestData: SelfieUploadRequestData? = null
|
||||
) {
|
||||
try {
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
var bitmapImage: Bitmap? = null
|
||||
when (viewModel.selfieSetting.value?.provider) {
|
||||
HYPERVERGE -> {
|
||||
val imgFile = File(url)
|
||||
if (imgFile.exists()) {
|
||||
val bmOptions = BitmapFactory.Options()
|
||||
bitmapImage = BitmapFactory.decodeFile(imgFile.absolutePath, bmOptions)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val inputStream: FileInputStream? = activity?.openFileInput(url)
|
||||
bitmapImage = BitmapFactory.decodeStream(inputStream)
|
||||
inputStream?.close()
|
||||
}
|
||||
}
|
||||
bitmapImage?.let {
|
||||
it.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
|
||||
deInitializeFirebaseListener()
|
||||
showLoader()
|
||||
viewModel.submitSelfie(outputStream.toByteArray())
|
||||
Glide.with(this).asBitmap().load(bitmapImage).circleCrop().into(binding.previewIv)
|
||||
} catch (e: java.lang.Exception) {
|
||||
e.printStackTrace()
|
||||
viewModel.submitSelfie(outputStream.toByteArray(), requestData)
|
||||
Glide.with(this).asBitmap().load(it).circleCrop().into(binding.previewIv)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.log()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,12 +604,24 @@ class KycFragment : BaseFragment(), RadioGroup.OnCheckedChangeListener, View.OnC
|
||||
}
|
||||
|
||||
private fun openSelfieScreen() {
|
||||
analyticsEventTracker.onSelfieButtonTap()
|
||||
try {
|
||||
val intent = Intent(activity, SelfieCaptureActivity::class.java)
|
||||
startActivityForResult(intent, SELFIE_REQUEST_CODE)
|
||||
} catch (e: Exception) {
|
||||
e.log()
|
||||
showLoader()
|
||||
viewModel.fetchSelfieSetting()
|
||||
}
|
||||
|
||||
private fun handleSelectSelfie(selfieSetting: SelfieSetting?) {
|
||||
when (selfieSetting?.provider?.toUpperCase()) {
|
||||
HYPERVERGE -> {
|
||||
selfieCaptureListener?.onSelfiCaptureClick(selfieSetting)
|
||||
}
|
||||
else -> {
|
||||
try {
|
||||
analyticsEventTracker.onSelfieButtonTap()
|
||||
val intent = Intent(activity, SelfieCaptureActivity::class.java)
|
||||
startActivityForResult(intent, SELFIE_REQUEST_CODE)
|
||||
} catch (e: Exception) {
|
||||
e.log()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,6 +797,7 @@ class KycFragment : BaseFragment(), RadioGroup.OnCheckedChangeListener, View.OnC
|
||||
try {
|
||||
listener = context as? FragmentInteractionListener
|
||||
permissionsListener = context as? NaviPermissionsListener
|
||||
selfieCaptureListener = context as? SelfieCaptureListener
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Exception on attach")
|
||||
}
|
||||
@@ -757,5 +814,7 @@ class KycFragment : BaseFragment(), RadioGroup.OnCheckedChangeListener, View.OnC
|
||||
private const val COMPLETED = "COMPLETED"
|
||||
private const val PERMANENT = "PERMANENT"
|
||||
private const val CORRESPONDENCE = "CORRESPONDENCE"
|
||||
private const val HYPERVERGE = "HYPERVERGE"
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* *
|
||||
* * Copyright (c) 2020 . All rights reserved @Navi
|
||||
*
|
||||
*/
|
||||
|
||||
package com.naviapp.getloan.kyc.listeners
|
||||
|
||||
import com.naviapp.models.request.SelfieSetting
|
||||
import com.naviapp.models.request.SelfieUploadRequestData
|
||||
import com.naviapp.selfiecapture.SelfieErrorData
|
||||
|
||||
interface SelfieCaptureListener {
|
||||
|
||||
fun onSelfiCaptureClick(data: SelfieSetting?)
|
||||
|
||||
fun onSelfieSuccess(data: SelfieUploadRequestData, imageUri: String?)
|
||||
|
||||
fun onSelfieError(data: SelfieErrorData?)
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package com.naviapp.getloan.kyc.repositories
|
||||
|
||||
import com.naviapp.getloan.kyc.models.AadhaarVerificationData
|
||||
import com.naviapp.models.request.KycRequest
|
||||
import com.naviapp.models.request.SelfieSetting
|
||||
import com.naviapp.models.request.SelfieUploadRequestData
|
||||
import com.naviapp.models.response.UploadDataAsyncResponse
|
||||
import com.naviapp.network.models.RepoResult
|
||||
import com.naviapp.network.retrofit.ResponseCallback
|
||||
@@ -12,7 +14,10 @@ import retrofit2.http.Body
|
||||
import retrofit2.http.Part
|
||||
|
||||
class KycRepository : ResponseCallback() {
|
||||
suspend fun submitSelfie(@Part type: RequestBody, @Part file: MultipartBody.Part) =
|
||||
suspend fun submitSelfie(
|
||||
@Part type: RequestBody? = null,
|
||||
@Part file: MultipartBody.Part
|
||||
) =
|
||||
apiResponseCallback(retrofitService().submitSelfie(type, file))
|
||||
|
||||
suspend fun submitKYCDocument(@Part type: RequestBody, @Part file: MultipartBody.Part) =
|
||||
@@ -38,4 +43,7 @@ class KycRepository : ResponseCallback() {
|
||||
|
||||
suspend fun postAadhaarVerificationData(data: AadhaarVerificationData) =
|
||||
apiResponseCallback(retrofitService().postAadharVerificationData(data))
|
||||
|
||||
suspend fun fetchSelfieSettingRequestData(): RepoResult<SelfieSetting> =
|
||||
apiResponseCallback(retrofitService().fetchSelfieSettings())
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.naviapp.getloan.kyc.models.AadhaarVerificationData
|
||||
import com.naviapp.models.request.SelfieUploadRequestData
|
||||
import com.naviapp.selfiecapture.SelfieErrorData
|
||||
|
||||
class KycSharedVM : ViewModel() {
|
||||
private val _aadhaarPermission = MutableLiveData<Boolean>()
|
||||
@@ -24,6 +26,14 @@ class KycSharedVM : ViewModel() {
|
||||
val hasUserCancelled: LiveData<Boolean>
|
||||
get() = _hasUserCancelled
|
||||
|
||||
private val _selfieRequestData = MutableLiveData<SelfieUploadRequestData>()
|
||||
val selfieRequestData: LiveData<SelfieUploadRequestData>
|
||||
get() = _selfieRequestData
|
||||
|
||||
private val _selfieErrorData = MutableLiveData<SelfieErrorData>()
|
||||
val selfieErrorData: LiveData<SelfieErrorData>
|
||||
get() = _selfieErrorData
|
||||
|
||||
fun setAadhaarPermission(permissions: Boolean?) {
|
||||
_aadhaarPermission.value = permissions
|
||||
}
|
||||
@@ -36,8 +46,18 @@ class KycSharedVM : ViewModel() {
|
||||
_hasUserCancelled.value = hasUserCancelled
|
||||
}
|
||||
|
||||
fun setSelfieRquestData(data: SelfieUploadRequestData) {
|
||||
_selfieRequestData.value = data
|
||||
}
|
||||
|
||||
fun setSelfieErrorData(data: SelfieErrorData) {
|
||||
_selfieErrorData.value = data
|
||||
}
|
||||
|
||||
fun deInitData() {
|
||||
_aadhaarPermission.value = null
|
||||
_aadhaarVerificationData.value = null
|
||||
_selfieRequestData.value = null
|
||||
_selfieErrorData.value = null
|
||||
}
|
||||
}
|
||||
@@ -14,15 +14,16 @@ import com.naviapp.getloan.kyc.models.KycStatus
|
||||
import com.naviapp.getloan.kyc.repositories.KycRepository
|
||||
import com.naviapp.models.RedirectPageStatus
|
||||
import com.naviapp.models.request.KycRequest
|
||||
import com.naviapp.models.request.SelfieSetting
|
||||
import com.naviapp.models.request.SelfieUploadRequestData
|
||||
import com.naviapp.models.response.KycDetailsResponse
|
||||
import com.naviapp.models.response.UploadDataAsyncResponse
|
||||
import com.naviapp.network.ApiErrorTagType
|
||||
import com.naviapp.network.models.ErrorMessage
|
||||
import com.naviapp.network.models.GenericErrorResponse
|
||||
import com.naviapp.utils.getMultipartBody
|
||||
import com.naviapp.utils.getMultipartRequestBody
|
||||
import com.naviapp.utils.orTrue
|
||||
import com.naviapp.utils.*
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.RequestBody
|
||||
|
||||
class KycVM : BaseVM() {
|
||||
private val repository by lazy { KycRepository() }
|
||||
@@ -75,9 +76,15 @@ class KycVM : BaseVM() {
|
||||
val asyncError: LiveData<Boolean>
|
||||
get() = _asyncError
|
||||
|
||||
private val _selfieSetting = MutableLiveData<SelfieSetting>()
|
||||
val selfieSetting: LiveData<SelfieSetting>
|
||||
get() = _selfieSetting
|
||||
|
||||
fun submitSelfie(bytes: ByteArray) {
|
||||
val requestBody = getMultipartRequestBody(SELFIE_IMAGE)
|
||||
fun submitSelfie(bytes: ByteArray, requestData: SelfieUploadRequestData? = null) {
|
||||
var requestBody: RequestBody? = null
|
||||
convertObjectToJsonString(requestData)?.let {
|
||||
requestBody = getMultipartRequestJsonBody(it)
|
||||
}
|
||||
val multipartBody = getMultipartBody(bytes, FILE_NAME)
|
||||
coroutineScope.launch {
|
||||
val response = repository.submitSelfie(
|
||||
@@ -227,6 +234,21 @@ class KycVM : BaseVM() {
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchSelfieSetting() {
|
||||
coroutineScope.launch {
|
||||
val response = repository.fetchSelfieSettingRequestData()
|
||||
if (response.error == null) {
|
||||
_selfieSetting.value = response.data
|
||||
} else {
|
||||
setErrorData(
|
||||
response.errors,
|
||||
response.error,
|
||||
ApiErrorTagType.SELFIE_SETTING_API_ERROR
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KYC = "kyc"
|
||||
private const val KYC_FILE_NAME = "kyc.jpg"
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* *
|
||||
* * Copyright (c) 2020 . All rights reserved @Navi
|
||||
*
|
||||
*/
|
||||
|
||||
package com.naviapp.models.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class SelfieSetting(
|
||||
@SerializedName("provider") val provider: String? = null,
|
||||
@SerializedName("livenessMode") val livenessMode: Boolean? = null,
|
||||
@SerializedName("showInstructionsPage") val showInstructionsPage: Boolean? = null,
|
||||
@SerializedName("faceCaptureTitle") val faceCaptureTitle: String? = null,
|
||||
@SerializedName("livenessAPIParameters") val livenessAPIParameters: ArrayList<livenessParamData>? = null
|
||||
)
|
||||
|
||||
data class livenessParamData(
|
||||
@SerializedName("value") val value: String? = null,
|
||||
@SerializedName("key") val key: String? = null
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* *
|
||||
* * Copyright (c) 2020 . All rights reserved @Navi
|
||||
*
|
||||
*/
|
||||
|
||||
package com.naviapp.models.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class SelfieUploadRequestData(
|
||||
@SerializedName("live") val live: String? = null,
|
||||
@SerializedName("livenessScore") val livenessScore: Float? = null,
|
||||
@SerializedName("toBeReviewed") val toBeReviewed: String? = null,
|
||||
val imageUrl: String? = null
|
||||
)
|
||||
@@ -45,6 +45,7 @@ object ApiErrorTagType {
|
||||
const val SUBMIT_KYC_API_ERROR = "SUBMIT_KYC_API_ERROR"
|
||||
const val FAQS_FETCH_ERROR = "FAQS_FETCH_ERROR"
|
||||
const val UI_STATUS_API_ERROR = "UI_STATUS_API_ERROR"
|
||||
const val SELFIE_SETTING_API_ERROR = "SELFIE_SETTING_API_ERROR"
|
||||
|
||||
@StringDef(
|
||||
SELFIE_UPLOAD,
|
||||
@@ -80,7 +81,8 @@ object ApiErrorTagType {
|
||||
FAQS_FETCH_ERROR,
|
||||
FETCH_USER_DETAILS_ERROR,
|
||||
UI_STATUS_API_ERROR,
|
||||
NO_INTERNET_ERROR
|
||||
NO_INTERNET_ERROR,
|
||||
SELFIE_SETTING_API_ERROR
|
||||
)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class ApiErrorTagTypeDef
|
||||
|
||||
@@ -28,25 +28,7 @@ import com.naviapp.models.UserInstalledApp
|
||||
import com.naviapp.models.UserLocation
|
||||
import com.naviapp.models.UserProfile
|
||||
import com.naviapp.models.UserSms
|
||||
import com.naviapp.models.request.BankDetail
|
||||
import com.naviapp.models.request.CkycRequest
|
||||
import com.naviapp.models.request.DeviceDetail
|
||||
import com.naviapp.models.request.GenerateOfferRequest
|
||||
import com.naviapp.models.request.GenerateQuoteRequest
|
||||
import com.naviapp.models.request.InputNameRequest
|
||||
import com.naviapp.models.request.KycRequest
|
||||
import com.naviapp.models.request.LoanApplicationRequest
|
||||
import com.naviapp.models.request.LoanFeeDetailsRequest
|
||||
import com.naviapp.models.request.LoanRequest
|
||||
import com.naviapp.models.request.LoginRequest
|
||||
import com.naviapp.models.request.MoratoriumRequest
|
||||
import com.naviapp.models.request.OtpRequest
|
||||
import com.naviapp.models.request.PanRequest
|
||||
import com.naviapp.models.request.ProfileRequest
|
||||
import com.naviapp.models.request.RateDataRequest
|
||||
import com.naviapp.models.request.RegisterRequest
|
||||
import com.naviapp.models.request.UserBasicDetailsRequest
|
||||
import com.naviapp.models.request.WorkRequest
|
||||
import com.naviapp.models.request.*
|
||||
import com.naviapp.models.response.AdditionalDataAsyncResponse
|
||||
import com.naviapp.models.response.AppUpgradeResponse
|
||||
import com.naviapp.models.response.ApplicationIdResponse
|
||||
@@ -221,8 +203,9 @@ interface RetrofitService {
|
||||
@Multipart
|
||||
@POST("/customer-service/customers/me/selfie")
|
||||
suspend fun submitSelfie(
|
||||
@Part("type") type: RequestBody,
|
||||
@Part("details") type: RequestBody? = null,
|
||||
@Part file: MultipartBody.Part
|
||||
|
||||
): Response<GenericResponse<UploadDataAsyncResponse>>
|
||||
|
||||
@Multipart
|
||||
@@ -420,4 +403,7 @@ interface RetrofitService {
|
||||
@POST("customer-service/customers/me/ratings")
|
||||
suspend fun postRatingReview(@Body request: RateDataRequest): Response<GenericResponse<RatingData>>
|
||||
|
||||
@GET("customer-service/customers/selfie-settings")
|
||||
suspend fun fetchSelfieSettings(): Response<GenericResponse<SelfieSetting>>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* *
|
||||
* * Copyright (c) 2020 . All rights reserved @Navi
|
||||
*
|
||||
*/
|
||||
|
||||
package com.naviapp.selfiecapture
|
||||
|
||||
data class SelfieErrorData(
|
||||
val isError: Boolean? = null,
|
||||
val isCameraPermission: Boolean? = null
|
||||
)
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* *
|
||||
* * Copyright (c) 2020 . All rights reserved @Navi
|
||||
*
|
||||
*/
|
||||
|
||||
package com.naviapp.selfiecapture
|
||||
|
||||
import android.app.Activity
|
||||
import co.hyperverge.hypersnapsdk.activities.HVFaceActivity
|
||||
import co.hyperverge.hypersnapsdk.listeners.FaceCaptureCompletionHandler
|
||||
import co.hyperverge.hypersnapsdk.objects.HVError
|
||||
import co.hyperverge.hypersnapsdk.objects.HVFaceConfig
|
||||
import com.naviapp.getloan.kyc.listeners.SelfieCaptureListener
|
||||
import com.naviapp.models.request.SelfieSetting
|
||||
import com.naviapp.models.request.SelfieUploadRequestData
|
||||
import com.naviapp.sharedpref.PreferenceManager
|
||||
import com.naviapp.utils.*
|
||||
import org.json.JSONObject
|
||||
|
||||
class SelfieVerificationHelper {
|
||||
|
||||
private var listener: SelfieCaptureListener? = null
|
||||
|
||||
fun startFaceCapture(
|
||||
activity: Activity,
|
||||
listener: SelfieCaptureListener?,
|
||||
data: SelfieSetting?
|
||||
) {
|
||||
try {
|
||||
val introShownCount =
|
||||
PreferenceManager.getIntPreference(SELFIE_INTRO_SHOWN_COUNT).orZero()
|
||||
this.listener = listener
|
||||
val hvFaceConfig = HVFaceConfig()
|
||||
data?.let {
|
||||
hvFaceConfig.setLivenessMode(
|
||||
if (it.livenessMode.orTrue())
|
||||
HVFaceConfig.LivenessMode.TEXTURELIVENESS
|
||||
else
|
||||
HVFaceConfig.LivenessMode.NONE
|
||||
)
|
||||
if (introShownCount < MAX_NO_SLEFIE_INTRO)
|
||||
hvFaceConfig.isShouldShowInstructionPage = it.showInstructionsPage.orFalse()
|
||||
else
|
||||
hvFaceConfig.isShouldShowInstructionPage = false
|
||||
|
||||
if (it.faceCaptureTitle.isNullOrBlank().not())
|
||||
hvFaceConfig.faceCaptureTitle = it.faceCaptureTitle
|
||||
|
||||
if (it.livenessAPIParameters?.size.orZero() > 0) {
|
||||
val params = JSONObject()
|
||||
it.livenessAPIParameters?.forEach { data ->
|
||||
data.key?.let { key ->
|
||||
params.put(key, data.value)
|
||||
}
|
||||
}
|
||||
hvFaceConfig.setLivenessAPIParameters(params)
|
||||
}
|
||||
|
||||
} ?: run {
|
||||
hvFaceConfig.setLivenessMode(HVFaceConfig.LivenessMode.TEXTURELIVENESS)
|
||||
hvFaceConfig.isShouldShowInstructionPage = false
|
||||
}
|
||||
|
||||
val completionCallback =
|
||||
FaceCaptureCompletionHandler { error, result, headers ->
|
||||
error?.let {
|
||||
when (it.errorCode) {
|
||||
HVError.OPERATION_CANCELLED_BY_USER_ERROR -> {
|
||||
listener?.onSelfieError(SelfieErrorData(isError = true))
|
||||
}
|
||||
HVError.PERMISSIONS_NOT_GRANTED_ERROR -> {
|
||||
listener?.onSelfieError(SelfieErrorData(isCameraPermission = true))
|
||||
}
|
||||
else -> {
|
||||
listener?.onSelfieError(SelfieErrorData(isError = true))
|
||||
}
|
||||
}
|
||||
}
|
||||
result?.let { data ->
|
||||
listener?.onSelfieSuccess(
|
||||
populateSelfieResponse(data),
|
||||
data.getString("imageUri")
|
||||
)
|
||||
}
|
||||
if (introShownCount < MAX_NO_SLEFIE_INTRO)
|
||||
PreferenceManager.setIntPreference(
|
||||
SELFIE_INTRO_SHOWN_COUNT,
|
||||
introShownCount + 1
|
||||
)
|
||||
}
|
||||
HVFaceActivity.start(activity, hvFaceConfig, completionCallback)
|
||||
} catch (e: Exception) {
|
||||
e.log()
|
||||
}
|
||||
}
|
||||
|
||||
private fun populateSelfieResponse(jsonData: JSONObject): SelfieUploadRequestData {
|
||||
try {
|
||||
val data = jsonData.getJSONObject("result")
|
||||
val selfieUploadRequestData = SelfieUploadRequestData(
|
||||
live = data.getString("live"),
|
||||
toBeReviewed = data.getString("to-be-reviewed"),
|
||||
livenessScore = data.getString("liveness-score").toFloat(),
|
||||
imageUrl = jsonData.getString("imageUri")
|
||||
)
|
||||
return selfieUploadRequestData
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.log()
|
||||
}
|
||||
return SelfieUploadRequestData(live = "no")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MAX_NO_SLEFIE_INTRO = 1
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,8 @@ import com.naviapp.network.retrofit.RetrofitService
|
||||
import com.naviapp.utils.Constants.INR
|
||||
import java.text.DecimalFormat
|
||||
|
||||
fun Context.toast(@StringRes message: Int) =
|
||||
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
||||
fun Context.toast(@StringRes message: Int, toastLength: Int = Toast.LENGTH_SHORT) =
|
||||
Toast.makeText(this, message, toastLength).show()
|
||||
|
||||
fun Context.toast(
|
||||
message: CharSequence?,
|
||||
|
||||
@@ -23,3 +23,4 @@ const val TERMS_AND_CONDITIONS_CHECKED = "TERMS_AND_CONDITIONS_CHECKED"
|
||||
const val PERMISSION_SCREEN_SHOWN = "PERMISSION_SCREEN_SHOWN"
|
||||
const val LOCATION = "LOCATION"
|
||||
const val APP_INSTALLED = "APP_INSTALLED"
|
||||
const val SELFIE_INTRO_SHOWN_COUNT = "SELFIE_INTRO_SHOWN_COUNT"
|
||||
|
||||
@@ -19,7 +19,6 @@ import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import com.google.firebase.auth.FirebaseAuth
|
||||
import com.google.gson.Gson
|
||||
import com.naviapp.analytics.moengage.MoengageUtil
|
||||
@@ -168,6 +167,12 @@ fun getMultipartBody(bytes: ByteArray, fileName: String): MultipartBody.Part {
|
||||
return MultipartBody.Part.createFormData("file", fileName, reqFile)
|
||||
}
|
||||
|
||||
fun getMultipartRequestJsonBody(content: String): RequestBody {
|
||||
return RequestBody.create(
|
||||
"application/json".toMediaTypeOrNull(), content
|
||||
)
|
||||
}
|
||||
|
||||
fun <T> convertObjectToJson(type: T): JSONObject? {
|
||||
val gson = Gson()
|
||||
val jsonString = gson.toJson(type)
|
||||
@@ -224,4 +229,14 @@ private fun getPackgName(): String {
|
||||
} catch (e: Exception) {
|
||||
return "com.naviapp"
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> convertObjectToJsonString(type: T): String? {
|
||||
val gson = Gson()
|
||||
val jsonString = gson.toJson(type)
|
||||
try {
|
||||
return jsonString
|
||||
} catch (e: JSONException) {
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -391,4 +391,14 @@
|
||||
<string name="supported_banks">Supported banks</string>
|
||||
<string name="this_bank_is_not_supported">This bank is not supported. Kindly select some other bank.</string>
|
||||
|
||||
<!-- Customisation strings in SDK -->
|
||||
<!-- Face capture screen -->
|
||||
<string name="selfie_title">Selfie Capture</string>
|
||||
<string name="faceCaptureFaceFound">Capture Now</string>
|
||||
<string name="faceCaptureFaceNotFound">Place your face within circle</string>
|
||||
<string name="faceCaptureMoveAway"> Move away from the phone</string>
|
||||
<string name="faceCaptureActivity">Processing</string>
|
||||
|
||||
<string name="selfie_permission_error">Please give camera permissions from phone settings</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -32,6 +32,13 @@ allprojects {
|
||||
}
|
||||
url "https://nexus.cmd.navi-tech.in/repository/maven-snapshots"
|
||||
}
|
||||
maven {
|
||||
url "s3://hvsdk/android/releases"
|
||||
credentials(AwsCredentials) {
|
||||
accessKey "AKIAIIQJNVDGKVQWN62Q"
|
||||
secretKey "PnjCs8szvYWjKBOJzs7F0J4zn9TgxrMj0bDWzhAk"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user