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

fork-5.53.8
Varsha 2022-09-29 17:13:10 -07:00 zatwierdzone przez Greyson Parrelli
rodzic f63ce79f16
commit afe36b982f
13 zmienionych plików z 278 dodań i 6 usunięć

Wyświetl plik

@ -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")
}
}
}

Wyświetl plik

@ -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()
}

Wyświetl plik

@ -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)

Wyświetl plik

@ -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

Wyświetl plik

@ -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)

Wyświetl plik

@ -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 {

Wyświetl plik

@ -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());

Wyświetl plik

@ -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

Wyświetl plik

@ -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()
}
}

Wyświetl plik

@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="88dp"
android:height="88dp"
android:viewportWidth="88"
android:viewportHeight="88">
<path
android:pathData="M44,44m-44,0a44,44 0,1 1,88 0a44,44 0,1 1,-88 0"
android:fillColor="#FBFCFE"/>
<path
android:pathData="M44,44m-44,0a44,44 0,1 1,88 0a44,44 0,1 1,-88 0"
android:fillColor="#50679F"
android:fillAlpha="0.05"/>
<path
android:pathData="M55.73,39.699V33.125C55.73,30.014 54.495,27.03 52.295,24.83C50.095,22.63 47.111,21.395 44,21.395C40.889,21.395 37.905,22.63 35.705,24.83C33.505,27.03 32.27,30.014 32.27,33.125V39.699C31.154,39.855 30.132,40.408 29.391,41.256C28.65,42.104 28.239,43.191 28.234,44.318V61.921C28.238,63.162 28.733,64.351 29.611,65.229C30.488,66.107 31.678,66.602 32.919,66.605H55.081C56.322,66.602 57.512,66.107 58.389,65.229C59.267,64.351 59.762,63.162 59.766,61.921V44.318C59.761,43.191 59.35,42.104 58.609,41.256C57.868,40.408 56.846,39.855 55.73,39.699V39.699ZM44,23.932C46.437,23.935 48.774,24.904 50.498,26.628C52.221,28.351 53.19,30.688 53.193,33.125V39.633H34.807V33.125C34.81,30.688 35.779,28.351 37.502,26.628C39.226,24.904 41.563,23.935 44,23.932V23.932ZM57.228,61.922C57.228,62.492 57.002,63.037 56.599,63.44C56.196,63.843 55.65,64.069 55.081,64.07H32.919C32.35,64.069 31.804,63.843 31.401,63.44C30.998,63.037 30.772,62.492 30.772,61.922V44.318C30.772,43.748 30.999,43.202 31.402,42.8C31.804,42.397 32.35,42.171 32.919,42.17H55.081C55.65,42.171 56.196,42.397 56.598,42.8C57.001,43.202 57.228,43.748 57.228,44.318V61.922Z"
android:fillColor="#2C58C3"/>
</vector>

Wyświetl plik

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="@dimen/signal_m3_toolbar_height"
android:theme="?actionBarStyle"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="@drawable/ic_arrow_left_24"
app:title="@string/PaymentsSecuritySetupFragment__security_setup"
app:titleTextAppearance="@style/TextAppearance.Signal.Body1.Bold" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/payments_security_setup_fragment_image"
android:layout_width="120dp"
android:layout_height="72dp"
android:layout_marginTop="162dp"
android:layout_marginBottom="20dp"
android:importantForAccessibility="no"
app:layout_constraintBottom_toTopOf="@id/payments_security_setup_fragment_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0"
app:layout_constraintVertical_chainStyle="packed"
app:srcCompat="@drawable/ic_payment_security_setup_lock" />
<TextView
android:id="@+id/payments_security_setup_fragment_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginEnd="40dp"
android:layout_marginBottom="12dp"
android:gravity="center"
android:text="@string/PaymentsSecuritySetupFragment__protect_your_funds"
android:textAppearance="@style/TextAppearance.Signal.Title2"
app:layout_constraintBottom_toTopOf="@id/payments_security_setup_fragment_message"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/payments_security_setup_fragment_image" />
<TextView
android:id="@+id/payments_security_setup_fragment_message"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginEnd="40dp"
android:layout_marginBottom="12dp"
android:gravity="center"
android:lineSpacingExtra="6sp"
android:text="@string/PaymentsSecuritySetupFragment__help_prevent"
android:textAppearance="@style/Signal.Text.BodyMedium"
android:textColor="@color/signal_text_secondary"
app:layout_constraintBottom_toTopOf="@id/payments_security_setup_enable_lock"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/payments_security_setup_fragment_title" />
<com.google.android.material.button.MaterialButton
android:id="@+id/payments_security_setup_enable_lock"
style="@style/Signal.Widget.Button.Large.Primary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="48dp"
android:layout_marginEnd="48dp"
android:layout_marginBottom="20dp"
android:text="@string/PaymentsSecuritySetupFragment__enable_payment_lock"
app:layout_constraintBottom_toTopOf="@id/payments_security_setup_fragment_not_now"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/payments_security_setup_fragment_not_now"
style="@style/Signal.Widget.Button.Medium.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="48dp"
android:layout_marginEnd="48dp"
android:layout_marginBottom="16dp"
android:text="@string/PaymentsSecuritySetupFragment__not_now"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</LinearLayout>

Wyświetl plik

@ -112,6 +112,14 @@
</action>
<action
android:id="@+id/action_paymentsHome_to_securitySetup"
app:destination="@id/paymentsSecuritySetup"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_close_enter"
app:popExitAnim="@anim/fragment_close_exit" />
</fragment>
<fragment
@ -192,6 +200,28 @@
</fragment>
<fragment
android:id="@+id/paymentsSecuritySetup"
android:name="org.thoughtcrime.securesms.payments.securitysetup.PaymentsSecuritySetupFragment"
tools:layout="@layout/payments_security_setup_fragment">
<action
android:id="@+id/action_paymentsHome_to_privacySettings"
app:destination="@id/privacy_settings"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_close_enter"
app:popExitAnim="@anim/fragment_close_exit"
app:popUpTo="@id/paymentsHome">
<argument
android:name="show_payment_lock"
app:argType="boolean" />
</action>
</fragment>
<action
android:id="@+id/action_directly_to_createPayment"
app:destination="@id/payments_create"

Wyświetl plik

@ -2909,6 +2909,26 @@
<!-- Alert dialog button to not enable payment lock for now -->
<string name="PaymentsHomeFragment__not_now">Not Now</string>
<!-- PaymentsSecuritySetupFragment -->
<!-- Toolbar title -->
<string name="PaymentsSecuritySetupFragment__security_setup">Security setup</string>
<!-- Title to enable payment lock -->
<string name="PaymentsSecuritySetupFragment__protect_your_funds">Protect your funds</string>
<!-- Description as to why payment lock is required -->
<string name="PaymentsSecuritySetupFragment__help_prevent">Help prevent a person with your phone from accessing your funds by adding another layer of security. You can disable this option in Settings.</string>
<!-- Option to enable payment lock -->
<string name="PaymentsSecuritySetupFragment__enable_payment_lock">Enable payment lock</string>
<!-- Option to cancel -->
<string name="PaymentsSecuritySetupFragment__not_now">Not now</string>
<!-- Dialog title to confirm skipping the step -->
<string name="PaymentsSecuritySetupFragment__skip_this_step">Skip this step?</string>
<!-- Dialog description to let users know why payment lock is required -->
<string name="PaymentsSecuritySetupFragment__skipping_this_step">Skipping this step could allow anyone who has physical access to your phone to transfer funds or view your recovery phrase.</string>
<!-- Dialog option to cancel -->
<string name="PaymentsSecuritySetupFragment__cancel">Cancel</string>
<!-- Dialog option to skip -->
<string name="PaymentsSecuritySetupFragment__skip">Skip</string>
<!-- PaymentsAddMoneyFragment -->
<string name="PaymentsAddMoneyFragment__add_funds">Add funds</string>
<string name="PaymentsAddMoneyFragment__your_wallet_address">Your Wallet Address</string>