kopia lustrzana https://github.com/ryukoposting/Signal-Android
Prompt update on MobileCoin enclave failure.
rodzic
2709f0ee0d
commit
b38ac44d0f
|
@ -0,0 +1,25 @@
|
||||||
|
package org.thoughtcrime.securesms.components.reminder
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.View
|
||||||
|
import org.thoughtcrime.securesms.R
|
||||||
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
|
import org.thoughtcrime.securesms.util.PlayStoreUtil
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Banner to update app to the latest version because of enclave failure
|
||||||
|
*/
|
||||||
|
class EnclaveFailureReminder(context: Context) : Reminder(null,
|
||||||
|
context.getString(R.string.EnclaveFailureReminder_update_signal)) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
addAction(Action(context.getString(R.string.ExpiredBuildReminder_update_now), R.id.reminder_action_update_now))
|
||||||
|
okListener = View.OnClickListener { PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(context) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isDismissable(): Boolean = false
|
||||||
|
|
||||||
|
override fun getImportance(): Importance {
|
||||||
|
return Importance.TERMINAL
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,6 +68,7 @@ internal class PaymentsValues internal constructor(store: KeyValueStore) : Signa
|
||||||
get() = getBoolean(USER_CONFIRMED_MNEMONIC_LARGE_BALANCE, false)
|
get() = getBoolean(USER_CONFIRMED_MNEMONIC_LARGE_BALANCE, false)
|
||||||
set(value) = putBoolean(USER_CONFIRMED_MNEMONIC_LARGE_BALANCE, value)
|
set(value) = putBoolean(USER_CONFIRMED_MNEMONIC_LARGE_BALANCE, value)
|
||||||
private val liveCurrentCurrency: MutableLiveData<Currency> by lazy { MutableLiveData(currentCurrency()) }
|
private val liveCurrentCurrency: MutableLiveData<Currency> by lazy { MutableLiveData(currentCurrency()) }
|
||||||
|
private val enclaveFailure: MutableLiveData<Boolean> by lazy { MutableLiveData(false) }
|
||||||
private val liveMobileCoinLedger: MutableLiveData<MobileCoinLedgerWrapper> by lazy { MutableLiveData(mobileCoinLatestFullLedger()) }
|
private val liveMobileCoinLedger: MutableLiveData<MobileCoinLedgerWrapper> by lazy { MutableLiveData(mobileCoinLatestFullLedger()) }
|
||||||
private val liveMobileCoinBalance: LiveData<Balance> by lazy { Transformations.map(liveMobileCoinLedger) { obj: MobileCoinLedgerWrapper -> obj.balance } }
|
private val liveMobileCoinBalance: LiveData<Balance> by lazy { Transformations.map(liveMobileCoinLedger) { obj: MobileCoinLedgerWrapper -> obj.balance } }
|
||||||
|
|
||||||
|
@ -214,6 +215,15 @@ internal class PaymentsValues internal constructor(store: KeyValueStore) : Signa
|
||||||
return liveCurrentCurrency
|
return liveCurrentCurrency
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setEnclaveFailure(failure: Boolean) {
|
||||||
|
enclaveFailure.postValue(failure)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enclaveFailure(): LiveData<Boolean> {
|
||||||
|
return enclaveFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun showAboutMobileCoinInfoCard(): Boolean {
|
fun showAboutMobileCoinInfoCard(): Boolean {
|
||||||
return store.getBoolean(SHOW_ABOUT_MOBILE_COIN_INFO_CARD, true)
|
return store.getBoolean(SHOW_ABOUT_MOBILE_COIN_INFO_CARD, true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,7 +198,7 @@ public final class Wallet {
|
||||||
.setHighestBlock(MobileCoinLedger.Block.newBuilder()
|
.setHighestBlock(MobileCoinLedger.Block.newBuilder()
|
||||||
.setBlockNumber(highestBlockIndex.longValue())
|
.setBlockNumber(highestBlockIndex.longValue())
|
||||||
.setTimestamp(highestBlockTimeStamp));
|
.setTimestamp(highestBlockTimeStamp));
|
||||||
|
SignalStore.paymentsValues().setEnclaveFailure(false);
|
||||||
return new MobileCoinLedgerWrapper(builder.build());
|
return new MobileCoinLedgerWrapper(builder.build());
|
||||||
} catch (InvalidFogResponse e) {
|
} catch (InvalidFogResponse e) {
|
||||||
Log.w(TAG, "Problem getting ledger", e);
|
Log.w(TAG, "Problem getting ledger", e);
|
||||||
|
@ -211,6 +211,7 @@ public final class Wallet {
|
||||||
}
|
}
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
} catch (AttestationException e) {
|
} catch (AttestationException e) {
|
||||||
|
SignalStore.paymentsValues().setEnclaveFailure(true);
|
||||||
Log.w(TAG, "Attestation problem getting ledger", e);
|
Log.w(TAG, "Attestation problem getting ledger", e);
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
} catch (Uint64RangeException e) {
|
} catch (Uint64RangeException e) {
|
||||||
|
@ -233,12 +234,18 @@ public final class Wallet {
|
||||||
BigInteger picoMob = amount.requireMobileCoin().toPicoMobBigInteger();
|
BigInteger picoMob = amount.requireMobileCoin().toPicoMobBigInteger();
|
||||||
AccountSnapshot accountSnapshot = getCachedAccountSnapshot();
|
AccountSnapshot accountSnapshot = getCachedAccountSnapshot();
|
||||||
Amount minimumFee = getCachedMinimumTxFee();
|
Amount minimumFee = getCachedMinimumTxFee();
|
||||||
|
Money.MobileCoin money;
|
||||||
if (accountSnapshot != null && minimumFee != null) {
|
if (accountSnapshot != null && minimumFee != null) {
|
||||||
return Money.picoMobileCoin(accountSnapshot.estimateTotalFee(Amount.ofMOB(picoMob), minimumFee).getValue());
|
money = Money.picoMobileCoin(accountSnapshot.estimateTotalFee(Amount.ofMOB(picoMob), minimumFee).getValue());
|
||||||
} else {
|
} else {
|
||||||
return Money.picoMobileCoin(mobileCoinClient.estimateTotalFee(Amount.ofMOB(picoMob)).getValue());
|
money = Money.picoMobileCoin(mobileCoinClient.estimateTotalFee(Amount.ofMOB(picoMob)).getValue());
|
||||||
}
|
}
|
||||||
} catch (InvalidFogResponse | AttestationException | InsufficientFundsException e) {
|
SignalStore.paymentsValues().setEnclaveFailure(false);
|
||||||
|
return money;
|
||||||
|
} catch (AttestationException e) {
|
||||||
|
SignalStore.paymentsValues().setEnclaveFailure(true);
|
||||||
|
return Money.MobileCoin.ZERO;
|
||||||
|
} catch (InvalidFogResponse | InsufficientFundsException e) {
|
||||||
Log.w(TAG, "Failed to get fee", e);
|
Log.w(TAG, "Failed to get fee", e);
|
||||||
return Money.MobileCoin.ZERO;
|
return Money.MobileCoin.ZERO;
|
||||||
} catch (NetworkException | FogSyncException e) {
|
} catch (NetworkException | FogSyncException e) {
|
||||||
|
@ -288,22 +295,31 @@ public final class Wallet {
|
||||||
try {
|
try {
|
||||||
Receipt receipt = Receipt.fromBytes(receiptBytes);
|
Receipt receipt = Receipt.fromBytes(receiptBytes);
|
||||||
Receipt.Status status = mobileCoinClient.getReceiptStatus(receipt);
|
Receipt.Status status = mobileCoinClient.getReceiptStatus(receipt);
|
||||||
|
ReceivedTransactionStatus txStatus = null;
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case UNKNOWN:
|
case UNKNOWN:
|
||||||
Log.w(TAG, "Unknown received Transaction Status");
|
Log.w(TAG, "Unknown received Transaction Status");
|
||||||
return ReceivedTransactionStatus.inProgress();
|
txStatus = ReceivedTransactionStatus.inProgress();
|
||||||
|
break;
|
||||||
case FAILED:
|
case FAILED:
|
||||||
return ReceivedTransactionStatus.failed();
|
txStatus = ReceivedTransactionStatus.failed();
|
||||||
|
break;
|
||||||
case RECEIVED:
|
case RECEIVED:
|
||||||
final Amount amount = receipt.getAmountData(account);
|
final Amount amount = receipt.getAmountData(account);
|
||||||
return ReceivedTransactionStatus.complete(Money.picoMobileCoin(amount.getValue()), status.getBlockIndex().longValue());
|
txStatus = ReceivedTransactionStatus.complete(Money.picoMobileCoin(amount.getValue()), status.getBlockIndex().longValue());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Unknown Transaction Status: " + status);
|
|
||||||
}
|
}
|
||||||
|
SignalStore.paymentsValues().setEnclaveFailure(false);
|
||||||
|
if (txStatus == null) throw new IllegalStateException("Unknown Transaction Status: " + status);
|
||||||
|
return txStatus;
|
||||||
} catch (SerializationException | InvalidFogResponse | InvalidReceiptException e) {
|
} catch (SerializationException | InvalidFogResponse | InvalidReceiptException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
return ReceivedTransactionStatus.failed();
|
return ReceivedTransactionStatus.failed();
|
||||||
} catch (NetworkException | AttestationException e) {
|
} catch (NetworkException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
} catch (AttestationException e) {
|
||||||
|
SignalStore.paymentsValues().setEnclaveFailure(true);
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
} catch (AmountDecoderException e) {
|
} catch (AmountDecoderException e) {
|
||||||
Log.w(TAG, "Failed to decode amount", e);
|
Log.w(TAG, "Failed to decode amount", e);
|
||||||
|
@ -322,11 +338,16 @@ public final class Wallet {
|
||||||
if (defragmentFirst) {
|
if (defragmentFirst) {
|
||||||
try {
|
try {
|
||||||
defragmentFees = defragment(amount, results);
|
defragmentFees = defragment(amount, results);
|
||||||
|
SignalStore.paymentsValues().setEnclaveFailure(false);
|
||||||
} catch (InsufficientFundsException e) {
|
} catch (InsufficientFundsException e) {
|
||||||
Log.w(TAG, "Insufficient funds", e);
|
Log.w(TAG, "Insufficient funds", e);
|
||||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.INSUFFICIENT_FUNDS, true));
|
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.INSUFFICIENT_FUNDS, true));
|
||||||
return;
|
return;
|
||||||
} catch (TimeoutException | InvalidTransactionException | InvalidFogResponse | AttestationException | TransactionBuilderException | NetworkException | FogReportException | FogSyncException e) {
|
} catch (AttestationException e) {
|
||||||
|
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, true));
|
||||||
|
SignalStore.paymentsValues().setEnclaveFailure(true);
|
||||||
|
return;
|
||||||
|
} catch (TimeoutException | InvalidTransactionException | InvalidFogResponse | TransactionBuilderException | NetworkException | FogReportException | FogSyncException e) {
|
||||||
Log.w(TAG, "Defragment failed", e);
|
Log.w(TAG, "Defragment failed", e);
|
||||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, true));
|
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, true));
|
||||||
return;
|
return;
|
||||||
|
@ -358,6 +379,7 @@ public final class Wallet {
|
||||||
Amount.ofMOB(feeMobileCoin.toPicoMobBigInteger()),
|
Amount.ofMOB(feeMobileCoin.toPicoMobBigInteger()),
|
||||||
TxOutMemoBuilder.createSenderAndDestinationRTHMemoBuilder(account));
|
TxOutMemoBuilder.createSenderAndDestinationRTHMemoBuilder(account));
|
||||||
}
|
}
|
||||||
|
SignalStore.paymentsValues().setEnclaveFailure(false);
|
||||||
} catch (InsufficientFundsException e) {
|
} catch (InsufficientFundsException e) {
|
||||||
Log.w(TAG, "Insufficient funds", e);
|
Log.w(TAG, "Insufficient funds", e);
|
||||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.INSUFFICIENT_FUNDS, false));
|
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.INSUFFICIENT_FUNDS, false));
|
||||||
|
@ -378,6 +400,7 @@ public final class Wallet {
|
||||||
} catch (AttestationException e) {
|
} catch (AttestationException e) {
|
||||||
Log.w(TAG, "Attestation problem", e);
|
Log.w(TAG, "Attestation problem", e);
|
||||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, false));
|
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, false));
|
||||||
|
SignalStore.paymentsValues().setEnclaveFailure(true);
|
||||||
} catch (NetworkException e) {
|
} catch (NetworkException e) {
|
||||||
Log.w(TAG, "Network problem", e);
|
Log.w(TAG, "Network problem", e);
|
||||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, false));
|
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, false));
|
||||||
|
@ -399,6 +422,7 @@ public final class Wallet {
|
||||||
mobileCoinClient.submitTransaction(pendingTransaction.getTransaction());
|
mobileCoinClient.submitTransaction(pendingTransaction.getTransaction());
|
||||||
Log.i(TAG, "Transaction submitted");
|
Log.i(TAG, "Transaction submitted");
|
||||||
results.add(TransactionSubmissionResult.successfullySubmitted(new PaymentTransactionId.MobileCoin(pendingTransaction.getTransaction().toByteArray(), pendingTransaction.getReceipt().toByteArray(), feeMobileCoin)));
|
results.add(TransactionSubmissionResult.successfullySubmitted(new PaymentTransactionId.MobileCoin(pendingTransaction.getTransaction().toByteArray(), pendingTransaction.getReceipt().toByteArray(), feeMobileCoin)));
|
||||||
|
SignalStore.paymentsValues().setEnclaveFailure(false);
|
||||||
} catch (NetworkException e) {
|
} catch (NetworkException e) {
|
||||||
Log.w(TAG, "Network problem", e);
|
Log.w(TAG, "Network problem", e);
|
||||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.NETWORK_FAILURE, false));
|
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.NETWORK_FAILURE, false));
|
||||||
|
@ -408,6 +432,7 @@ public final class Wallet {
|
||||||
} catch (AttestationException e) {
|
} catch (AttestationException e) {
|
||||||
Log.w(TAG, "Attestation problem", e);
|
Log.w(TAG, "Attestation problem", e);
|
||||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, false));
|
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, false));
|
||||||
|
SignalStore.paymentsValues().setEnclaveFailure(true);
|
||||||
} catch (SerializationException e) {
|
} catch (SerializationException e) {
|
||||||
Log.w(TAG, "Serialization problem", e);
|
Log.w(TAG, "Serialization problem", e);
|
||||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, false));
|
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, false));
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.thoughtcrime.securesms.payments.FiatMoneyUtil;
|
||||||
import org.thoughtcrime.securesms.payments.MoneyView;
|
import org.thoughtcrime.securesms.payments.MoneyView;
|
||||||
import org.thoughtcrime.securesms.payments.preferences.RecipientHasNotEnabledPaymentsDialog;
|
import org.thoughtcrime.securesms.payments.preferences.RecipientHasNotEnabledPaymentsDialog;
|
||||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||||
|
import org.thoughtcrime.securesms.util.PlayStoreUtil;
|
||||||
import org.thoughtcrime.securesms.util.SpanUtil;
|
import org.thoughtcrime.securesms.util.SpanUtil;
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||||
import org.thoughtcrime.securesms.util.navigation.SafeNavigation;
|
import org.thoughtcrime.securesms.util.navigation.SafeNavigation;
|
||||||
|
@ -144,6 +145,17 @@ public class CreatePaymentFragment extends LoggingFragment {
|
||||||
viewModel.getNote().observe(getViewLifecycleOwner(), this::updateNote);
|
viewModel.getNote().observe(getViewLifecycleOwner(), this::updateNote);
|
||||||
viewModel.getSpendableBalance().observe(getViewLifecycleOwner(), this::updateBalance);
|
viewModel.getSpendableBalance().observe(getViewLifecycleOwner(), this::updateBalance);
|
||||||
viewModel.getCanSendPayment().observe(getViewLifecycleOwner(), this::updatePayAmountButtons);
|
viewModel.getCanSendPayment().observe(getViewLifecycleOwner(), this::updatePayAmountButtons);
|
||||||
|
viewModel.getEnclaveFailure().observe(getViewLifecycleOwner(), failure -> {
|
||||||
|
if (failure) {
|
||||||
|
new MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(getString(R.string.PaymentsHomeFragment__update_required))
|
||||||
|
.setMessage(getString(R.string.PaymentsHomeFragment__an_update_is_required))
|
||||||
|
.setPositiveButton(R.string.PaymentsHomeFragment__update_now, (dialog, which) -> { PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(requireContext()); })
|
||||||
|
.setNegativeButton(R.string.PaymentsHomeFragment__cancel, (dialog, which) -> {})
|
||||||
|
.setCancelable(false)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void goBack(View v) {
|
private void goBack(View v) {
|
||||||
|
|
|
@ -45,6 +45,7 @@ public class CreatePaymentViewModel extends ViewModel {
|
||||||
private final LiveData<Boolean> isValidAmount;
|
private final LiveData<Boolean> isValidAmount;
|
||||||
private final Store<InputState> inputState;
|
private final Store<InputState> inputState;
|
||||||
private final LiveData<Boolean> isPaymentsSupportedByPayee;
|
private final LiveData<Boolean> isPaymentsSupportedByPayee;
|
||||||
|
private final LiveData<Boolean> enclaveFailure;
|
||||||
|
|
||||||
private final PayeeParcelable payee;
|
private final PayeeParcelable payee;
|
||||||
private final MutableLiveData<CharSequence> note;
|
private final MutableLiveData<CharSequence> note;
|
||||||
|
@ -55,6 +56,7 @@ public class CreatePaymentViewModel extends ViewModel {
|
||||||
this.note = new MutableLiveData<>(note);
|
this.note = new MutableLiveData<>(note);
|
||||||
this.inputState = new Store<>(new InputState());
|
this.inputState = new Store<>(new InputState());
|
||||||
this.isValidAmount = LiveDataUtil.combineLatest(spendableBalance, inputState.getStateLiveData(), (b, s) -> validateAmount(b.requireMobileCoin(), s.getMoney().requireMobileCoin()));
|
this.isValidAmount = LiveDataUtil.combineLatest(spendableBalance, inputState.getStateLiveData(), (b, s) -> validateAmount(b.requireMobileCoin(), s.getMoney().requireMobileCoin()));
|
||||||
|
this.enclaveFailure = LiveDataUtil.mapDistinct(SignalStore.paymentsValues().enclaveFailure(), isFailure -> isFailure);
|
||||||
|
|
||||||
if (payee.getPayee().hasRecipientId()) {
|
if (payee.getPayee().hasRecipientId()) {
|
||||||
isPaymentsSupportedByPayee = LiveDataUtil.mapAsync(new DefaultValueLiveData<>(payee.getPayee().requireRecipientId()), r -> {
|
isPaymentsSupportedByPayee = LiveDataUtil.mapAsync(new DefaultValueLiveData<>(payee.getPayee().requireRecipientId()), r -> {
|
||||||
|
@ -92,6 +94,10 @@ public class CreatePaymentViewModel extends ViewModel {
|
||||||
inputState.update(liveExchangeRate, (rate, state) -> updateAmount(ApplicationDependencies.getApplication(), state.updateExchangeRate(rate), AmountKeyboardGlyph.NONE));
|
inputState.update(liveExchangeRate, (rate, state) -> updateAmount(ApplicationDependencies.getApplication(), state.updateExchangeRate(rate), AmountKeyboardGlyph.NONE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull LiveData<Boolean> getEnclaveFailure() {
|
||||||
|
return enclaveFailure;
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull LiveData<InputState> getInputState() {
|
@NonNull LiveData<InputState> getInputState() {
|
||||||
return inputState.getStateLiveData();
|
return inputState.getStateLiveData();
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.LoggingFragment;
|
import org.thoughtcrime.securesms.LoggingFragment;
|
||||||
import org.thoughtcrime.securesms.PaymentPreferencesDirections;
|
import org.thoughtcrime.securesms.PaymentPreferencesDirections;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.components.reminder.EnclaveFailureReminder;
|
||||||
|
import org.thoughtcrime.securesms.components.reminder.ReminderView;
|
||||||
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
|
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
|
||||||
import org.thoughtcrime.securesms.help.HelpFragment;
|
import org.thoughtcrime.securesms.help.HelpFragment;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
|
@ -37,8 +39,11 @@ import org.thoughtcrime.securesms.payments.backup.confirm.PaymentsRecoveryPhrase
|
||||||
import org.thoughtcrime.securesms.payments.preferences.model.InfoCard;
|
import org.thoughtcrime.securesms.payments.preferences.model.InfoCard;
|
||||||
import org.thoughtcrime.securesms.payments.preferences.model.PaymentItem;
|
import org.thoughtcrime.securesms.payments.preferences.model.PaymentItem;
|
||||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||||
|
import org.thoughtcrime.securesms.util.PlayStoreUtil;
|
||||||
import org.thoughtcrime.securesms.util.SpanUtil;
|
import org.thoughtcrime.securesms.util.SpanUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||||
import org.thoughtcrime.securesms.util.navigation.SafeNavigation;
|
import org.thoughtcrime.securesms.util.navigation.SafeNavigation;
|
||||||
|
import org.thoughtcrime.securesms.util.views.Stub;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@ -95,6 +100,7 @@ public class PaymentsHomeFragment extends LoggingFragment {
|
||||||
View sendMoney = view.findViewById(R.id.button_end_frame);
|
View sendMoney = view.findViewById(R.id.button_end_frame);
|
||||||
View refresh = view.findViewById(R.id.payments_home_fragment_header_refresh);
|
View refresh = view.findViewById(R.id.payments_home_fragment_header_refresh);
|
||||||
LottieAnimationView refreshAnimation = view.findViewById(R.id.payments_home_fragment_header_refresh_animation);
|
LottieAnimationView refreshAnimation = view.findViewById(R.id.payments_home_fragment_header_refresh_animation);
|
||||||
|
Stub<ReminderView> reminderView = ViewUtil.findStubById(view, R.id.reminder);
|
||||||
|
|
||||||
toolbar.setNavigationOnClickListener(v -> {
|
toolbar.setNavigationOnClickListener(v -> {
|
||||||
viewModel.markAllPaymentsSeen();
|
viewModel.markAllPaymentsSeen();
|
||||||
|
@ -104,14 +110,18 @@ public class PaymentsHomeFragment extends LoggingFragment {
|
||||||
toolbar.setOnMenuItemClickListener(this::onMenuItemSelected);
|
toolbar.setOnMenuItemClickListener(this::onMenuItemSelected);
|
||||||
|
|
||||||
addMoney.setOnClickListener(v -> {
|
addMoney.setOnClickListener(v -> {
|
||||||
if (SignalStore.paymentsValues().getPaymentsAvailability().isSendAllowed()) {
|
if (viewModel.isEnclaveFailurePresent()) {
|
||||||
|
showUpdateIsRequiredDialog();
|
||||||
|
} else if (SignalStore.paymentsValues().getPaymentsAvailability().isSendAllowed()) {
|
||||||
SafeNavigation.safeNavigate(Navigation.findNavController(v), PaymentsHomeFragmentDirections.actionPaymentsHomeToPaymentsAddMoney());
|
SafeNavigation.safeNavigate(Navigation.findNavController(v), PaymentsHomeFragmentDirections.actionPaymentsHomeToPaymentsAddMoney());
|
||||||
} else {
|
} else {
|
||||||
showPaymentsDisabledDialog();
|
showPaymentsDisabledDialog();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
sendMoney.setOnClickListener(v -> {
|
sendMoney.setOnClickListener(v -> {
|
||||||
if (SignalStore.paymentsValues().getPaymentsAvailability().isSendAllowed()) {
|
if (viewModel.isEnclaveFailurePresent()) {
|
||||||
|
showUpdateIsRequiredDialog();
|
||||||
|
} else if (SignalStore.paymentsValues().getPaymentsAvailability().isSendAllowed()) {
|
||||||
SafeNavigation.safeNavigate(Navigation.findNavController(v), PaymentsHomeFragmentDirections.actionPaymentsHomeToPaymentRecipientSelectionFragment());
|
SafeNavigation.safeNavigate(Navigation.findNavController(v), PaymentsHomeFragmentDirections.actionPaymentsHomeToPaymentRecipientSelectionFragment());
|
||||||
} else {
|
} else {
|
||||||
showPaymentsDisabledDialog();
|
showPaymentsDisabledDialog();
|
||||||
|
@ -246,6 +256,20 @@ public class PaymentsHomeFragment extends LoggingFragment {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
viewModel.getEnclaveFailure().observe(getViewLifecycleOwner(), failure -> {
|
||||||
|
if (failure) {
|
||||||
|
showUpdateIsRequiredDialog();
|
||||||
|
reminderView.get().showReminder(new EnclaveFailureReminder(requireContext()));
|
||||||
|
reminderView.get().setOnActionClickListener(actionId -> {
|
||||||
|
if (actionId == R.id.reminder_action_update_now) {
|
||||||
|
PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(requireContext());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
reminderView.get().requestDismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
requireActivity().getOnBackPressedDispatcher().addCallback(onBackPressed);
|
requireActivity().getOnBackPressedDispatcher().addCallback(onBackPressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,9 +285,23 @@ public class PaymentsHomeFragment extends LoggingFragment {
|
||||||
onBackPressed.setEnabled(false);
|
onBackPressed.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showUpdateIsRequiredDialog() {
|
||||||
|
new MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(getString(R.string.PaymentsHomeFragment__update_required))
|
||||||
|
.setMessage(getString(R.string.PaymentsHomeFragment__an_update_is_required))
|
||||||
|
.setPositiveButton(R.string.PaymentsHomeFragment__update_now, (dialog, which) -> { PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(requireContext()); })
|
||||||
|
.setNegativeButton(R.string.PaymentsHomeFragment__cancel, (dialog, which) -> {})
|
||||||
|
.setCancelable(false)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean onMenuItemSelected(@NonNull MenuItem item) {
|
private boolean onMenuItemSelected(@NonNull MenuItem item) {
|
||||||
if (item.getItemId() == R.id.payments_home_fragment_menu_transfer_to_exchange) {
|
if (item.getItemId() == R.id.payments_home_fragment_menu_transfer_to_exchange) {
|
||||||
|
if (viewModel.isEnclaveFailurePresent()) {
|
||||||
|
showUpdateIsRequiredDialog();
|
||||||
|
} else {
|
||||||
SafeNavigation.safeNavigate(NavHostFragment.findNavController(this), R.id.action_paymentsHome_to_paymentsTransfer);
|
SafeNavigation.safeNavigate(NavHostFragment.findNavController(this), R.id.action_paymentsHome_to_paymentsTransfer);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (item.getItemId() == R.id.payments_home_fragment_menu_set_currency) {
|
} else if (item.getItemId() == R.id.payments_home_fragment_menu_set_currency) {
|
||||||
SafeNavigation.safeNavigate(NavHostFragment.findNavController(this), R.id.action_paymentsHome_to_setCurrency);
|
SafeNavigation.safeNavigate(NavHostFragment.findNavController(this), R.id.action_paymentsHome_to_setCurrency);
|
||||||
|
|
|
@ -51,6 +51,7 @@ public class PaymentsHomeViewModel extends ViewModel {
|
||||||
private final LiveData<FiatMoney> exchange;
|
private final LiveData<FiatMoney> exchange;
|
||||||
private final SingleLiveEvent<PaymentStateEvent> paymentStateEvents;
|
private final SingleLiveEvent<PaymentStateEvent> paymentStateEvents;
|
||||||
private final SingleLiveEvent<ErrorEnabling> errorEnablingPayments;
|
private final SingleLiveEvent<ErrorEnabling> errorEnablingPayments;
|
||||||
|
private final LiveData<Boolean> enclaveFailure;
|
||||||
|
|
||||||
private final PaymentsHomeRepository paymentsHomeRepository;
|
private final PaymentsHomeRepository paymentsHomeRepository;
|
||||||
private final CurrencyExchangeRepository currencyExchangeRepository;
|
private final CurrencyExchangeRepository currencyExchangeRepository;
|
||||||
|
@ -72,7 +73,7 @@ public class PaymentsHomeViewModel extends ViewModel {
|
||||||
this.exchangeLoadState = LiveDataUtil.mapDistinct(store.getStateLiveData(), PaymentsHomeState::getExchangeRateLoadState);
|
this.exchangeLoadState = LiveDataUtil.mapDistinct(store.getStateLiveData(), PaymentsHomeState::getExchangeRateLoadState);
|
||||||
this.paymentStateEvents = new SingleLiveEvent<>();
|
this.paymentStateEvents = new SingleLiveEvent<>();
|
||||||
this.errorEnablingPayments = new SingleLiveEvent<>();
|
this.errorEnablingPayments = new SingleLiveEvent<>();
|
||||||
|
this.enclaveFailure = LiveDataUtil.mapDistinct(SignalStore.paymentsValues().enclaveFailure(), isFailure -> isFailure);
|
||||||
this.store.update(paymentsRepository.getRecentPayments(), this::updateRecentPayments);
|
this.store.update(paymentsRepository.getRecentPayments(), this::updateRecentPayments);
|
||||||
|
|
||||||
LiveData<CurrencyExchange.ExchangeRate> liveExchangeRate = LiveDataUtil.combineLatest(SignalStore.paymentsValues().liveCurrentCurrency(),
|
LiveData<CurrencyExchange.ExchangeRate> liveExchangeRate = LiveDataUtil.combineLatest(SignalStore.paymentsValues().liveCurrentCurrency(),
|
||||||
|
@ -109,6 +110,14 @@ public class PaymentsHomeViewModel extends ViewModel {
|
||||||
return errorEnablingPayments;
|
return errorEnablingPayments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull LiveData<Boolean> getEnclaveFailure() {
|
||||||
|
return enclaveFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull boolean isEnclaveFailurePresent() {
|
||||||
|
return Boolean.TRUE.equals(getEnclaveFailure().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull LiveData<MappingModelList> getList() {
|
@NonNull LiveData<MappingModelList> getList() {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -13,22 +14,44 @@
|
||||||
android:layout_height="@dimen/signal_m3_toolbar_height"
|
android:layout_height="@dimen/signal_m3_toolbar_height"
|
||||||
android:minHeight="@dimen/signal_m3_toolbar_height"
|
android:minHeight="@dimen/signal_m3_toolbar_height"
|
||||||
android:theme="?actionBarStyle"
|
android:theme="?actionBarStyle"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:navigationIcon="@drawable/ic_arrow_left_24"
|
app:navigationIcon="@drawable/ic_arrow_left_24"
|
||||||
app:title="@string/preferences__payments_beta"
|
app:title="@string/preferences__payments_beta"
|
||||||
app:titleTextAppearance="@style/Signal.Text.TitleLarge" />
|
app:titleTextAppearance="@style/Signal.Text.TitleLarge" />
|
||||||
|
|
||||||
|
<ViewStub
|
||||||
|
android:id="@+id/reminder"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inflatedId="@+id/reminder"
|
||||||
|
android:layout="@layout/conversation_list_reminder_view"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/payments_home_fragment_toolbar" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Barrier
|
||||||
|
android:id="@+id/banner_barrier"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:barrierDirection="bottom"
|
||||||
|
app:constraint_referenced_ids="reminder" />
|
||||||
|
|
||||||
<include
|
<include
|
||||||
layout="@layout/payments_home_fragment_header"
|
layout="@layout/payments_home_fragment_header"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/banner_barrier" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/payments_home_fragment_recycler"
|
android:id="@+id/payments_home_fragment_recycler"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="0dp"
|
||||||
android:scrollIndicators="top|bottom"
|
android:scrollIndicators="top|bottom"
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/payments_home_fragment_header" />
|
||||||
|
|
||||||
</LinearLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -2155,6 +2155,12 @@
|
||||||
<string name="UnauthorizedReminder_device_no_longer_registered">Device no longer registered</string>
|
<string name="UnauthorizedReminder_device_no_longer_registered">Device no longer registered</string>
|
||||||
<string name="UnauthorizedReminder_this_is_likely_because_you_registered_your_phone_number_with_Signal_on_a_different_device">This is likely because you registered your phone number with Signal on a different device. Tap to re-register.</string>
|
<string name="UnauthorizedReminder_this_is_likely_because_you_registered_your_phone_number_with_Signal_on_a_different_device">This is likely because you registered your phone number with Signal on a different device. Tap to re-register.</string>
|
||||||
|
|
||||||
|
<!-- EnclaveFailureReminder -->
|
||||||
|
<!-- Banner message to update app to use payments -->
|
||||||
|
<string name="EnclaveFailureReminder_update_signal">Update Signal to continue using payments. Your balance may not be up-to-date.</string>
|
||||||
|
<!-- Banner button to update now -->
|
||||||
|
<string name="EnclaveFailureReminder_update_now">Update now</string>
|
||||||
|
|
||||||
<!-- WebRtcCallActivity -->
|
<!-- WebRtcCallActivity -->
|
||||||
<string name="WebRtcCallActivity_to_answer_the_call_give_signal_access_to_your_microphone">To answer the call, give Signal access to your microphone.</string>
|
<string name="WebRtcCallActivity_to_answer_the_call_give_signal_access_to_your_microphone">To answer the call, give Signal access to your microphone.</string>
|
||||||
<string name="WebRtcCallActivity_to_answer_the_call_from_s_give_signal_access_to_your_microphone">To answer the call from %s, give Signal access to your microphone.</string>
|
<string name="WebRtcCallActivity_to_answer_the_call_from_s_give_signal_access_to_your_microphone">To answer the call from %s, give Signal access to your microphone.</string>
|
||||||
|
@ -3018,6 +3024,14 @@
|
||||||
<string name="PaymentsHomeFragment__enable">Turn On</string>
|
<string name="PaymentsHomeFragment__enable">Turn On</string>
|
||||||
<!-- Alert dialog button to not enable payment lock for now -->
|
<!-- Alert dialog button to not enable payment lock for now -->
|
||||||
<string name="PaymentsHomeFragment__not_now">Not Now</string>
|
<string name="PaymentsHomeFragment__not_now">Not Now</string>
|
||||||
|
<!-- Alert dialog title which shows up to update app to send payments -->
|
||||||
|
<string name="PaymentsHomeFragment__update_required">Update required</string>
|
||||||
|
<!-- Alert dialog description that app update is required to send payments-->
|
||||||
|
<string name="PaymentsHomeFragment__an_update_is_required">An update is required to continue sending and receiving payments, and to view your up-to-date payment balance.</string>
|
||||||
|
<!-- Alert dialog button to cancel -->
|
||||||
|
<string name="PaymentsHomeFragment__cancel">Cancel</string>
|
||||||
|
<!-- Alert dialog button to update now -->
|
||||||
|
<string name="PaymentsHomeFragment__update_now">Update now</string>
|
||||||
|
|
||||||
<!-- PaymentsSecuritySetupFragment -->
|
<!-- PaymentsSecuritySetupFragment -->
|
||||||
<!-- Toolbar title -->
|
<!-- Toolbar title -->
|
||||||
|
|
Ładowanie…
Reference in New Issue