diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 468e16ac..8932a8ed 100644 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ diff --git a/app/build.gradle b/app/build.gradle index f5a09e53..5b419e61 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -153,7 +153,7 @@ dependencies { implementation 'com.android.support:cardview-v7:28.0.0' implementation 'com.theartofdev.edmodo:android-image-cropper:2.7.0' implementation 'com.scottyab:rootbeer-lib:0.0.7' - implementation 'com.github.heasley:RxFingerprint:1.0.1' + implementation 'com.mtramin:rxfingerprint:2.2.1' implementation project(':securitykeypad') implementation project(':SpinnerDatePickerLib-release') } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4c5e19c3..821ac9d4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,6 +11,9 @@ + + + showProgressBar(true,getString(R.string.processing_request_text))) + .doFinally(()->showProgressBar(false,"")) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(FingerprintEncryptionResult fingerprintEncryptionResult) { + if(fingerprintEncryptionResult.isSuccess()) { + showPopUpMessage("Now you can use your fingerprint to perform transactions",CustomAlertDialog.AlertType.SUCCESS,action->GmeApplication.getStorage().edit().putString(PrefKeys.APP_USER_SECRET_KEY, fingerprintEncryptionResult.getEncrypted()).apply()); + } + else + showPopUpMessage(fingerprintEncryptionResult.getMessage(),CustomAlertDialog.AlertType.FAILED,null); + } + + @Override + public void onError(Throwable e) { + showPopUpMessage(e.getMessage(),CustomAlertDialog.AlertType.FAILED,null); + } + + @Override + public void onComplete() { + + } + }); + } + + private void promptLanguageSelectionDialog() { LanguageSelectionListingDialog languageSelectionDialog = new LanguageSelectionListingDialog(); diff --git a/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/gateway/TransactionPasswordPromptV2Gateway.java b/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/gateway/TransactionPasswordPromptV2Gateway.java index 1b81ef72..23a12e47 100644 --- a/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/gateway/TransactionPasswordPromptV2Gateway.java +++ b/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/gateway/TransactionPasswordPromptV2Gateway.java @@ -1,5 +1,6 @@ package com.gmeremit.online.gmeremittance_native.transactionpasspromt.gateway; +import com.gmeremit.online.gmeremittance_native.GmeApplication; import com.gmeremit.online.gmeremittance_native.base.PrivilegedGateway; import com.gmeremit.online.gmeremittance_native.transactionpasspromt.presenter.TransactionPasswordPromptV2InteractorInterface; import com.gmeremit.online.gmeremittance_native.utils.https.HttpClientV2; @@ -8,6 +9,9 @@ import com.google.gson.JsonObject; import io.reactivex.Observable; import okhttp3.ResponseBody; +import static com.gmeremit.online.gmeremittance_native.base.PrefKeys.APP_FINGER_PRINT_ENABLED; +import static com.gmeremit.online.gmeremittance_native.base.PrefKeys.APP_USER_SECRET_KEY; + public class TransactionPasswordPromptV2Gateway extends PrivilegedGateway implements TransactionPasswordPromptV2InteractorInterface.TransactionPasswordPromptV2GatewayInterface { private final TransactionPasswordPromptV2InteractorInterface interactor; @@ -25,4 +29,14 @@ public class TransactionPasswordPromptV2Gateway extends PrivilegedGateway implem return HttpClientV2.getInstance().requestOTPForSendMoney(auth,jsonObject); } + + @Override + public boolean isFingerPrintEnabled() { + return GmeApplication.getStorage().getBoolean(APP_FINGER_PRINT_ENABLED,false); + } + + @Override + public String getStoredSecretKey() { + return GmeApplication.getStorage().getString(APP_USER_SECRET_KEY,""); + } } diff --git a/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/presenter/TransactionPasswordPromptV2InteractorInterface.java b/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/presenter/TransactionPasswordPromptV2InteractorInterface.java index c1b4d4c9..87d6307e 100644 --- a/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/presenter/TransactionPasswordPromptV2InteractorInterface.java +++ b/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/presenter/TransactionPasswordPromptV2InteractorInterface.java @@ -11,5 +11,9 @@ public interface TransactionPasswordPromptV2InteractorInterface extends BaseInte interface TransactionPasswordPromptV2GatewayInterface extends PrivilegedGatewayInterface { Observable requestOtp(String auth, String Uid,String amount,String kftc); + + boolean isFingerPrintEnabled(); + + String getStoredSecretKey(); } } diff --git a/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/presenter/TransactionPasswordPromptV2Presenter.java b/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/presenter/TransactionPasswordPromptV2Presenter.java index 67836a4c..d06dc02a 100644 --- a/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/presenter/TransactionPasswordPromptV2Presenter.java +++ b/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/presenter/TransactionPasswordPromptV2Presenter.java @@ -8,6 +8,7 @@ import com.gmeremit.online.gmeremittance_native.utils.Constants; import com.gmeremit.online.gmeremittance_native.utils.https.GenericApiObserverResponseV2; import com.gmeremit.online.gmeremittance_native.utils.https.GenericResponseDataModel; import com.gmeremit.online.gmeremittance_native.utils.https.MessageResponseDataModel; +import com.gmeremit.online.gmeremittance_native.utils.security.SecurityUtils; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; @@ -16,7 +17,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; -public class TransactionPasswordPromptV2Presenter extends BasePresenter implements TransactionPasswordPromptV2PresenterInterface,TransactionPasswordPromptV2InteractorInterface { +public class TransactionPasswordPromptV2Presenter extends BasePresenter implements TransactionPasswordPromptV2PresenterInterface, TransactionPasswordPromptV2InteractorInterface { private final TransactionPasswordPromptV2PresenterInterface.TransactionPasswordPromptV2ContractInterface view; private final TransactionPasswordPromptV2InteractorInterface.TransactionPasswordPromptV2GatewayInterface gateway; @@ -26,29 +27,27 @@ public class TransactionPasswordPromptV2Presenter extends BasePresenter implemen private String selectedKftcId; public TransactionPasswordPromptV2Presenter(TransactionPasswordPromptV2PresenterInterface.TransactionPasswordPromptV2ContractInterface view) { - this.view=view; - this.gateway=new TransactionPasswordPromptV2Gateway(this); + this.view = view; + this.gateway = new TransactionPasswordPromptV2Gateway(this); this.compositeDisposable = new CompositeDisposable(); - this.updateCountdownValue =-1; - this.selectedKftcId=""; - this.selectedAmount=""; + this.updateCountdownValue = -1; + this.selectedKftcId = ""; + this.selectedAmount = ""; } @Override public void getOtp() { - if(updateCountdownValue==-1) { + if (updateCountdownValue == -1) { this.compositeDisposable.add( - this.gateway.requestOtp(this.gateway.getAuth(), this.gateway.getUserID(),selectedAmount,selectedKftcId) + this.gateway.requestOtp(this.gateway.getAuth(), this.gateway.getUserID(), selectedAmount, selectedKftcId) .doOnSubscribe(disposable -> view.showProgressBar(true, getStringfromStringId(R.string.requesting_otp_text))) .doFinally(() -> view.showProgressBar(false, "")) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new ResendRequestObserver()) ); - } - else - { + } else { view.showResendRequestView(false); view.startCountdownView(updateCountdownValue); } @@ -57,7 +56,7 @@ public class TransactionPasswordPromptV2Presenter extends BasePresenter implemen @Override public void updateCountdown(long millisUntilFinished) { - this.updateCountdownValue =millisUntilFinished; + this.updateCountdownValue = millisUntilFinished; } @Override @@ -67,26 +66,33 @@ public class TransactionPasswordPromptV2Presenter extends BasePresenter implemen @Override public void updateSelectedAmount(String selectedAmount) { - if(selectedAmount==null) - selectedAmount=""; - this.selectedAmount=selectedAmount; + if (selectedAmount == null) + selectedAmount = ""; + this.selectedAmount = selectedAmount; } @Override public void updateSelectedKftcId(String selectedKftcId) { - if(selectedKftcId==null) - selectedKftcId=""; - this.selectedKftcId=selectedKftcId; + if (selectedKftcId == null) + selectedKftcId = ""; + this.selectedKftcId = selectedKftcId; } @Override public boolean checkIfUserHasEnabledBiometricAuth() { return true; +// return gateway.isFingerPrintEnabled()&&SecurityUtils.doesAppHasBiometricFeature(view.getContext()); } + @Override + public String getSecretKey() { + return gateway.getStoredSecretKey(); + - class ResendRequestObserver extends GenericApiObserverResponseV2 - { + } + + + class ResendRequestObserver extends GenericApiObserverResponseV2 { @Override protected Type setType() { @@ -100,8 +106,7 @@ public class TransactionPasswordPromptV2Presenter extends BasePresenter implemen view.showResendRequestView(false); view.startCountdownView(120000); }); - } - else { + } else { view.showPopUpMessage(response.getMsg(), CustomAlertDialog.AlertType.FAILED, alertType -> { view.showResendRequestView(true); }); diff --git a/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/presenter/TransactionPasswordPromptV2PresenterInterface.java b/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/presenter/TransactionPasswordPromptV2PresenterInterface.java index 9dea1e78..adc2aa86 100644 --- a/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/presenter/TransactionPasswordPromptV2PresenterInterface.java +++ b/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/presenter/TransactionPasswordPromptV2PresenterInterface.java @@ -1,5 +1,7 @@ package com.gmeremit.online.gmeremittance_native.transactionpasspromt.presenter; +import android.content.Context; + import com.gmeremit.online.gmeremittance_native.base.BaseContractInterface; import com.gmeremit.online.gmeremittance_native.base.BasePresenterInterface; @@ -16,10 +18,17 @@ public interface TransactionPasswordPromptV2PresenterInterface extends BasePrese boolean checkIfUserHasEnabledBiometricAuth(); + String getSecretKey(); + + interface TransactionPasswordPromptV2ContractInterface extends BaseContractInterface { void startCountdownView(long durationMillisecond); void showResendRequestView(boolean action); + Context getContext(); + + + } } diff --git a/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/view/TransactionPasswordPromptActivity.java b/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/view/TransactionPasswordPromptActivity.java index c0912ac9..4aaf5de4 100644 --- a/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/view/TransactionPasswordPromptActivity.java +++ b/app/src/main/java/com/gmeremit/online/gmeremittance_native/transactionpasspromt/view/TransactionPasswordPromptActivity.java @@ -2,11 +2,11 @@ package com.gmeremit.online.gmeremittance_native.transactionpasspromt.view; import android.annotation.SuppressLint; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.graphics.Rect; import android.graphics.drawable.AnimatedVectorDrawable; -import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.os.CountDownTimer; @@ -29,6 +29,7 @@ import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; +import com.gmeremit.online.gmeremittance_native.GmeApplication; import com.gmeremit.online.gmeremittance_native.R; import com.gmeremit.online.gmeremittance_native.base.BaseActivity; import com.gmeremit.online.gmeremittance_native.customwidgets.GmeErrorTextView; @@ -38,12 +39,21 @@ import com.gmeremit.online.gmeremittance_native.utils.security.SecurityUtils; import com.gmeremit.online.gmeremittance_native.utils.security.securitykeypad.SecurityKeyboardManager; import com.gmeremit.online.gmeremittance_native.utils.security.securitykeypad.SecurityKeyboardView; import com.gmeremit.online.gmeremittance_native.utils.security.securitykeypad.SecurityKeypadRequestParamBuilder; +import com.mtramin.rxfingerprint.RxFingerprint; +import com.mtramin.rxfingerprint.data.FingerprintDecryptionResult; +import com.mtramin.rxfingerprint.data.FingerprintEncryptionResult; import com.softsecurity.transkey.TransKeyActivity; import java.util.Locale; import butterknife.BindView; import butterknife.ButterKnife; +import io.reactivex.Observable; +import io.reactivex.ObservableSource; +import io.reactivex.Observer; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Function; +import io.reactivex.observers.DisposableObserver; public class TransactionPasswordPromptActivity extends BaseActivity implements SecurityKeyboardView.SecurityKeyboardFocusStateListener, TransactionPasswordPromptV2PresenterInterface.TransactionPasswordPromptV2ContractInterface, View.OnClickListener { @@ -91,6 +101,9 @@ public class TransactionPasswordPromptActivity extends BaseActivity implements S private CountDownTimer countDownTimer; private AnimatedVectorDrawable fingerPrintAVD; private AnimatedVectorDrawableCompat fingerPrintAVDCompat; + private Disposable fingerPrintReaderSubs; + + String TAG = "FingerPrintObserver"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -169,14 +182,14 @@ public class TransactionPasswordPromptActivity extends BaseActivity implements S } private void setupFingerPrintIconIfRequired() { - //TODO if biometric is on - if (SecurityUtils.doesAppHasBiometricFeature(this) && presenter.checkIfUserHasEnabledBiometricAuth()) { + if (presenter.checkIfUserHasEnabledBiometricAuth()) { orTextView.setVisibility(View.VISIBLE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { fingerPrintAVD = (AnimatedVectorDrawable) ContextCompat.getDrawable(this, R.drawable.avd_fingerprint_off_to_on); } else { fingerPrintAVDCompat = (AnimatedVectorDrawableCompat) ContextCompat.getDrawable(this, R.drawable.avd_fingerprint_off_to_on); } + } else { passwordPromptImageView.setBackgroundResource(R.drawable.ic_penny_test_pending); } @@ -222,6 +235,8 @@ public class TransactionPasswordPromptActivity extends BaseActivity implements S securityKeyboardView.setSecurityKeyboardFocusStateListener(this); if (checkIfRequiredOTPScreen()) resendRequest.setOnClickListener(this); + else if (presenter.checkIfUserHasEnabledBiometricAuth()) + fingerPrintReaderSubs = startObservingFingerPrintScanner(); } @Override @@ -230,35 +245,63 @@ public class TransactionPasswordPromptActivity extends BaseActivity implements S securityKeyboardView.setSecurityKeyboardFocusStateListener(null); if (checkIfRequiredOTPScreen()) resendRequest.setOnClickListener(null); + if (fingerPrintReaderSubs != null && !fingerPrintReaderSubs.isDisposed()) + fingerPrintReaderSubs.dispose(); } @Override protected void onResume() { super.onResume(); - new Handler().postDelayed(this::animateFingerPrintAppearAnimationIfRequired, 400); + animateFingerPrintAppearAnimationIfRequired(true); } @Override protected void onPause() { super.onPause(); - if (!checkIfRequiredOTPScreen() && SecurityUtils.doesAppHasBiometricFeature(this) && presenter.checkIfUserHasEnabledBiometricAuth()) - new Handler().postDelayed(() -> passwordPromptImageView.setImageDrawable(null), 200); + animateFingerPrintAppearAnimationIfRequired(false); + } @SuppressLint("NewApi") - private void animateFingerPrintAppearAnimationIfRequired() { - if (!checkIfRequiredOTPScreen()) { - if (fingerPrintAVD != null && !fingerPrintAVD.isRunning()) { - passwordPromptImageView.setImageDrawable(fingerPrintAVD); - fingerPrintAVD.start(); - } else if (fingerPrintAVDCompat != null && !fingerPrintAVDCompat.isRunning()) { - passwordPromptImageView.setImageDrawable(fingerPrintAVDCompat); - fingerPrintAVDCompat.start(); - } + private void animateFingerPrintAppearAnimationIfRequired(boolean action) { + if (!checkIfRequiredOTPScreen() && presenter.checkIfUserHasEnabledBiometricAuth() && action) { + new Handler().postDelayed(() -> { + if (fingerPrintAVD != null && !fingerPrintAVD.isRunning()) { + passwordPromptImageView.setImageDrawable(fingerPrintAVD); + fingerPrintAVD.start(); + } else if (fingerPrintAVDCompat != null && !fingerPrintAVDCompat.isRunning()) { + passwordPromptImageView.setImageDrawable(fingerPrintAVDCompat); + fingerPrintAVDCompat.start(); + } + + }, 400); + } else { + new Handler().postDelayed(() -> { + passwordPromptImageView.setImageDrawable(null); + }, 200); } } + private Disposable startObservingFingerPrintScanner() { + return SecurityUtils.decryptSecret(this, presenter.getSecretKey()) + .flatMap(authentication -> { + Log.d(TAG, authentication.getResult().toString()); + switch (authentication.getResult()) { + case FAILED: + return io.reactivex.Observable.error(new SecurityUtils.FailedFingerPrintException(authentication.getMessage())); + case HELP: + return io.reactivex.Observable.error(new SecurityUtils.SensorNotReadyException(authentication.getMessage())); + case AUTHENTICATED: + return Observable.just(authentication); + default: + return io.reactivex.Observable.error(new Throwable(authentication.getMessage())); + } + + }) + .subscribeWith(new FingerPrintScannerObserver()); + } + @Override public void onSecurityViewRecievedFocus() { if (securityKeyboardManager != null && !securityKeyboardManager.isKeyboardVisible()) @@ -338,6 +381,13 @@ public class TransactionPasswordPromptActivity extends BaseActivity implements S } } + @Override + public Context getContext() { + return this; + } + + + @Override public void onBackPressed() { @@ -416,4 +466,26 @@ public class TransactionPasswordPromptActivity extends BaseActivity implements S } + + class FingerPrintScannerObserver extends DisposableObserver { + + + @Override + public void onNext(FingerprintDecryptionResult fingerprintAuthenticationResult) { + Intent returnIntent = new Intent(); + returnIntent.putExtra(TRANSACTION_PWD_ENC_DATA, fingerprintAuthenticationResult.getDecrypted()); + setResult(Activity.RESULT_OK, returnIntent); + finish(); + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + } } diff --git a/app/src/main/java/com/gmeremit/online/gmeremittance_native/utils/security/SecurityUtils.java b/app/src/main/java/com/gmeremit/online/gmeremittance_native/utils/security/SecurityUtils.java index ceb31f27..cd4f87a1 100644 --- a/app/src/main/java/com/gmeremit/online/gmeremittance_native/utils/security/SecurityUtils.java +++ b/app/src/main/java/com/gmeremit/online/gmeremittance_native/utils/security/SecurityUtils.java @@ -2,13 +2,53 @@ package com.gmeremit.online.gmeremittance_native.utils.security; import android.content.Context; +import com.mtramin.rxfingerprint.EncryptionMethod; import com.mtramin.rxfingerprint.RxFingerprint; +import com.mtramin.rxfingerprint.data.FingerprintAuthenticationResult; +import com.mtramin.rxfingerprint.data.FingerprintDecryptionResult; +import com.mtramin.rxfingerprint.data.FingerprintEncryptionResult; + +import io.reactivex.Observable; + public class SecurityUtils { + private static final String KEY_STORE_SECRET_ALIAS="gmeSecret321"; + public static boolean doesAppHasBiometricFeature(Context context) { -// return RxFingerprint.isAvailable(context); - return true; + return RxFingerprint.isAvailable(context); + } + + public static Observable encryptSecret(Context context, String secret) + { + return RxFingerprint.encrypt(EncryptionMethod.RSA,context,KEY_STORE_SECRET_ALIAS,secret); } + + public static Observable decryptSecret(Context context, String secret) + { + return RxFingerprint.decrypt(EncryptionMethod.RSA,context,KEY_STORE_SECRET_ALIAS,secret); + } + + public static Observable authenticateFingerPrint(Context context) + { + return RxFingerprint.authenticate(context); + } + + + public static class SensorNotReadyException extends Exception + { + public SensorNotReadyException(String message) { + super(message); + } + } + + public static class FailedFingerPrintException extends Exception + { + + public FailedFingerPrintException(String message) { + super(message); + } + } + } diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 11aa6dc2..45c6699e 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -135,6 +135,12 @@ android:textColor="@color/darkgray" android:textSize="14sp" /> + +