mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 00:59:49 +01:00
Prompt to setup payment bioauth, require to disable payment lock.
This commit is contained in:
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user