Prompt update on MobileCoin enclave failure.
This commit is contained in:
parent
2709f0ee0d
commit
b38ac44d0f
9 changed files with 181 additions and 19 deletions
|
@ -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)
|
||||
set(value) = putBoolean(USER_CONFIRMED_MNEMONIC_LARGE_BALANCE, value)
|
||||
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 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
|
||||
}
|
||||
|
||||
fun setEnclaveFailure(failure: Boolean) {
|
||||
enclaveFailure.postValue(failure)
|
||||
}
|
||||
|
||||
fun enclaveFailure(): LiveData<Boolean> {
|
||||
return enclaveFailure
|
||||
}
|
||||
|
||||
|
||||
fun showAboutMobileCoinInfoCard(): Boolean {
|
||||
return store.getBoolean(SHOW_ABOUT_MOBILE_COIN_INFO_CARD, true)
|
||||
}
|
||||
|
|
|
@ -198,7 +198,7 @@ public final class Wallet {
|
|||
.setHighestBlock(MobileCoinLedger.Block.newBuilder()
|
||||
.setBlockNumber(highestBlockIndex.longValue())
|
||||
.setTimestamp(highestBlockTimeStamp));
|
||||
|
||||
SignalStore.paymentsValues().setEnclaveFailure(false);
|
||||
return new MobileCoinLedgerWrapper(builder.build());
|
||||
} catch (InvalidFogResponse e) {
|
||||
Log.w(TAG, "Problem getting ledger", e);
|
||||
|
@ -211,6 +211,7 @@ public final class Wallet {
|
|||
}
|
||||
throw new IOException(e);
|
||||
} catch (AttestationException e) {
|
||||
SignalStore.paymentsValues().setEnclaveFailure(true);
|
||||
Log.w(TAG, "Attestation problem getting ledger", e);
|
||||
throw new IOException(e);
|
||||
} catch (Uint64RangeException e) {
|
||||
|
@ -233,12 +234,18 @@ public final class Wallet {
|
|||
BigInteger picoMob = amount.requireMobileCoin().toPicoMobBigInteger();
|
||||
AccountSnapshot accountSnapshot = getCachedAccountSnapshot();
|
||||
Amount minimumFee = getCachedMinimumTxFee();
|
||||
Money.MobileCoin money;
|
||||
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 {
|
||||
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);
|
||||
return Money.MobileCoin.ZERO;
|
||||
} catch (NetworkException | FogSyncException e) {
|
||||
|
@ -288,22 +295,31 @@ public final class Wallet {
|
|||
try {
|
||||
Receipt receipt = Receipt.fromBytes(receiptBytes);
|
||||
Receipt.Status status = mobileCoinClient.getReceiptStatus(receipt);
|
||||
ReceivedTransactionStatus txStatus = null;
|
||||
switch (status) {
|
||||
case UNKNOWN:
|
||||
Log.w(TAG, "Unknown received Transaction Status");
|
||||
return ReceivedTransactionStatus.inProgress();
|
||||
txStatus = ReceivedTransactionStatus.inProgress();
|
||||
break;
|
||||
case FAILED:
|
||||
return ReceivedTransactionStatus.failed();
|
||||
txStatus = ReceivedTransactionStatus.failed();
|
||||
break;
|
||||
case RECEIVED:
|
||||
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:
|
||||
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) {
|
||||
Log.w(TAG, e);
|
||||
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);
|
||||
} catch (AmountDecoderException e) {
|
||||
Log.w(TAG, "Failed to decode amount", e);
|
||||
|
@ -322,11 +338,16 @@ public final class Wallet {
|
|||
if (defragmentFirst) {
|
||||
try {
|
||||
defragmentFees = defragment(amount, results);
|
||||
SignalStore.paymentsValues().setEnclaveFailure(false);
|
||||
} catch (InsufficientFundsException e) {
|
||||
Log.w(TAG, "Insufficient funds", e);
|
||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.INSUFFICIENT_FUNDS, true));
|
||||
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);
|
||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, true));
|
||||
return;
|
||||
|
@ -358,6 +379,7 @@ public final class Wallet {
|
|||
Amount.ofMOB(feeMobileCoin.toPicoMobBigInteger()),
|
||||
TxOutMemoBuilder.createSenderAndDestinationRTHMemoBuilder(account));
|
||||
}
|
||||
SignalStore.paymentsValues().setEnclaveFailure(false);
|
||||
} catch (InsufficientFundsException e) {
|
||||
Log.w(TAG, "Insufficient funds", e);
|
||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.INSUFFICIENT_FUNDS, false));
|
||||
|
@ -378,6 +400,7 @@ public final class Wallet {
|
|||
} catch (AttestationException e) {
|
||||
Log.w(TAG, "Attestation problem", e);
|
||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, false));
|
||||
SignalStore.paymentsValues().setEnclaveFailure(true);
|
||||
} catch (NetworkException e) {
|
||||
Log.w(TAG, "Network problem", e);
|
||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, false));
|
||||
|
@ -399,6 +422,7 @@ public final class Wallet {
|
|||
mobileCoinClient.submitTransaction(pendingTransaction.getTransaction());
|
||||
Log.i(TAG, "Transaction submitted");
|
||||
results.add(TransactionSubmissionResult.successfullySubmitted(new PaymentTransactionId.MobileCoin(pendingTransaction.getTransaction().toByteArray(), pendingTransaction.getReceipt().toByteArray(), feeMobileCoin)));
|
||||
SignalStore.paymentsValues().setEnclaveFailure(false);
|
||||
} catch (NetworkException e) {
|
||||
Log.w(TAG, "Network problem", e);
|
||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.NETWORK_FAILURE, false));
|
||||
|
@ -408,6 +432,7 @@ public final class Wallet {
|
|||
} catch (AttestationException e) {
|
||||
Log.w(TAG, "Attestation problem", e);
|
||||
results.add(TransactionSubmissionResult.failure(TransactionSubmissionResult.ErrorCode.GENERIC_FAILURE, false));
|
||||
SignalStore.paymentsValues().setEnclaveFailure(true);
|
||||
} catch (SerializationException e) {
|
||||
Log.w(TAG, "Serialization problem", e);
|
||||
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.preferences.RecipientHasNotEnabledPaymentsDialog;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.PlayStoreUtil;
|
||||
import org.thoughtcrime.securesms.util.SpanUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.navigation.SafeNavigation;
|
||||
|
@ -144,6 +145,17 @@ public class CreatePaymentFragment extends LoggingFragment {
|
|||
viewModel.getNote().observe(getViewLifecycleOwner(), this::updateNote);
|
||||
viewModel.getSpendableBalance().observe(getViewLifecycleOwner(), this::updateBalance);
|
||||
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) {
|
||||
|
|
|
@ -45,6 +45,7 @@ public class CreatePaymentViewModel extends ViewModel {
|
|||
private final LiveData<Boolean> isValidAmount;
|
||||
private final Store<InputState> inputState;
|
||||
private final LiveData<Boolean> isPaymentsSupportedByPayee;
|
||||
private final LiveData<Boolean> enclaveFailure;
|
||||
|
||||
private final PayeeParcelable payee;
|
||||
private final MutableLiveData<CharSequence> note;
|
||||
|
@ -55,6 +56,7 @@ public class CreatePaymentViewModel extends ViewModel {
|
|||
this.note = new MutableLiveData<>(note);
|
||||
this.inputState = new Store<>(new InputState());
|
||||
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()) {
|
||||
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));
|
||||
}
|
||||
|
||||
@NonNull LiveData<Boolean> getEnclaveFailure() {
|
||||
return enclaveFailure;
|
||||
}
|
||||
|
||||
@NonNull LiveData<InputState> getInputState() {
|
||||
return inputState.getStateLiveData();
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ import org.signal.core.util.logging.Log;
|
|||
import org.thoughtcrime.securesms.LoggingFragment;
|
||||
import org.thoughtcrime.securesms.PaymentPreferencesDirections;
|
||||
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.help.HelpFragment;
|
||||
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.PaymentItem;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.PlayStoreUtil;
|
||||
import org.thoughtcrime.securesms.util.SpanUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.navigation.SafeNavigation;
|
||||
import org.thoughtcrime.securesms.util.views.Stub;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -95,6 +100,7 @@ public class PaymentsHomeFragment extends LoggingFragment {
|
|||
View sendMoney = view.findViewById(R.id.button_end_frame);
|
||||
View refresh = view.findViewById(R.id.payments_home_fragment_header_refresh);
|
||||
LottieAnimationView refreshAnimation = view.findViewById(R.id.payments_home_fragment_header_refresh_animation);
|
||||
Stub<ReminderView> reminderView = ViewUtil.findStubById(view, R.id.reminder);
|
||||
|
||||
toolbar.setNavigationOnClickListener(v -> {
|
||||
viewModel.markAllPaymentsSeen();
|
||||
|
@ -104,14 +110,18 @@ public class PaymentsHomeFragment extends LoggingFragment {
|
|||
toolbar.setOnMenuItemClickListener(this::onMenuItemSelected);
|
||||
|
||||
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());
|
||||
} else {
|
||||
showPaymentsDisabledDialog();
|
||||
}
|
||||
});
|
||||
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());
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -261,9 +285,23 @@ public class PaymentsHomeFragment extends LoggingFragment {
|
|||
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) {
|
||||
if (item.getItemId() == R.id.payments_home_fragment_menu_transfer_to_exchange) {
|
||||
SafeNavigation.safeNavigate(NavHostFragment.findNavController(this), R.id.action_paymentsHome_to_paymentsTransfer);
|
||||
if (viewModel.isEnclaveFailurePresent()) {
|
||||
showUpdateIsRequiredDialog();
|
||||
} else {
|
||||
SafeNavigation.safeNavigate(NavHostFragment.findNavController(this), R.id.action_paymentsHome_to_paymentsTransfer);
|
||||
}
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.payments_home_fragment_menu_set_currency) {
|
||||
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 SingleLiveEvent<PaymentStateEvent> paymentStateEvents;
|
||||
private final SingleLiveEvent<ErrorEnabling> errorEnablingPayments;
|
||||
private final LiveData<Boolean> enclaveFailure;
|
||||
|
||||
private final PaymentsHomeRepository paymentsHomeRepository;
|
||||
private final CurrencyExchangeRepository currencyExchangeRepository;
|
||||
|
@ -72,7 +73,7 @@ public class PaymentsHomeViewModel extends ViewModel {
|
|||
this.exchangeLoadState = LiveDataUtil.mapDistinct(store.getStateLiveData(), PaymentsHomeState::getExchangeRateLoadState);
|
||||
this.paymentStateEvents = new SingleLiveEvent<>();
|
||||
this.errorEnablingPayments = new SingleLiveEvent<>();
|
||||
|
||||
this.enclaveFailure = LiveDataUtil.mapDistinct(SignalStore.paymentsValues().enclaveFailure(), isFailure -> isFailure);
|
||||
this.store.update(paymentsRepository.getRecentPayments(), this::updateRecentPayments);
|
||||
|
||||
LiveData<CurrencyExchange.ExchangeRate> liveExchangeRate = LiveDataUtil.combineLatest(SignalStore.paymentsValues().liveCurrentCurrency(),
|
||||
|
@ -109,6 +110,14 @@ public class PaymentsHomeViewModel extends ViewModel {
|
|||
return errorEnablingPayments;
|
||||
}
|
||||
|
||||
@NonNull LiveData<Boolean> getEnclaveFailure() {
|
||||
return enclaveFailure;
|
||||
}
|
||||
|
||||
@NonNull boolean isEnclaveFailurePresent() {
|
||||
return Boolean.TRUE.equals(getEnclaveFailure().getValue());
|
||||
}
|
||||
|
||||
@NonNull LiveData<MappingModelList> getList() {
|
||||
return list;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?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:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -13,22 +14,44 @@
|
|||
android:layout_height="@dimen/signal_m3_toolbar_height"
|
||||
android:minHeight="@dimen/signal_m3_toolbar_height"
|
||||
android:theme="?actionBarStyle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navigationIcon="@drawable/ic_arrow_left_24"
|
||||
app:title="@string/preferences__payments_beta"
|
||||
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
|
||||
layout="@layout/payments_home_fragment_header"
|
||||
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
|
||||
android:id="@+id/payments_home_fragment_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:scrollIndicators="top|bottom"
|
||||
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_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 -->
|
||||
<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>
|
||||
|
@ -3018,6 +3024,14 @@
|
|||
<string name="PaymentsHomeFragment__enable">Turn On</string>
|
||||
<!-- Alert dialog button to not enable payment lock for now -->
|
||||
<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 -->
|
||||
<!-- Toolbar title -->
|
||||
|
|
Loading…
Add table
Reference in a new issue