Prompt to setup payment bioauth, require to disable payment lock.

This commit is contained in:
Varsha
2022-09-29 17:13:10 -07:00
committed by Greyson Parrelli
parent f63ce79f16
commit afe36b982f
13 changed files with 278 additions and 6 deletions

View File

@@ -5,6 +5,7 @@ import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.text.SpannableStringBuilder
import android.text.Spanned
@@ -13,7 +14,11 @@ import android.view.View
import android.view.WindowManager
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.StringRes
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.biometric.BiometricPrompt.PromptInfo
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.Navigation
@@ -25,6 +30,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import mobi.upod.timedurationpicker.TimeDurationPicker
import mobi.upod.timedurationpicker.TimeDurationPickerDialog
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.BiometricDeviceAuthentication
import org.thoughtcrime.securesms.BiometricDeviceLockContract
import org.thoughtcrime.securesms.PassphraseChangeActivity
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.settings.ClickPreference
@@ -59,6 +66,8 @@ private val TAG = Log.tag(PrivacySettingsFragment::class.java)
class PrivacySettingsFragment : DSLSettingsFragment(R.string.preferences__privacy) {
private lateinit var viewModel: PrivacySettingsViewModel
private lateinit var biometricAuth: BiometricDeviceAuthentication
private lateinit var biometricDeviceLockLauncher: ActivityResultLauncher<String>
private val incognitoSummary: CharSequence by lazy {
SpannableStringBuilder(getString(R.string.preferences__this_setting_is_not_a_guarantee))
@@ -70,11 +79,35 @@ class PrivacySettingsFragment : DSLSettingsFragment(R.string.preferences__privac
)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
biometricDeviceLockLauncher = registerForActivityResult(BiometricDeviceLockContract()) { result: Int ->
if (result == BiometricDeviceAuthentication.AUTHENTICATED) {
viewModel.togglePaymentLock(false)
}
}
val promptInfo = PromptInfo.Builder()
.setAllowedAuthenticators(BiometricDeviceAuthentication.ALLOWED_AUTHENTICATORS)
.setTitle(requireContext().getString(R.string.BiometricDeviceAuthentication__signal))
.setConfirmationRequired(false)
.build()
biometricAuth = BiometricDeviceAuthentication(
BiometricManager.from(requireActivity()),
BiometricPrompt(requireActivity(), BiometricAuthenticationListener()),
promptInfo
)
}
override fun onResume() {
super.onResume()
viewModel.refreshBlockedCount()
}
override fun onPause() {
super.onPause()
biometricAuth.cancelAuthentication()
}
override fun bindAdapter(adapter: MappingAdapter) {
adapter.registerFactory(ValueClickPreference::class.java, LayoutFactory(::ValueClickPreferenceViewHolder, R.layout.value_click_preference_item))
@@ -323,8 +356,10 @@ class PrivacySettingsFragment : DSLSettingsFragment(R.string.preferences__privac
onClick = {
if (!ServiceUtil.getKeyguardManager(requireContext()).isKeyguardSecure) {
showGoToPhoneSettings()
} else if (state.paymentLock) {
biometricAuth.authenticate(requireContext(), true) { biometricDeviceLockLauncher?.launch(getString(R.string.BiometricDeviceAuthentication__signal)) }
} else {
viewModel.togglePaymentLock()
viewModel.togglePaymentLock(true)
}
}
)
@@ -512,4 +547,20 @@ class PrivacySettingsFragment : DSLSettingsFragment(R.string.preferences__privac
valueText.text = model.value.resolve(context)
}
}
inner class BiometricAuthenticationListener : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errorString: CharSequence) {
Log.w(TAG, "Authentication error: $errorCode")
onAuthenticationFailed()
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
Log.i(TAG, "onAuthenticationSucceeded")
viewModel.togglePaymentLock(false)
}
override fun onAuthenticationFailed() {
Log.w(TAG, "Unable to authenticate payment lock")
}
}
}

View File

@@ -74,8 +74,8 @@ class PrivacySettingsViewModel(
refresh()
}
fun togglePaymentLock() {
SignalStore.paymentsValues().paymentLock = state.value?.let { !it.paymentLock } ?: false
fun togglePaymentLock(enable: Boolean) {
SignalStore.paymentsValues().paymentLock = enable
refresh()
}

View File

@@ -55,6 +55,7 @@ internal class PaymentsValues internal constructor(store: KeyValueStore) : Signa
const val MOB_PAYMENTS_ENABLED = "mob_payments_enabled"
}
@get:JvmName("isPaymentLockEnabled")
var paymentLock: Boolean by booleanValue(PAYMENT_LOCK_ENABLED, false)
var paymentLockTimestamp: Long by longValue(PAYMENT_LOCK_TIMESTAMP, 0)
var paymentLockSkipCount: Int by integerValue(PAYMENT_LOCK_SKIP_COUNT, 0)

View File

@@ -34,7 +34,7 @@ object MediaIntentFactory {
val initialCaption: String? = null,
val leftIsRecent: Boolean = false,
val hideAllMedia: Boolean = false,
val showThread: Boolean= false,
val showThread: Boolean = false,
val sorting: Int,
val isVideoGif: Boolean
) : Parcelable

View File

@@ -65,7 +65,7 @@ public class PaymentsRecoveryStartFragment extends Fragment {
message.setText(getDescription(state));
message.setLink(getString(R.string.PaymentsRecoveryStartFragment__learn_more__view));
startButton.setOnClickListener(v -> {
if (state == RecoveryPhraseStates.FROM_PAYMENTS_MENU_WITH_MNEMONIC_CONFIRMED && ServiceUtil.getKeyguardManager(requireContext()).isKeyguardSecure() && SignalStore.paymentsValues().getPaymentLock()) {
if (state == RecoveryPhraseStates.FROM_PAYMENTS_MENU_WITH_MNEMONIC_CONFIRMED && ServiceUtil.getKeyguardManager(requireContext()).isKeyguardSecure() && SignalStore.paymentsValues().isPaymentLockEnabled()) {
BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo
.Builder()
.setAllowedAuthenticators(BiometricDeviceAuthentication.ALLOWED_AUTHENTICATORS)

View File

@@ -211,7 +211,7 @@ public class ConfirmPaymentFragment extends BottomSheetDialogFragment {
private boolean isPaymentLockEnabled(Context context) {
return SignalStore.paymentsValues().getPaymentLock() && ServiceUtil.getKeyguardManager(context).isKeyguardSecure();
return SignalStore.paymentsValues().isPaymentLockEnabled() && ServiceUtil.getKeyguardManager(context).isKeyguardSecure();
}
private class Callbacks implements ConfirmPaymentAdapter.Callbacks {

View File

@@ -222,6 +222,9 @@ public class PaymentsHomeFragment extends LoggingFragment {
});
break;
case ACTIVATED:
if (!SignalStore.paymentsValues().isPaymentLockEnabled()) {
SafeNavigation.safeNavigate(NavHostFragment.findNavController(this), R.id.action_paymentsHome_to_securitySetup);
}
return;
default:
throw new IllegalStateException("Unsupported event type: " + paymentStateEvent.name());

View File

@@ -203,6 +203,7 @@ public class PaymentsHomeViewModel extends ViewModel {
@Override
public void onComplete(@Nullable Void result) {
store.update(state -> state.updatePaymentsEnabled(PaymentsHomeState.PaymentsState.ACTIVATED));
paymentStateEvents.postValue(PaymentStateEvent.ACTIVATED);
}
@Override

View File

@@ -0,0 +1,46 @@
package org.thoughtcrime.securesms.payments.securitysetup
import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.databinding.PaymentsSecuritySetupFragmentBinding
import org.thoughtcrime.securesms.payments.preferences.PaymentsHomeFragmentDirections
import org.thoughtcrime.securesms.util.navigation.safeNavigate
/**
* Fragment to let user know to enable payment lock to protect their funds
*/
class PaymentsSecuritySetupFragment : Fragment(R.layout.payments_security_setup_fragment) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val binding = PaymentsSecuritySetupFragmentBinding.bind(view)
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
showSkipDialog()
}
}
)
binding.paymentsSecuritySetupEnableLock.setOnClickListener {
findNavController().safeNavigate(PaymentsHomeFragmentDirections.actionPaymentsHomeToPrivacySettings(true))
}
binding.paymentsSecuritySetupFragmentNotNow.setOnClickListener { showSkipDialog() }
binding.toolbar.setNavigationOnClickListener { showSkipDialog() }
}
private fun showSkipDialog() {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.PaymentsSecuritySetupFragment__skip_this_step))
.setMessage(getString(R.string.PaymentsSecuritySetupFragment__skipping_this_step))
.setPositiveButton(R.string.PaymentsSecuritySetupFragment__skip) { _, _ -> findNavController().popBackStack() }
.setNegativeButton(R.string.PaymentsSecuritySetupFragment__cancel) { _, _ -> }
.setCancelable(false)
.show()
}
}