mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 09:20:19 +01:00
Migrate local account data into SignalStore.
This commit is contained in:
committed by
Cody Henthorne
parent
87f175a96b
commit
8aea20f147
@@ -0,0 +1,155 @@
|
||||
package org.thoughtcrime.securesms.keyvalue
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.whispersystems.signalservice.api.push.ACI
|
||||
import org.whispersystems.signalservice.api.push.PNI
|
||||
|
||||
internal class AccountValues internal constructor(store: KeyValueStore) : SignalStoreValues(store) {
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(AccountValues::class.java)
|
||||
private const val KEY_ACI = "account.aci"
|
||||
private const val KEY_PNI = "account.pni"
|
||||
private const val KEY_SERVICE_PASSWORD = "account.service_password"
|
||||
private const val KEY_IS_REGISTERED = "account.is_registered"
|
||||
private const val KEY_REGISTRATION_ID = "account.registration_id"
|
||||
private const val KEY_FCM_ENABLED = "account.fcm_enabled"
|
||||
private const val KEY_FCM_TOKEN = "account.fcm_token"
|
||||
private const val KEY_FCM_TOKEN_VERSION = "account.fcm_token_version"
|
||||
private const val KEY_FCM_TOKEN_LAST_SET_TIME = "account.fcm_token_last_set_time"
|
||||
|
||||
@VisibleForTesting
|
||||
const val KEY_E164 = "account.e164"
|
||||
}
|
||||
|
||||
init {
|
||||
if (!store.containsKey(KEY_ACI)) {
|
||||
migrateFromSharedPrefs(ApplicationDependencies.getApplication())
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onFirstEverAppLaunch() = Unit
|
||||
|
||||
public override fun getKeysToIncludeInBackup(): List<String> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
/** The local user's [ACI]. */
|
||||
val aci: ACI?
|
||||
get() = ACI.parseOrNull(getString(KEY_ACI, null))
|
||||
|
||||
fun setAci(aci: ACI) {
|
||||
putString(KEY_ACI, aci.toString())
|
||||
}
|
||||
|
||||
/** The local user's [PNI]. */
|
||||
val pni: PNI?
|
||||
get() = PNI.parseOrNull(getString(KEY_ACI, null))
|
||||
|
||||
fun setPni(pni: PNI) {
|
||||
putString(KEY_PNI, pni.toString())
|
||||
}
|
||||
|
||||
/** The local user's E164. */
|
||||
val e164: String?
|
||||
get() = getString(KEY_E164, null)
|
||||
|
||||
fun setE164(e164: String) {
|
||||
putString(KEY_E164, e164)
|
||||
}
|
||||
|
||||
/** The password for communicating with the Signal service. */
|
||||
val servicePassword: String?
|
||||
get() = getString(KEY_SERVICE_PASSWORD, null)
|
||||
|
||||
fun setServicePassword(servicePassword: String) {
|
||||
putString(KEY_SERVICE_PASSWORD, servicePassword)
|
||||
}
|
||||
|
||||
/** A randomly-generated value that represents this registration instance. Helps the server know if you reinstalled. */
|
||||
var registrationId: Int
|
||||
get() = getInteger(KEY_REGISTRATION_ID, 0)
|
||||
set(value) = putInteger(KEY_REGISTRATION_ID, value)
|
||||
|
||||
/** Indicates whether the user has the ability to receive FCM messages. Largely coupled to whether they have Play Service. */
|
||||
var fcmEnabled: Boolean
|
||||
@JvmName("isFcmEnabled")
|
||||
get() = getBoolean(KEY_FCM_ENABLED, false)
|
||||
set(value) = putBoolean(KEY_FCM_ENABLED, value)
|
||||
|
||||
/** The FCM token, which allows the server to send us FCM messages. */
|
||||
var fcmToken: String?
|
||||
get() {
|
||||
val tokenVersion: Int = getInteger(KEY_FCM_TOKEN_VERSION, 0)
|
||||
return if (tokenVersion == Util.getCanonicalVersionCode()) {
|
||||
getString(KEY_FCM_TOKEN, null)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
set(value) {
|
||||
store.beginWrite()
|
||||
.putString(KEY_FCM_TOKEN, value)
|
||||
.putInteger(KEY_FCM_TOKEN_VERSION, Util.getCanonicalVersionCode())
|
||||
.putLong(KEY_FCM_TOKEN_LAST_SET_TIME, System.currentTimeMillis())
|
||||
.apply()
|
||||
}
|
||||
|
||||
/** When we last set the [fcmToken] */
|
||||
val fcmTokenLastSetTime: Long
|
||||
get() = getLong(KEY_FCM_TOKEN_LAST_SET_TIME, 0)
|
||||
|
||||
/** Whether or not the user is registered with the Signal service. */
|
||||
val isRegistered: Boolean
|
||||
get() = getBoolean(KEY_IS_REGISTERED, false)
|
||||
|
||||
fun setRegistered(registered: Boolean) {
|
||||
Log.i(TAG, "Setting push registered: $registered", Throwable())
|
||||
|
||||
val previous = isRegistered
|
||||
|
||||
putBoolean(KEY_IS_REGISTERED, registered)
|
||||
|
||||
ApplicationDependencies.getIncomingMessageObserver().notifyRegistrationChanged()
|
||||
|
||||
if (previous != registered) {
|
||||
Recipient.self().live().refresh()
|
||||
}
|
||||
|
||||
if (previous && !registered) {
|
||||
clearLocalCredentials(ApplicationDependencies.getApplication())
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearLocalCredentials(context: Context) {
|
||||
putString(KEY_SERVICE_PASSWORD, Util.getSecret(18))
|
||||
|
||||
val newProfileKey = ProfileKeyUtil.createNew()
|
||||
val self = Recipient.self()
|
||||
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileKey(self.id, newProfileKey)
|
||||
ApplicationDependencies.getGroupsV2Authorization().clear()
|
||||
}
|
||||
|
||||
private fun migrateFromSharedPrefs(context: Context) {
|
||||
Log.i(TAG, "Migrating account values from shared prefs.")
|
||||
|
||||
putString(KEY_ACI, TextSecurePreferences.getStringPreference(context, "pref_local_uuid", null))
|
||||
putString(KEY_E164, TextSecurePreferences.getStringPreference(context, "pref_local_number", null))
|
||||
putString(KEY_SERVICE_PASSWORD, TextSecurePreferences.getStringPreference(context, "pref_gcm_password", null))
|
||||
putBoolean(KEY_IS_REGISTERED, TextSecurePreferences.getBooleanPreference(context, "pref_gcm_registered", false))
|
||||
putInteger(KEY_REGISTRATION_ID, TextSecurePreferences.getIntegerPreference(context, "pref_local_registration_id", 0))
|
||||
putBoolean(KEY_FCM_ENABLED, !TextSecurePreferences.getBooleanPreference(context, "pref_gcm_disabled", false))
|
||||
putString(KEY_FCM_TOKEN, TextSecurePreferences.getStringPreference(context, "pref_gcm_registration_id", null))
|
||||
putInteger(KEY_FCM_TOKEN_VERSION, TextSecurePreferences.getIntegerPreference(context, "pref_gcm_registration_id_version", 0))
|
||||
putLong(KEY_FCM_TOKEN_LAST_SET_TIME, TextSecurePreferences.getLongPreference(context, "pref_gcm_registration_id_last_set_time", 0))
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,9 @@ import org.signal.donations.StripeApi
|
||||
import org.thoughtcrime.securesms.badges.Badges
|
||||
import org.thoughtcrime.securesms.badges.models.Badge
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.BadgeList
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.payments.currency.CurrencyUtil
|
||||
import org.thoughtcrime.securesms.subscription.LevelUpdateOperation
|
||||
import org.thoughtcrime.securesms.subscription.Subscriber
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.api.subscriptions.IdempotencyKey
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
||||
import java.util.Currency
|
||||
@@ -52,7 +50,7 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign
|
||||
val currency: Currency? = if (currencyCode == null) {
|
||||
val localeCurrency = CurrencyUtil.getCurrencyByLocale(Locale.getDefault())
|
||||
if (localeCurrency == null) {
|
||||
val e164 = TextSecurePreferences.getLocalNumber(ApplicationDependencies.getApplication())
|
||||
val e164: String? = SignalStore.account().e164
|
||||
if (e164 == null) {
|
||||
null
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.thoughtcrime.securesms.keyvalue;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface KeyValuePersistentStorage {
|
||||
void writeDataSet(@NonNull KeyValueDataSet dataSet, @NonNull Collection<String> removes);
|
||||
@NonNull KeyValueDataSet getDataSet();
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
package org.thoughtcrime.securesms.keyvalue;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -11,8 +8,6 @@ import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.KeyValueDatabase;
|
||||
import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -35,14 +30,14 @@ public final class KeyValueStore implements KeyValueReader {
|
||||
|
||||
private static final String TAG = Log.tag(KeyValueStore.class);
|
||||
|
||||
private final ExecutorService executor;
|
||||
private final KeyValueDatabase database;
|
||||
private final ExecutorService executor;
|
||||
private final KeyValuePersistentStorage storage;
|
||||
|
||||
private KeyValueDataSet dataSet;
|
||||
|
||||
public KeyValueStore(@NonNull Application application) {
|
||||
public KeyValueStore(@NonNull KeyValuePersistentStorage storage) {
|
||||
this.executor = SignalExecutors.newCachedSingleThreadExecutor("signal-KeyValueStore");
|
||||
this.database = KeyValueDatabase.getInstance(application);
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@@ -150,12 +145,12 @@ public final class KeyValueStore implements KeyValueReader {
|
||||
dataSet.putAll(newDataSet);
|
||||
dataSet.removeAll(removes);
|
||||
|
||||
executor.execute(() -> database.writeDataSet(newDataSet, removes));
|
||||
executor.execute(() -> storage.writeDataSet(newDataSet, removes));
|
||||
}
|
||||
|
||||
private void initializeIfNecessary() {
|
||||
if (dataSet != null) return;
|
||||
this.dataSet = database.getDataSet();
|
||||
this.dataSet = storage.getDataSet();
|
||||
}
|
||||
|
||||
class Writer {
|
||||
|
||||
@@ -1,366 +0,0 @@
|
||||
package org.thoughtcrime.securesms.keyvalue;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Transformations;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.mobilecoin.lib.Mnemonics;
|
||||
import com.mobilecoin.lib.exceptions.BadMnemonicException;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.lock.v2.PinKeyboardType;
|
||||
import org.thoughtcrime.securesms.payments.Balance;
|
||||
import org.thoughtcrime.securesms.payments.Entropy;
|
||||
import org.thoughtcrime.securesms.payments.GeographicalRestrictions;
|
||||
import org.thoughtcrime.securesms.payments.Mnemonic;
|
||||
import org.thoughtcrime.securesms.payments.MobileCoinLedgerWrapper;
|
||||
import org.thoughtcrime.securesms.payments.currency.CurrencyUtil;
|
||||
import org.thoughtcrime.securesms.payments.proto.MobileCoinLedger;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.signalservice.api.payments.Money;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Currency;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class PaymentsValues extends SignalStoreValues {
|
||||
|
||||
private static final String TAG = Log.tag(PaymentsValues.class);
|
||||
|
||||
private static final String PAYMENTS_ENTROPY = "payments_entropy";
|
||||
private static final String MOB_PAYMENTS_ENABLED = "mob_payments_enabled";
|
||||
private static final String MOB_LEDGER = "mob_ledger";
|
||||
private static final String PAYMENTS_CURRENT_CURRENCY = "payments_current_currency";
|
||||
private static final String DEFAULT_CURRENCY_CODE = "GBP";
|
||||
private static final String USER_CONFIRMED_MNEMONIC = "mob_payments_user_confirmed_mnemonic";
|
||||
|
||||
private static final String SHOW_ABOUT_MOBILE_COIN_INFO_CARD = "mob_payments_show_about_mobile_coin_info_card";
|
||||
private static final String SHOW_ADDING_TO_YOUR_WALLET_INFO_CARD = "mob_payments_show_adding_to_your_wallet_info_card";
|
||||
private static final String SHOW_CASHING_OUT_INFO_CARD = "mob_payments_show_cashing_out_info_card";
|
||||
private static final String SHOW_RECOVERY_PHRASE_INFO_CARD = "mob_payments_show_recovery_phrase_info_card";
|
||||
private static final String SHOW_UPDATE_PIN_INFO_CARD = "mob_payments_show_update_pin_info_card";
|
||||
|
||||
private static final Money.MobileCoin LARGE_BALANCE_THRESHOLD = Money.mobileCoin(BigDecimal.valueOf(500));
|
||||
|
||||
private final MutableLiveData<Currency> liveCurrentCurrency;
|
||||
private final MutableLiveData<MobileCoinLedgerWrapper> liveMobileCoinLedger;
|
||||
private final LiveData<Balance> liveMobileCoinBalance;
|
||||
|
||||
PaymentsValues(@NonNull KeyValueStore store) {
|
||||
super(store);
|
||||
this.liveCurrentCurrency = new MutableLiveData<>(currentCurrency());
|
||||
this.liveMobileCoinLedger = new MutableLiveData<>(mobileCoinLatestFullLedger());
|
||||
this.liveMobileCoinBalance = Transformations.map(liveMobileCoinLedger, MobileCoinLedgerWrapper::getBalance);
|
||||
}
|
||||
|
||||
@Override void onFirstEverAppLaunch() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull List<String> getKeysToIncludeInBackup() {
|
||||
return Arrays.asList(PAYMENTS_ENTROPY,
|
||||
MOB_PAYMENTS_ENABLED,
|
||||
MOB_LEDGER,
|
||||
PAYMENTS_CURRENT_CURRENCY,
|
||||
DEFAULT_CURRENCY_CODE,
|
||||
USER_CONFIRMED_MNEMONIC,
|
||||
SHOW_ABOUT_MOBILE_COIN_INFO_CARD,
|
||||
SHOW_ADDING_TO_YOUR_WALLET_INFO_CARD,
|
||||
SHOW_CASHING_OUT_INFO_CARD,
|
||||
SHOW_RECOVERY_PHRASE_INFO_CARD,
|
||||
SHOW_UPDATE_PIN_INFO_CARD);
|
||||
}
|
||||
|
||||
public boolean userConfirmedMnemonic() {
|
||||
return getStore().getBoolean(USER_CONFIRMED_MNEMONIC, false);
|
||||
}
|
||||
|
||||
public void setUserConfirmedMnemonic(boolean userConfirmedMnemonic) {
|
||||
getStore().beginWrite().putBoolean(USER_CONFIRMED_MNEMONIC, userConfirmedMnemonic).commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consider using {@link #getPaymentsAvailability} which includes feature flag and region status.
|
||||
*/
|
||||
public boolean mobileCoinPaymentsEnabled() {
|
||||
KeyValueReader reader = getStore().beginRead();
|
||||
|
||||
return reader.getBoolean(MOB_PAYMENTS_ENABLED, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies feature flags and region restrictions to return an enum which describes the available feature set for the user.
|
||||
*/
|
||||
public PaymentsAvailability getPaymentsAvailability() {
|
||||
Context context = ApplicationDependencies.getApplication();
|
||||
|
||||
if (!TextSecurePreferences.isPushRegistered(context) ||
|
||||
!GeographicalRestrictions.e164Allowed(TextSecurePreferences.getLocalNumber(context)))
|
||||
{
|
||||
return PaymentsAvailability.NOT_IN_REGION;
|
||||
}
|
||||
|
||||
if (FeatureFlags.payments()) {
|
||||
if (mobileCoinPaymentsEnabled()) {
|
||||
return PaymentsAvailability.WITHDRAW_AND_SEND;
|
||||
} else {
|
||||
return PaymentsAvailability.REGISTRATION_AVAILABLE;
|
||||
}
|
||||
} else {
|
||||
if (mobileCoinPaymentsEnabled()) {
|
||||
return PaymentsAvailability.WITHDRAW_ONLY;
|
||||
} else {
|
||||
return PaymentsAvailability.DISABLED_REMOTELY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public void setMobileCoinPaymentsEnabled(boolean isMobileCoinPaymentsEnabled) {
|
||||
if (mobileCoinPaymentsEnabled() == isMobileCoinPaymentsEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMobileCoinPaymentsEnabled) {
|
||||
Entropy entropy = getPaymentsEntropy();
|
||||
if (entropy == null) {
|
||||
entropy = Entropy.generateNew();
|
||||
Log.i(TAG, "Generated new payments entropy");
|
||||
}
|
||||
|
||||
getStore().beginWrite()
|
||||
.putBlob(PAYMENTS_ENTROPY, entropy.getBytes())
|
||||
.putBoolean(MOB_PAYMENTS_ENABLED, true)
|
||||
.putString(PAYMENTS_CURRENT_CURRENCY, currentCurrency().getCurrencyCode())
|
||||
.commit();
|
||||
} else {
|
||||
getStore().beginWrite()
|
||||
.putBoolean(MOB_PAYMENTS_ENABLED, false)
|
||||
.putBoolean(USER_CONFIRMED_MNEMONIC, false)
|
||||
.commit();
|
||||
}
|
||||
|
||||
DatabaseFactory.getRecipientDatabase(ApplicationDependencies.getApplication()).markNeedsSync(Recipient.self().getId());
|
||||
StorageSyncHelper.scheduleSyncForDataChange();
|
||||
}
|
||||
|
||||
public @NonNull Mnemonic getPaymentsMnemonic() {
|
||||
Entropy paymentsEntropy = getPaymentsEntropy();
|
||||
if (paymentsEntropy == null) {
|
||||
throw new IllegalStateException("Entropy has not been set");
|
||||
}
|
||||
|
||||
return paymentsEntropy.asMnemonic();
|
||||
}
|
||||
|
||||
/**
|
||||
* True if a local entropy is set, regardless of whether payments is currently enabled.
|
||||
*/
|
||||
public boolean hasPaymentsEntropy() {
|
||||
return getPaymentsEntropy() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local payments entropy, regardless of whether payments is currently enabled.
|
||||
* <p>
|
||||
* And null if has never been set.
|
||||
*/
|
||||
public @Nullable Entropy getPaymentsEntropy() {
|
||||
return Entropy.fromBytes(getStore().getBlob(PAYMENTS_ENTROPY, null));
|
||||
}
|
||||
|
||||
public @NonNull Balance mobileCoinLatestBalance() {
|
||||
return mobileCoinLatestFullLedger().getBalance();
|
||||
}
|
||||
|
||||
public @NonNull LiveData<MobileCoinLedgerWrapper> liveMobileCoinLedger() {
|
||||
return liveMobileCoinLedger;
|
||||
}
|
||||
|
||||
public @NonNull LiveData<Balance> liveMobileCoinBalance() {
|
||||
return liveMobileCoinBalance;
|
||||
}
|
||||
|
||||
public void setCurrentCurrency(@NonNull Currency currentCurrency) {
|
||||
getStore().beginWrite()
|
||||
.putString(PAYMENTS_CURRENT_CURRENCY, currentCurrency.getCurrencyCode())
|
||||
.commit();
|
||||
|
||||
liveCurrentCurrency.postValue(currentCurrency);
|
||||
}
|
||||
|
||||
public @NonNull Currency currentCurrency() {
|
||||
String currencyCode = getStore().getString(PAYMENTS_CURRENT_CURRENCY, null);
|
||||
return currencyCode == null ? determineCurrency()
|
||||
: Currency.getInstance(currencyCode);
|
||||
}
|
||||
|
||||
public @NonNull MutableLiveData<Currency> liveCurrentCurrency() {
|
||||
return liveCurrentCurrency;
|
||||
}
|
||||
|
||||
public boolean showAboutMobileCoinInfoCard() {
|
||||
return getStore().getBoolean(SHOW_ABOUT_MOBILE_COIN_INFO_CARD, true);
|
||||
}
|
||||
|
||||
public boolean showAddingToYourWalletInfoCard() {
|
||||
return getStore().getBoolean(SHOW_ADDING_TO_YOUR_WALLET_INFO_CARD, true);
|
||||
}
|
||||
|
||||
public boolean showCashingOutInfoCard() {
|
||||
return getStore().getBoolean(SHOW_CASHING_OUT_INFO_CARD, true);
|
||||
}
|
||||
|
||||
public boolean showRecoveryPhraseInfoCard() {
|
||||
if (userHasLargeBalance()) {
|
||||
return getStore().getBoolean(SHOW_CASHING_OUT_INFO_CARD, true);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean showUpdatePinInfoCard() {
|
||||
if (userHasLargeBalance() &&
|
||||
SignalStore.kbsValues().hasPin() &&
|
||||
!SignalStore.kbsValues().hasOptedOut() &&
|
||||
SignalStore.pinValues().getKeyboardType().equals(PinKeyboardType.NUMERIC)) {
|
||||
return getStore().getBoolean(SHOW_CASHING_OUT_INFO_CARD, true);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void dismissAboutMobileCoinInfoCard() {
|
||||
getStore().beginWrite()
|
||||
.putBoolean(SHOW_ABOUT_MOBILE_COIN_INFO_CARD, false)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public void dismissAddingToYourWalletInfoCard() {
|
||||
getStore().beginWrite()
|
||||
.putBoolean(SHOW_ADDING_TO_YOUR_WALLET_INFO_CARD, false)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public void dismissCashingOutInfoCard() {
|
||||
getStore().beginWrite()
|
||||
.putBoolean(SHOW_CASHING_OUT_INFO_CARD, false)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public void dismissRecoveryPhraseInfoCard() {
|
||||
getStore().beginWrite()
|
||||
.putBoolean(SHOW_RECOVERY_PHRASE_INFO_CARD, false)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public void dismissUpdatePinInfoCard() {
|
||||
getStore().beginWrite()
|
||||
.putBoolean(SHOW_UPDATE_PIN_INFO_CARD, false)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public void setMobileCoinFullLedger(@NonNull MobileCoinLedgerWrapper ledger) {
|
||||
getStore().beginWrite()
|
||||
.putBlob(MOB_LEDGER, ledger.serialize())
|
||||
.commit();
|
||||
|
||||
liveMobileCoinLedger.postValue(ledger);
|
||||
}
|
||||
|
||||
public @NonNull MobileCoinLedgerWrapper mobileCoinLatestFullLedger() {
|
||||
byte[] blob = getStore().getBlob(MOB_LEDGER, null);
|
||||
|
||||
if (blob == null) {
|
||||
return new MobileCoinLedgerWrapper(MobileCoinLedger.getDefaultInstance());
|
||||
}
|
||||
|
||||
try {
|
||||
return new MobileCoinLedgerWrapper(MobileCoinLedger.parseFrom(blob));
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
Log.w(TAG, "Bad cached ledger, clearing", e);
|
||||
setMobileCoinFullLedger(new MobileCoinLedgerWrapper(MobileCoinLedger.getDefaultInstance()));
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private @NonNull Currency determineCurrency() {
|
||||
String localE164 = TextSecurePreferences.getLocalNumber(ApplicationDependencies.getApplication());
|
||||
if (localE164 == null) {
|
||||
localE164 = "";
|
||||
}
|
||||
return Util.firstNonNull(CurrencyUtil.getCurrencyByE164(localE164),
|
||||
CurrencyUtil.getCurrencyByLocale(Locale.getDefault()),
|
||||
Currency.getInstance(DEFAULT_CURRENCY_CODE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not trigger a storage sync.
|
||||
*/
|
||||
public void setEnabledAndEntropy(boolean enabled, @Nullable Entropy entropy) {
|
||||
KeyValueStore.Writer writer = getStore().beginWrite();
|
||||
|
||||
if (entropy != null) {
|
||||
writer.putBlob(PAYMENTS_ENTROPY, entropy.getBytes());
|
||||
}
|
||||
|
||||
writer.putBoolean(MOB_PAYMENTS_ENABLED, enabled)
|
||||
.commit();
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public WalletRestoreResult restoreWallet(@NonNull String mnemonic) {
|
||||
byte[] entropyFromMnemonic;
|
||||
try {
|
||||
entropyFromMnemonic = Mnemonics.bip39EntropyFromMnemonic(mnemonic);
|
||||
} catch (BadMnemonicException e) {
|
||||
return WalletRestoreResult.MNEMONIC_ERROR;
|
||||
}
|
||||
Entropy paymentsEntropy = getPaymentsEntropy();
|
||||
if (paymentsEntropy != null) {
|
||||
byte[] existingEntropy = paymentsEntropy.getBytes();
|
||||
if (Arrays.equals(existingEntropy, entropyFromMnemonic)) {
|
||||
setMobileCoinPaymentsEnabled(true);
|
||||
setUserConfirmedMnemonic(true);
|
||||
return WalletRestoreResult.ENTROPY_UNCHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
getStore().beginWrite()
|
||||
.putBlob(PAYMENTS_ENTROPY, entropyFromMnemonic)
|
||||
.putBoolean(MOB_PAYMENTS_ENABLED, true)
|
||||
.remove(MOB_LEDGER)
|
||||
.putBoolean(USER_CONFIRMED_MNEMONIC, true)
|
||||
.commit();
|
||||
|
||||
liveMobileCoinLedger.postValue(new MobileCoinLedgerWrapper(MobileCoinLedger.getDefaultInstance()));
|
||||
|
||||
StorageSyncHelper.scheduleSyncForDataChange();
|
||||
|
||||
return WalletRestoreResult.ENTROPY_CHANGED;
|
||||
}
|
||||
|
||||
public enum WalletRestoreResult {
|
||||
ENTROPY_CHANGED,
|
||||
ENTROPY_UNCHANGED,
|
||||
MNEMONIC_ERROR
|
||||
}
|
||||
|
||||
private boolean userHasLargeBalance() {
|
||||
return mobileCoinLatestBalance().getFullAmount().requireMobileCoin().greaterThan(LARGE_BALANCE_THRESHOLD);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
package org.thoughtcrime.securesms.keyvalue
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import com.google.protobuf.InvalidProtocolBufferException
|
||||
import com.mobilecoin.lib.Mnemonics
|
||||
import com.mobilecoin.lib.exceptions.BadMnemonicException
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.PaymentsValues.WalletRestoreResult
|
||||
import org.thoughtcrime.securesms.lock.v2.PinKeyboardType
|
||||
import org.thoughtcrime.securesms.payments.Balance
|
||||
import org.thoughtcrime.securesms.payments.Entropy
|
||||
import org.thoughtcrime.securesms.payments.GeographicalRestrictions
|
||||
import org.thoughtcrime.securesms.payments.Mnemonic
|
||||
import org.thoughtcrime.securesms.payments.MobileCoinLedgerWrapper
|
||||
import org.thoughtcrime.securesms.payments.currency.CurrencyUtil
|
||||
import org.thoughtcrime.securesms.payments.proto.MobileCoinLedger
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.whispersystems.signalservice.api.payments.Money
|
||||
import java.lang.AssertionError
|
||||
import java.lang.IllegalStateException
|
||||
import java.math.BigDecimal
|
||||
import java.util.Arrays
|
||||
import java.util.Currency
|
||||
import java.util.Locale
|
||||
|
||||
internal class PaymentsValues internal constructor(store: KeyValueStore) : SignalStoreValues(store) {
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(PaymentsValues::class.java)
|
||||
|
||||
private const val PAYMENTS_ENTROPY = "payments_entropy"
|
||||
private const val MOB_PAYMENTS_ENABLED = "mob_payments_enabled"
|
||||
private const val MOB_LEDGER = "mob_ledger"
|
||||
private const val PAYMENTS_CURRENT_CURRENCY = "payments_current_currency"
|
||||
private const val DEFAULT_CURRENCY_CODE = "GBP"
|
||||
private const val USER_CONFIRMED_MNEMONIC = "mob_payments_user_confirmed_mnemonic"
|
||||
private const val SHOW_ABOUT_MOBILE_COIN_INFO_CARD = "mob_payments_show_about_mobile_coin_info_card"
|
||||
private const val SHOW_ADDING_TO_YOUR_WALLET_INFO_CARD = "mob_payments_show_adding_to_your_wallet_info_card"
|
||||
private const val SHOW_CASHING_OUT_INFO_CARD = "mob_payments_show_cashing_out_info_card"
|
||||
private const val SHOW_RECOVERY_PHRASE_INFO_CARD = "mob_payments_show_recovery_phrase_info_card"
|
||||
private const val SHOW_UPDATE_PIN_INFO_CARD = "mob_payments_show_update_pin_info_card"
|
||||
|
||||
private val LARGE_BALANCE_THRESHOLD = Money.mobileCoin(BigDecimal.valueOf(500))
|
||||
}
|
||||
|
||||
private val liveCurrentCurrency: MutableLiveData<Currency> by lazy { MutableLiveData(currentCurrency()) }
|
||||
private val liveMobileCoinLedger: MutableLiveData<MobileCoinLedgerWrapper> by lazy { MutableLiveData(mobileCoinLatestFullLedger()) }
|
||||
private val liveMobileCoinBalance: LiveData<Balance> by lazy { Transformations.map(liveMobileCoinLedger) { obj: MobileCoinLedgerWrapper -> obj.balance } }
|
||||
|
||||
public override fun onFirstEverAppLaunch() {}
|
||||
|
||||
public override fun getKeysToIncludeInBackup(): List<String> {
|
||||
return listOf(
|
||||
PAYMENTS_ENTROPY,
|
||||
MOB_PAYMENTS_ENABLED,
|
||||
MOB_LEDGER,
|
||||
PAYMENTS_CURRENT_CURRENCY,
|
||||
DEFAULT_CURRENCY_CODE,
|
||||
USER_CONFIRMED_MNEMONIC,
|
||||
SHOW_ABOUT_MOBILE_COIN_INFO_CARD,
|
||||
SHOW_ADDING_TO_YOUR_WALLET_INFO_CARD,
|
||||
SHOW_CASHING_OUT_INFO_CARD,
|
||||
SHOW_RECOVERY_PHRASE_INFO_CARD,
|
||||
SHOW_UPDATE_PIN_INFO_CARD
|
||||
)
|
||||
}
|
||||
|
||||
fun userConfirmedMnemonic(): Boolean {
|
||||
return store.getBoolean(USER_CONFIRMED_MNEMONIC, false)
|
||||
}
|
||||
|
||||
fun setUserConfirmedMnemonic(userConfirmedMnemonic: Boolean) {
|
||||
store.beginWrite().putBoolean(USER_CONFIRMED_MNEMONIC, userConfirmedMnemonic).commit()
|
||||
}
|
||||
|
||||
/**
|
||||
* Consider using [.getPaymentsAvailability] which includes feature flag and region status.
|
||||
*/
|
||||
fun mobileCoinPaymentsEnabled(): Boolean {
|
||||
return getBoolean(MOB_PAYMENTS_ENABLED, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies feature flags and region restrictions to return an enum which describes the available feature set for the user.
|
||||
*/
|
||||
val paymentsAvailability: PaymentsAvailability
|
||||
get() {
|
||||
if (!SignalStore.account().isRegistered ||
|
||||
!GeographicalRestrictions.e164Allowed(Recipient.self().requireE164())
|
||||
) {
|
||||
return PaymentsAvailability.NOT_IN_REGION
|
||||
}
|
||||
return if (FeatureFlags.payments()) {
|
||||
if (mobileCoinPaymentsEnabled()) {
|
||||
PaymentsAvailability.WITHDRAW_AND_SEND
|
||||
} else {
|
||||
PaymentsAvailability.REGISTRATION_AVAILABLE
|
||||
}
|
||||
} else {
|
||||
if (mobileCoinPaymentsEnabled()) {
|
||||
PaymentsAvailability.WITHDRAW_ONLY
|
||||
} else {
|
||||
PaymentsAvailability.DISABLED_REMOTELY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun setMobileCoinPaymentsEnabled(isMobileCoinPaymentsEnabled: Boolean) {
|
||||
if (mobileCoinPaymentsEnabled() == isMobileCoinPaymentsEnabled) {
|
||||
return
|
||||
}
|
||||
if (isMobileCoinPaymentsEnabled) {
|
||||
var entropy = paymentsEntropy
|
||||
if (entropy == null) {
|
||||
entropy = Entropy.generateNew()
|
||||
Log.i(TAG, "Generated new payments entropy")
|
||||
}
|
||||
store.beginWrite()
|
||||
.putBlob(PAYMENTS_ENTROPY, entropy.bytes)
|
||||
.putBoolean(MOB_PAYMENTS_ENABLED, true)
|
||||
.putString(PAYMENTS_CURRENT_CURRENCY, currentCurrency().currencyCode)
|
||||
.commit()
|
||||
} else {
|
||||
store.beginWrite()
|
||||
.putBoolean(MOB_PAYMENTS_ENABLED, false)
|
||||
.putBoolean(USER_CONFIRMED_MNEMONIC, false)
|
||||
.commit()
|
||||
}
|
||||
DatabaseFactory.getRecipientDatabase(ApplicationDependencies.getApplication()).markNeedsSync(Recipient.self().id)
|
||||
StorageSyncHelper.scheduleSyncForDataChange()
|
||||
}
|
||||
|
||||
val paymentsMnemonic: Mnemonic
|
||||
get() {
|
||||
val paymentsEntropy = paymentsEntropy ?: throw IllegalStateException("Entropy has not been set")
|
||||
return paymentsEntropy.asMnemonic()
|
||||
}
|
||||
|
||||
/**
|
||||
* True if a local entropy is set, regardless of whether payments is currently enabled.
|
||||
*/
|
||||
fun hasPaymentsEntropy(): Boolean {
|
||||
return paymentsEntropy != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local payments entropy, regardless of whether payments is currently enabled.
|
||||
*
|
||||
*
|
||||
* And null if has never been set.
|
||||
*/
|
||||
val paymentsEntropy: Entropy?
|
||||
get() = Entropy.fromBytes(store.getBlob(PAYMENTS_ENTROPY, null))
|
||||
|
||||
fun mobileCoinLatestBalance(): Balance {
|
||||
return mobileCoinLatestFullLedger().balance
|
||||
}
|
||||
|
||||
fun liveMobileCoinLedger(): LiveData<MobileCoinLedgerWrapper> {
|
||||
return liveMobileCoinLedger
|
||||
}
|
||||
|
||||
fun liveMobileCoinBalance(): LiveData<Balance> {
|
||||
return liveMobileCoinBalance
|
||||
}
|
||||
|
||||
fun setCurrentCurrency(currentCurrency: Currency) {
|
||||
store.beginWrite()
|
||||
.putString(PAYMENTS_CURRENT_CURRENCY, currentCurrency.currencyCode)
|
||||
.commit()
|
||||
liveCurrentCurrency.postValue(currentCurrency)
|
||||
}
|
||||
|
||||
fun currentCurrency(): Currency {
|
||||
val currencyCode = store.getString(PAYMENTS_CURRENT_CURRENCY, null)
|
||||
return if (currencyCode == null) determineCurrency() else Currency.getInstance(currencyCode)
|
||||
}
|
||||
|
||||
fun liveCurrentCurrency(): MutableLiveData<Currency> {
|
||||
return liveCurrentCurrency
|
||||
}
|
||||
|
||||
fun showAboutMobileCoinInfoCard(): Boolean {
|
||||
return store.getBoolean(SHOW_ABOUT_MOBILE_COIN_INFO_CARD, true)
|
||||
}
|
||||
|
||||
fun showAddingToYourWalletInfoCard(): Boolean {
|
||||
return store.getBoolean(SHOW_ADDING_TO_YOUR_WALLET_INFO_CARD, true)
|
||||
}
|
||||
|
||||
fun showCashingOutInfoCard(): Boolean {
|
||||
return store.getBoolean(SHOW_CASHING_OUT_INFO_CARD, true)
|
||||
}
|
||||
|
||||
fun showRecoveryPhraseInfoCard(): Boolean {
|
||||
return if (userHasLargeBalance()) {
|
||||
store.getBoolean(SHOW_CASHING_OUT_INFO_CARD, true)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun showUpdatePinInfoCard(): Boolean {
|
||||
return if (userHasLargeBalance() &&
|
||||
SignalStore.kbsValues().hasPin() &&
|
||||
!SignalStore.kbsValues().hasOptedOut() && SignalStore.pinValues().keyboardType == PinKeyboardType.NUMERIC
|
||||
) {
|
||||
store.getBoolean(SHOW_CASHING_OUT_INFO_CARD, true)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun dismissAboutMobileCoinInfoCard() {
|
||||
store.beginWrite()
|
||||
.putBoolean(SHOW_ABOUT_MOBILE_COIN_INFO_CARD, false)
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun dismissAddingToYourWalletInfoCard() {
|
||||
store.beginWrite()
|
||||
.putBoolean(SHOW_ADDING_TO_YOUR_WALLET_INFO_CARD, false)
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun dismissCashingOutInfoCard() {
|
||||
store.beginWrite()
|
||||
.putBoolean(SHOW_CASHING_OUT_INFO_CARD, false)
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun dismissRecoveryPhraseInfoCard() {
|
||||
store.beginWrite()
|
||||
.putBoolean(SHOW_RECOVERY_PHRASE_INFO_CARD, false)
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun dismissUpdatePinInfoCard() {
|
||||
store.beginWrite()
|
||||
.putBoolean(SHOW_UPDATE_PIN_INFO_CARD, false)
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun setMobileCoinFullLedger(ledger: MobileCoinLedgerWrapper) {
|
||||
store.beginWrite()
|
||||
.putBlob(MOB_LEDGER, ledger.serialize())
|
||||
.commit()
|
||||
liveMobileCoinLedger.postValue(ledger)
|
||||
}
|
||||
|
||||
fun mobileCoinLatestFullLedger(): MobileCoinLedgerWrapper {
|
||||
val blob = store.getBlob(MOB_LEDGER, null) ?: return MobileCoinLedgerWrapper(MobileCoinLedger.getDefaultInstance())
|
||||
return try {
|
||||
MobileCoinLedgerWrapper(MobileCoinLedger.parseFrom(blob))
|
||||
} catch (e: InvalidProtocolBufferException) {
|
||||
Log.w(TAG, "Bad cached ledger, clearing", e)
|
||||
setMobileCoinFullLedger(MobileCoinLedgerWrapper(MobileCoinLedger.getDefaultInstance()))
|
||||
throw AssertionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun determineCurrency(): Currency {
|
||||
val localE164: String = SignalStore.account().e164 ?: ""
|
||||
|
||||
return Util.firstNonNull(
|
||||
CurrencyUtil.getCurrencyByE164(localE164),
|
||||
CurrencyUtil.getCurrencyByLocale(Locale.getDefault()),
|
||||
Currency.getInstance(DEFAULT_CURRENCY_CODE)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not trigger a storage sync.
|
||||
*/
|
||||
fun setEnabledAndEntropy(enabled: Boolean, entropy: Entropy?) {
|
||||
val writer = store.beginWrite()
|
||||
|
||||
if (entropy != null) {
|
||||
writer.putBlob(PAYMENTS_ENTROPY, entropy.bytes)
|
||||
}
|
||||
|
||||
writer.putBoolean(MOB_PAYMENTS_ENABLED, enabled).commit()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun restoreWallet(mnemonic: String): WalletRestoreResult {
|
||||
val entropyFromMnemonic: ByteArray = try {
|
||||
Mnemonics.bip39EntropyFromMnemonic(mnemonic)
|
||||
} catch (e: BadMnemonicException) {
|
||||
return WalletRestoreResult.MNEMONIC_ERROR
|
||||
}
|
||||
|
||||
val paymentsEntropy = paymentsEntropy
|
||||
|
||||
if (paymentsEntropy != null) {
|
||||
val existingEntropy = paymentsEntropy.bytes
|
||||
if (Arrays.equals(existingEntropy, entropyFromMnemonic)) {
|
||||
setMobileCoinPaymentsEnabled(true)
|
||||
setUserConfirmedMnemonic(true)
|
||||
return WalletRestoreResult.ENTROPY_UNCHANGED
|
||||
}
|
||||
}
|
||||
|
||||
store.beginWrite()
|
||||
.putBlob(PAYMENTS_ENTROPY, entropyFromMnemonic)
|
||||
.putBoolean(MOB_PAYMENTS_ENABLED, true)
|
||||
.remove(MOB_LEDGER)
|
||||
.putBoolean(USER_CONFIRMED_MNEMONIC, true)
|
||||
.commit()
|
||||
|
||||
liveMobileCoinLedger.postValue(MobileCoinLedgerWrapper(MobileCoinLedger.getDefaultInstance()))
|
||||
StorageSyncHelper.scheduleSyncForDataChange()
|
||||
|
||||
return WalletRestoreResult.ENTROPY_CHANGED
|
||||
}
|
||||
|
||||
enum class WalletRestoreResult {
|
||||
ENTROPY_CHANGED, ENTROPY_UNCHANGED, MNEMONIC_ERROR
|
||||
}
|
||||
|
||||
private fun userHasLargeBalance(): Boolean {
|
||||
return mobileCoinLatestBalance().fullAmount.requireMobileCoin().greaterThan(LARGE_BALANCE_THRESHOLD)
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.PreferenceDataStore;
|
||||
|
||||
import org.thoughtcrime.securesms.database.KeyValueDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler;
|
||||
|
||||
@@ -15,9 +16,9 @@ import java.util.List;
|
||||
*/
|
||||
public final class SignalStore {
|
||||
|
||||
private static final SignalStore INSTANCE = new SignalStore();
|
||||
private KeyValueStore store;
|
||||
|
||||
private final KeyValueStore store;
|
||||
private final AccountValues accountValues;
|
||||
private final KbsValues kbsValues;
|
||||
private final RegistrationValues registrationValues;
|
||||
private final PinValues pinValues;
|
||||
@@ -40,8 +41,23 @@ public final class SignalStore {
|
||||
private final ChatColorsValues chatColorsValues;
|
||||
private final ImageEditorValues imageEditorValues;
|
||||
|
||||
private SignalStore() {
|
||||
this.store = new KeyValueStore(ApplicationDependencies.getApplication());
|
||||
private static volatile SignalStore instance;
|
||||
|
||||
private static @NonNull SignalStore getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (SignalStore.class) {
|
||||
if (instance == null) {
|
||||
instance = new SignalStore(new KeyValueStore(KeyValueDatabase.getInstance(ApplicationDependencies.getApplication())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private SignalStore(@NonNull KeyValueStore store) {
|
||||
this.store = store;
|
||||
this.accountValues = new AccountValues(store);
|
||||
this.kbsValues = new KbsValues(store);
|
||||
this.registrationValues = new RegistrationValues(store);
|
||||
this.pinValues = new PinValues(store);
|
||||
@@ -66,6 +82,7 @@ public final class SignalStore {
|
||||
}
|
||||
|
||||
public static void onFirstEverAppLaunch() {
|
||||
account().onFirstEverAppLaunch();
|
||||
kbsValues().onFirstEverAppLaunch();
|
||||
registrationValues().onFirstEverAppLaunch();
|
||||
pinValues().onFirstEverAppLaunch();
|
||||
@@ -91,6 +108,7 @@ public final class SignalStore {
|
||||
|
||||
public static List<String> getKeysToIncludeInBackup() {
|
||||
List<String> keys = new ArrayList<>();
|
||||
keys.addAll(account().getKeysToIncludeInBackup());
|
||||
keys.addAll(kbsValues().getKeysToIncludeInBackup());
|
||||
keys.addAll(registrationValues().getKeysToIncludeInBackup());
|
||||
keys.addAll(pinValues().getKeysToIncludeInBackup());
|
||||
@@ -121,91 +139,95 @@ public final class SignalStore {
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static void resetCache() {
|
||||
INSTANCE.store.resetCache();
|
||||
getInstance().store.resetCache();
|
||||
}
|
||||
|
||||
public static @NonNull AccountValues account() {
|
||||
return getInstance().accountValues;
|
||||
}
|
||||
|
||||
public static @NonNull KbsValues kbsValues() {
|
||||
return INSTANCE.kbsValues;
|
||||
return getInstance().kbsValues;
|
||||
}
|
||||
|
||||
public static @NonNull RegistrationValues registrationValues() {
|
||||
return INSTANCE.registrationValues;
|
||||
return getInstance().registrationValues;
|
||||
}
|
||||
|
||||
public static @NonNull PinValues pinValues() {
|
||||
return INSTANCE.pinValues;
|
||||
return getInstance().pinValues;
|
||||
}
|
||||
|
||||
public static @NonNull RemoteConfigValues remoteConfigValues() {
|
||||
return INSTANCE.remoteConfigValues;
|
||||
return getInstance().remoteConfigValues;
|
||||
}
|
||||
|
||||
public static @NonNull StorageServiceValues storageService() {
|
||||
return INSTANCE.storageServiceValues;
|
||||
return getInstance().storageServiceValues;
|
||||
}
|
||||
|
||||
public static @NonNull UiHints uiHints() {
|
||||
return INSTANCE.uiHints;
|
||||
return getInstance().uiHints;
|
||||
}
|
||||
|
||||
public static @NonNull TooltipValues tooltips() {
|
||||
return INSTANCE.tooltipValues;
|
||||
return getInstance().tooltipValues;
|
||||
}
|
||||
|
||||
public static @NonNull MiscellaneousValues misc() {
|
||||
return INSTANCE.misc;
|
||||
return getInstance().misc;
|
||||
}
|
||||
|
||||
public static @NonNull InternalValues internalValues() {
|
||||
return INSTANCE.internalValues;
|
||||
return getInstance().internalValues;
|
||||
}
|
||||
|
||||
public static @NonNull EmojiValues emojiValues() {
|
||||
return INSTANCE.emojiValues;
|
||||
return getInstance().emojiValues;
|
||||
}
|
||||
|
||||
public static @NonNull SettingsValues settings() {
|
||||
return INSTANCE.settingsValues;
|
||||
return getInstance().settingsValues;
|
||||
}
|
||||
|
||||
public static @NonNull CertificateValues certificateValues() {
|
||||
return INSTANCE.certificateValues;
|
||||
return getInstance().certificateValues;
|
||||
}
|
||||
|
||||
public static @NonNull PhoneNumberPrivacyValues phoneNumberPrivacy() {
|
||||
return INSTANCE.phoneNumberPrivacyValues;
|
||||
return getInstance().phoneNumberPrivacyValues;
|
||||
}
|
||||
|
||||
public static @NonNull OnboardingValues onboarding() {
|
||||
return INSTANCE.onboardingValues;
|
||||
return getInstance().onboardingValues;
|
||||
}
|
||||
|
||||
public static @NonNull WallpaperValues wallpaper() {
|
||||
return INSTANCE.wallpaperValues;
|
||||
return getInstance().wallpaperValues;
|
||||
}
|
||||
|
||||
public static @NonNull PaymentsValues paymentsValues() {
|
||||
return INSTANCE.paymentsValues;
|
||||
return getInstance().paymentsValues;
|
||||
}
|
||||
|
||||
public static @NonNull DonationsValues donationsValues() {
|
||||
return INSTANCE.donationsValues;
|
||||
return getInstance().donationsValues;
|
||||
}
|
||||
|
||||
public static @NonNull ProxyValues proxy() {
|
||||
return INSTANCE.proxyValues;
|
||||
return getInstance().proxyValues;
|
||||
}
|
||||
|
||||
public static @NonNull RateLimitValues rateLimit() {
|
||||
return INSTANCE.rateLimitValues;
|
||||
return getInstance().rateLimitValues;
|
||||
}
|
||||
|
||||
public static @NonNull ChatColorsValues chatColorsValues() {
|
||||
return INSTANCE.chatColorsValues;
|
||||
return getInstance().chatColorsValues;
|
||||
}
|
||||
|
||||
public static @NonNull ImageEditorValues imageEditorValues() {
|
||||
return INSTANCE.imageEditorValues;
|
||||
return getInstance().imageEditorValues;
|
||||
}
|
||||
|
||||
public static @NonNull GroupsV2AuthorizationSignalStoreCache groupsV2AuthorizationCache() {
|
||||
@@ -225,6 +247,14 @@ public final class SignalStore {
|
||||
}
|
||||
|
||||
private static @NonNull KeyValueStore getStore() {
|
||||
return INSTANCE.store;
|
||||
return getInstance().store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to set a custom KeyValueStore to read from. Only for testing!
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static void inject(@NonNull KeyValueStore store) {
|
||||
instance = new SignalStore(store);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user