diff --git a/app/build.gradle b/app/build.gradle
index 589330fb..88bbc75d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,7 +65,7 @@ android {
applicationId = 'com.loafwallet'
minSdkVersion 31
targetSdkVersion 33
- versionCode 20240424
+ versionCode 20240521
versionName "v2.10.0"
multiDexEnabled true
archivesBaseName = "${versionName}(${versionCode})"
diff --git a/app/src/main/java/com/breadwallet/presenter/activities/PaperKeyActivity.java b/app/src/main/java/com/breadwallet/presenter/activities/PaperKeyActivity.java
index 87d12b0e..918e8b84 100644
--- a/app/src/main/java/com/breadwallet/presenter/activities/PaperKeyActivity.java
+++ b/app/src/main/java/com/breadwallet/presenter/activities/PaperKeyActivity.java
@@ -115,7 +115,7 @@ public void onClick(BRDialogView brDialogView) {
brDialogView.dismissWithAnimation();
}
}, null, null, 0);
- IllegalArgumentException ex = new IllegalArgumentException("Paper Key error, please contact support at contact@loafwallet.org: " + wordArray.length);
+ IllegalArgumentException ex = new IllegalArgumentException("Paper Key error, please contact support at support.litewallet.io: " + wordArray.length);
Timber.e(ex);
throw ex;
} else {
diff --git a/app/src/main/java/com/breadwallet/presenter/activities/settings/AboutActivity.java b/app/src/main/java/com/breadwallet/presenter/activities/settings/AboutActivity.java
index 39abeac5..7df7c604 100644
--- a/app/src/main/java/com/breadwallet/presenter/activities/settings/AboutActivity.java
+++ b/app/src/main/java/com/breadwallet/presenter/activities/settings/AboutActivity.java
@@ -55,6 +55,8 @@ protected void onCreate(Bundle savedInstanceState) {
blogShare = (ImageView) findViewById(R.id.blog_share_button);
versionText.setText(BRConstants.APP_VERSION_NAME_CODE);
+ versionText.setText(BRConstants.APP_VERSION_NAME_CODE);
+
instagramShare.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/app/src/main/java/com/breadwallet/presenter/activities/settings/SettingsActivity.java b/app/src/main/java/com/breadwallet/presenter/activities/settings/SettingsActivity.java
index fba4f2b6..4c8d9d22 100644
--- a/app/src/main/java/com/breadwallet/presenter/activities/settings/SettingsActivity.java
+++ b/app/src/main/java/com/breadwallet/presenter/activities/settings/SettingsActivity.java
@@ -128,14 +128,6 @@ private void populateItems() {
/*Wallet Title*/
items.add(new BRSettingsItem(getString(R.string.Settings_wallet), "", null, true));
- /*Import Title*/
- items.add(new BRSettingsItem(getString(R.string.Settings_importTitle), "", v -> {
- Intent intent = new Intent(SettingsActivity.this, ImportActivity.class);
- startActivity(intent);
- overridePendingTransition(R.anim.enter_from_bottom, R.anim.empty_300);
-
- }, false));
-
/*Show Seed Phrase*/
items.add(new BRSettingsItem(getString(R.string.settings_show_seed), "", v -> {
BRAnimator.showBalanceSeedFragment(this);
diff --git a/app/src/main/java/com/breadwallet/presenter/activities/settings/SyncBlockchainActivity.java b/app/src/main/java/com/breadwallet/presenter/activities/settings/SyncBlockchainActivity.java
index 941f5be6..bcb5b10b 100644
--- a/app/src/main/java/com/breadwallet/presenter/activities/settings/SyncBlockchainActivity.java
+++ b/app/src/main/java/com/breadwallet/presenter/activities/settings/SyncBlockchainActivity.java
@@ -145,6 +145,14 @@ public void onClick(View v) {
}
});
+ closeButton = (ImageButton) findViewById(R.id.close_button);
+ closeButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onBackPressed();
+ }
+ });
+
}
diff --git a/app/src/main/java/com/breadwallet/presenter/fragments/FragmentSend.kt b/app/src/main/java/com/breadwallet/presenter/fragments/FragmentSend.kt
index 081971e7..6cfe35a4 100644
--- a/app/src/main/java/com/breadwallet/presenter/fragments/FragmentSend.kt
+++ b/app/src/main/java/com/breadwallet/presenter/fragments/FragmentSend.kt
@@ -114,6 +114,7 @@ class FragmentSend : Fragment() {
// Hiding until layouts are built.
showKeyboard(false)
signalLayout.layoutTransition = BRAnimator.getDefaultTransition()
+
return rootView
}
diff --git a/app/src/main/java/com/breadwallet/tools/security/KeyStoreManager.java b/app/src/main/java/com/breadwallet/tools/security/KeyStoreManager.java
new file mode 100644
index 00000000..798f980a
--- /dev/null
+++ b/app/src/main/java/com/breadwallet/tools/security/KeyStoreManager.java
@@ -0,0 +1,677 @@
+package com.breadwallet.tools.security;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyPermanentlyInvalidatedException;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.UserNotAuthenticatedException;
+import android.util.Log;
+
+import com.breadwallet.R;
+import com.breadwallet.exceptions.BRKeystoreErrorException;
+import com.breadwallet.presenter.activities.IntroActivity;
+import com.breadwallet.presenter.activities.MainActivity;
+import com.breadwallet.tools.animation.BRAnimator;
+import com.breadwallet.tools.util.ByteReader;
+import com.breadwallet.tools.manager.SharedPreferencesManager;
+import com.breadwallet.tools.util.TypesConverter;
+import com.breadwallet.wallet.BRWalletManager;
+
+import junit.framework.Assert;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.text.Normalizer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.SynchronousQueue;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * BreadWallet
+ *
+ * Created by Mihail Gutan on 9/29/15.
+ * Copyright (c) 2016 breadwallet LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+public class KeyStoreManager {
+ private static final String TAG = KeyStoreManager.class.getName();
+
+ public static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
+ public static final String PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7;
+ public static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
+ public static final String ANDROID_KEY_STORE = "AndroidKeyStore";
+
+ public static Map aliasObjectMap;
+
+ private static final String PHRASE_IV = "ivphrase";
+ private static final String CANARY_IV = "ivcanary";
+ private static final String PUB_KEY_IV = "ivpubkey";
+ private static final String WALLET_CREATION_TIME_IV = "ivtime";
+ private static final String PASS_CODE_IV = "ivpasscode";
+ private static final String FAIL_COUNT_IV = "ivfailcount";
+ private static final String SPENT_LIMIT_IV = "ivspendlimit";
+ private static final String FAIL_TIMESTAMP_IV = "ivfailtimestamp";
+ private static final String AUTH_KEY_IV = "ivauthkey";
+ private static final String TOKEN_IV = "ivtoken";
+ private static final String PASS_TIME_IV = "passtimetoken";
+
+ public static final String PHRASE_ALIAS = "phrase";
+ public static final String CANARY_ALIAS = "canary";
+ public static final String PUB_KEY_ALIAS = "pubKey";
+ public static final String WALLET_CREATION_TIME_ALIAS = "creationTime";
+ public static final String PASS_CODE_ALIAS = "passCode";
+ public static final String FAIL_COUNT_ALIAS = "failCount";
+ public static final String SPEND_LIMIT_ALIAS = "spendlimit";
+ public static final String FAIL_TIMESTAMP_ALIAS = "failTimeStamp";
+ public static final String AUTH_KEY_ALIAS = "authKey";
+ public static final String TOKEN_ALIAS = "token";
+ public static final String PASS_TIME_ALIAS = "passTime";
+
+ private static final String PHRASE_FILENAME = "my_phrase";
+ private static final String CANARY_FILENAME = "my_canary";
+ private static final String PUB_KEY_FILENAME = "my_pub_key";
+ private static final String WALLET_CREATION_TIME_FILENAME = "my_creation_time";
+ private static final String PASS_CODE_FILENAME = "my_pass_code";
+ private static final String FAIL_COUNT_FILENAME = "my_fail_count";
+ private static final String SPEND_LIMIT_FILENAME = "my_spend_limit";
+ private static final String FAIL_TIMESTAMP_FILENAME = "my_fail_timestamp";
+ private static final String AUTH_KEY_FILENAME = "my_auth_key";
+ private static final String TOKEN_FILENAME = "my_token";
+ private static final String PASS_TIME_FILENAME = "my_pass_time";
+
+ public static final int AUTH_DURATION_SEC = 300;
+
+ static {
+ aliasObjectMap = new HashMap<>();
+ aliasObjectMap.put(PHRASE_ALIAS, new AliasObject(PHRASE_ALIAS, PHRASE_FILENAME, PHRASE_IV));
+ aliasObjectMap.put(CANARY_ALIAS, new AliasObject(CANARY_ALIAS, CANARY_FILENAME, CANARY_IV));
+ aliasObjectMap.put(PUB_KEY_ALIAS, new AliasObject(PUB_KEY_ALIAS, PUB_KEY_FILENAME, PUB_KEY_IV));
+ aliasObjectMap.put(WALLET_CREATION_TIME_ALIAS, new AliasObject(WALLET_CREATION_TIME_ALIAS, WALLET_CREATION_TIME_FILENAME, WALLET_CREATION_TIME_IV));
+ aliasObjectMap.put(PASS_CODE_ALIAS, new AliasObject(PASS_CODE_ALIAS, PASS_CODE_FILENAME, PASS_CODE_IV));
+ aliasObjectMap.put(FAIL_COUNT_ALIAS, new AliasObject(FAIL_COUNT_ALIAS, FAIL_COUNT_FILENAME, FAIL_COUNT_IV));
+ aliasObjectMap.put(FAIL_COUNT_ALIAS, new AliasObject(FAIL_COUNT_ALIAS, FAIL_COUNT_FILENAME, FAIL_COUNT_IV));
+ aliasObjectMap.put(SPEND_LIMIT_ALIAS, new AliasObject(SPEND_LIMIT_ALIAS, SPEND_LIMIT_FILENAME, SPENT_LIMIT_IV));
+ aliasObjectMap.put(FAIL_TIMESTAMP_ALIAS, new AliasObject(FAIL_TIMESTAMP_ALIAS, FAIL_TIMESTAMP_FILENAME, FAIL_TIMESTAMP_IV));
+ aliasObjectMap.put(AUTH_KEY_ALIAS, new AliasObject(AUTH_KEY_ALIAS, AUTH_KEY_FILENAME, AUTH_KEY_IV));
+ aliasObjectMap.put(TOKEN_ALIAS, new AliasObject(TOKEN_ALIAS, TOKEN_FILENAME, TOKEN_IV));
+ aliasObjectMap.put(PASS_TIME_ALIAS, new AliasObject(PASS_TIME_ALIAS, PASS_TIME_FILENAME, PASS_TIME_IV));
+
+ Assert.assertEquals(aliasObjectMap.size(), 11);
+ Assert.assertEquals(AUTH_DURATION_SEC, 300);
+ }
+
+ private static android.app.AlertDialog dialog;
+
+ private static boolean _setData(Activity context, byte[] data, String alias, String alias_file, String alias_iv, int request_code, boolean auth_required) {
+ if (alias.equals(alias_file) || alias.equals(alias_iv) || alias_file.equals(alias_iv))
+ throw new IllegalArgumentException("mistake in parameters!");
+ try {
+ KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
+ keyStore.load(null);
+ // Create the keys if necessary
+ if (!keyStore.containsAlias(alias)) {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
+
+ // Set the alias of the entry in Android KeyStore where the key will appear
+ // and the constrains (purposes) in the constructor of the Builder
+ keyGenerator.init(new KeyGenParameterSpec.Builder(alias,
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(BLOCK_MODE)
+ .setKeySize(256)
+ .setUserAuthenticationRequired(auth_required)
+ .setUserAuthenticationValidityDurationSeconds(AUTH_DURATION_SEC)
+ .setRandomizedEncryptionRequired(false)
+ .setEncryptionPaddings(PADDING)
+ .build());
+ SecretKey key = keyGenerator.generateKey();
+
+ }
+
+ String encryptedDataFilePath = getEncryptedDataFilePath(alias_file, context);
+
+ SecretKey secret = (SecretKey) keyStore.getKey(alias, null);
+ if (secret == null) return false;
+ Cipher inCipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ inCipher.init(Cipher.ENCRYPT_MODE, secret);
+ byte[] iv = inCipher.getIV();
+ String path = getEncryptedDataFilePath(alias_iv, context);
+ boolean success = writeBytesToFile(path, iv);
+ if (!success) throw new NullPointerException("FAILED TO WRITE BYTES TO FILE");
+ CipherOutputStream cipherOutputStream = new CipherOutputStream(
+ new FileOutputStream(encryptedDataFilePath), inCipher);
+ cipherOutputStream.write(data);
+ try {
+ cipherOutputStream.close();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ return true;
+ } catch (UserNotAuthenticatedException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ showAuthenticationScreen(context, request_code);
+ } catch (CertificateException | NoSuchAlgorithmException | InvalidKeyException | NullPointerException
+ | NoSuchPaddingException | KeyStoreException | UnrecoverableKeyException |
+ InvalidAlgorithmParameterException | NoSuchProviderException | IOException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ private static byte[] _getData(final Activity context, String alias, String alias_file, String alias_iv, int request_code)
+ throws BRKeystoreErrorException {
+
+ if (alias.equals(alias_file) || alias.equals(alias_iv) || alias_file.equals(alias_iv))
+ throw new RuntimeException("mistake in parameters!");
+ Log.e(TAG, "_getData: " + alias);
+ KeyStore keyStore;
+
+ String encryptedDataFilePath = getEncryptedDataFilePath(alias_file, context);
+ byte[] result = new byte[0];
+ try {
+ keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
+ keyStore.load(null);
+ SecretKey secretKey = (SecretKey)
+ keyStore.getKey(alias, null);
+ if (secretKey == null) {
+ /** no such key, the key is just simply not there */
+ boolean fileExists = new File(encryptedDataFilePath).exists();
+ Log.e(TAG, "_getData: " + alias + " file exist: " + fileExists);
+ if (!fileExists) return result; /** file also not there, fine then */
+ showKeyStoreFailedToLoad(context);
+ throw new BRKeystoreErrorException("no key but the phrase is there");
+ }
+
+ if (!new File(getEncryptedDataFilePath(alias_iv, context)).exists() ||
+ !new File(getEncryptedDataFilePath(alias_file, context)).exists()) {
+ removeAliasAndFiles(alias, context);
+ return result;
+ }
+
+ byte[] iv = readBytesFromFile(getEncryptedDataFilePath(alias_iv, context));
+ Cipher outCipher;
+ outCipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ outCipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
+
+ CipherInputStream cipherInputStream = new CipherInputStream(
+ new FileInputStream(encryptedDataFilePath), outCipher);
+ return ByteReader.readBytesFromStream(cipherInputStream);
+ } catch (InvalidKeyException e) {
+ Log.e(TAG, "_getData: InvalidKeyException");
+ if (e instanceof UserNotAuthenticatedException) {
+ /**user not authenticated, ask the system for authentication*/
+ Log.e(TAG, Log.getStackTraceString(e));
+ showAuthenticationScreen(context, request_code);
+ throw new BRKeystoreErrorException(e.getMessage());
+ } else if (e instanceof KeyPermanentlyInvalidatedException) {
+ showKeyStoreDialog("KeyStore Error", "Your Breadwallet encrypted data was recently invalidated because you " +
+ "disabled your Android lock screen. Please input your phrase to recover your Breadwallet now.", context.getString(R.string.ok), null,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ }, null, new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialogInterface) {
+ if (context instanceof IntroActivity) {
+ if (BRAnimator.checkTheMultipressingAvailability()) {
+ ((IntroActivity) context).showRecoverWalletFragment();
+ }
+ }
+ }
+ });
+ throw new BRKeystoreErrorException("KeyPermanentlyInvalidatedException");
+ } else {
+ Log.e(TAG, "_getData: InvalidKeyException", e);
+ showKeyStoreFailedToLoad(context);
+ throw new BRKeystoreErrorException("Key store error");
+ }
+ } catch (IOException | CertificateException | KeyStoreException e) {
+ /** keyStore.load(null) threw the Exception, meaning the keystore is unavailable */
+ Log.e(TAG, "_getData: keyStore.load(null) threw the Exception, meaning the keystore is unavailable", e);
+ if (e instanceof FileNotFoundException) {
+ Log.e(TAG, "_getData: File not found exception", e);
+ throw new RuntimeException("the key is present but the phrase on the disk no???");
+ } else {
+ showKeyStoreFailedToLoad(context);
+ throw new BRKeystoreErrorException("Failed to load KeyStore");
+ }
+
+ } catch (UnrecoverableKeyException | InvalidAlgorithmParameterException | NoSuchAlgorithmException | NoSuchPaddingException e) {
+ /** if for any other reason the keystore fails, crash! */
+ Log.e(TAG, "getData: error: " + e.getClass().getSuperclass().getName());
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ private static String getEncryptedDataFilePath(String fileName, Context context) {
+ String filesDirectory = context.getFilesDir().getAbsolutePath();
+ return filesDirectory + File.separator + fileName;
+ }
+
+ private static void showKeyStoreFailedToLoad(final Activity context) {
+ showKeyStoreDialog("KeyStore Error", "Failed to load KeyStore. Please try again later or enter your phrase to recover your Breadwallet now.", "recover now", "try later",
+ context instanceof IntroActivity ?
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (BRAnimator.checkTheMultipressingAvailability()) {
+ ((IntroActivity) context).showRecoverWalletFragment();
+ }
+ }
+ } : null, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ context.finish();
+ }
+ },
+ null);
+ }
+
+ public static boolean putKeyStorePhrase(byte[] strToStore, Activity context, int requestCode) {
+ AliasObject obj = aliasObjectMap.get(PHRASE_ALIAS);
+ return !(strToStore == null || strToStore.length == 0) && _setData(context, strToStore, obj.alias, obj.datafileName, obj.ivFileName, requestCode, true);
+ }
+
+ public static byte[] getKeyStorePhrase(final Activity context, int requestCode)
+ throws BRKeystoreErrorException {
+ AliasObject obj = aliasObjectMap.get(PHRASE_ALIAS);
+ return _getData(context, obj.alias, obj.datafileName, obj.ivFileName, requestCode);
+ }
+
+ public static boolean putKeyStoreCanary(String strToStore, Activity context, int requestCode) {
+ if (strToStore == null || strToStore.isEmpty()) return false;
+ AliasObject obj = aliasObjectMap.get(CANARY_ALIAS);
+ byte[] strBytes = new byte[0];
+ try {
+ strBytes = strToStore.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ return strBytes.length != 0 && _setData(context, strBytes, obj.alias, obj.datafileName, obj.ivFileName, requestCode, true);
+ }
+
+ public static String getKeyStoreCanary(final Activity context, int requestCode)
+ throws BRKeystoreErrorException {
+ AliasObject obj = aliasObjectMap.get(CANARY_ALIAS);
+ byte[] data = _getData(context, obj.alias, obj.datafileName, obj.ivFileName, requestCode);
+ String result = null;
+ try {
+ result = new String(data, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ return result;
+ }
+
+ public static boolean putMasterPublicKey(byte[] masterPubKey, Activity context) {
+ AliasObject obj = aliasObjectMap.get(PUB_KEY_ALIAS);
+ return masterPubKey != null && masterPubKey.length != 0 && _setData(context, masterPubKey, obj.alias, obj.datafileName, obj.ivFileName, 0, false);
+ }
+
+ public static byte[] getMasterPublicKey(final Activity context) {
+ byte[] result = new byte[0];
+ AliasObject obj = aliasObjectMap.get(PUB_KEY_ALIAS);
+ try {
+ result = _getData(context, obj.alias, obj.datafileName, obj.ivFileName, 0);
+ } catch (BRKeystoreErrorException e) {
+ e.printStackTrace();
+ }
+ return result;
+ }
+
+ public static boolean putAuthKey(byte[] authKey, Activity context) {
+ AliasObject obj = aliasObjectMap.get(AUTH_KEY_ALIAS);
+ return authKey != null && authKey.length != 0 && _setData(context, authKey, obj.alias, obj.datafileName, obj.ivFileName, 0, false);
+ }
+
+ public static byte[] getAuthKey(final Activity context) {
+ AliasObject obj = aliasObjectMap.get(AUTH_KEY_ALIAS);
+ byte[] result = new byte[0];
+ try {
+ result = _getData(context, obj.alias, obj.datafileName, obj.ivFileName, 0);
+ } catch (BRKeystoreErrorException e) {
+ e.printStackTrace();
+ }
+ return result;
+ }
+
+ public static boolean putToken(byte[] token, Activity context) {
+ AliasObject obj = aliasObjectMap.get(TOKEN_ALIAS);
+ return token != null && token.length != 0 && _setData(context, token, obj.alias, obj.datafileName, obj.ivFileName, 0, false);
+ }
+
+ public static byte[] getToken(final Activity context) {
+ AliasObject obj = aliasObjectMap.get(TOKEN_ALIAS);
+ byte[] result = new byte[0];
+ try {
+ result = _getData(context, obj.alias, obj.datafileName, obj.ivFileName, 0);
+ } catch (BRKeystoreErrorException e) {
+ e.printStackTrace();
+ }
+ return result;
+ }
+
+ public static boolean putWalletCreationTime(int creationTime, Activity context) {
+ Log.e(TAG, "putWalletCreationTime: " + creationTime);
+ AliasObject obj = aliasObjectMap.get(WALLET_CREATION_TIME_ALIAS);
+ byte[] bytesToStore = TypesConverter.intToBytes(creationTime);
+ return bytesToStore.length != 0 && _setData(context, bytesToStore, obj.alias, obj.datafileName, obj.ivFileName, 0, false);
+ }
+
+ public static int getWalletCreationTime(final Activity context) {
+ AliasObject obj = aliasObjectMap.get(WALLET_CREATION_TIME_ALIAS);
+ byte[] result = new byte[0];
+ try {
+ result = _getData(context, obj.alias, obj.datafileName, obj.ivFileName, 0);
+ } catch (BRKeystoreErrorException e) {
+ e.printStackTrace();
+ }
+ return result.length > 0 ? TypesConverter.bytesToInt(result) : 0;
+ }
+
+ public static boolean putPassCode(String passcode, Activity context) {
+ AliasObject obj = aliasObjectMap.get(PASS_CODE_ALIAS);
+ byte[] bytesToStore = passcode.getBytes();
+ return _setData(context, bytesToStore, obj.alias, obj.datafileName, obj.ivFileName, 0, false);
+ }
+
+ public static String getPassCode(final Activity context) {
+ AliasObject obj = aliasObjectMap.get(PASS_CODE_ALIAS);
+ byte[] result = new byte[0];
+ try {
+ result = _getData(context, obj.alias, obj.datafileName, obj.ivFileName, 0);
+ } catch (BRKeystoreErrorException e) {
+ e.printStackTrace();
+ }
+ String passCode = new String(result);
+ try {
+ int test = Integer.parseInt(passCode);
+ } catch (Exception e) {
+ Log.e(TAG, "getPassCode: " + e.getMessage());
+ passCode = "";
+ putPassCode(passCode, context);
+ KeyStoreManager.putFailCount(0, context);
+ KeyStoreManager.putFailTimeStamp(0, context);
+ return passCode;
+ }
+ if (passCode.length() != 4) {
+ passCode = "";
+ putPassCode(passCode, context);
+ }
+ return passCode;
+ }
+
+ public static boolean putFailCount(int failCount, Activity context) {
+ AliasObject obj = aliasObjectMap.get(FAIL_COUNT_ALIAS);
+ if (failCount >= 3) {
+ long time = SharedPreferencesManager.getSecureTime(context);
+ putFailTimeStamp(time, context);
+ }
+ byte[] bytesToStore = TypesConverter.intToBytes(failCount);
+ return bytesToStore.length != 0 && _setData(context, bytesToStore, obj.alias, obj.datafileName, obj.ivFileName, 0, false);
+ }
+
+ public static int getFailCount(final Activity context) {
+ AliasObject obj = aliasObjectMap.get(FAIL_COUNT_ALIAS);
+ byte[] result = new byte[0];
+ try {
+ result = _getData(context, obj.alias, obj.datafileName, obj.ivFileName, 0);
+ } catch (BRKeystoreErrorException e) {
+ e.printStackTrace();
+ }
+
+ return result.length > 0 ? TypesConverter.bytesToInt(result) : 0;
+ }
+
+ public static boolean putSpendLimit(long spendLimit, Activity context) {
+ AliasObject obj = aliasObjectMap.get(SPEND_LIMIT_ALIAS);
+ byte[] bytesToStore = TypesConverter.long2byteArray(spendLimit);
+ return bytesToStore.length != 0 && _setData(context, bytesToStore, obj.alias, obj.datafileName, obj.ivFileName, 0, false);
+ }
+
+ public static long getSpendLimit(final Activity context) {
+ AliasObject obj = aliasObjectMap.get(SPEND_LIMIT_ALIAS);
+ byte[] result = new byte[0];
+ try {
+ result = _getData(context, obj.alias, obj.datafileName, obj.ivFileName, 0);
+ } catch (BRKeystoreErrorException e) {
+ e.printStackTrace();
+ }
+
+ return result.length > 0 ? TypesConverter.byteArray2long(result) : 0;
+ }
+
+ public static boolean putFailTimeStamp(long spendLimit, Activity context) {
+ AliasObject obj = aliasObjectMap.get(FAIL_TIMESTAMP_ALIAS);
+ byte[] bytesToStore = TypesConverter.long2byteArray(spendLimit);
+ return bytesToStore.length != 0 && _setData(context, bytesToStore, obj.alias, obj.datafileName, obj.ivFileName, 0, false);
+ }
+
+ public static long getFailTimeStamp(final Activity context) {
+ AliasObject obj = aliasObjectMap.get(FAIL_TIMESTAMP_ALIAS);
+ byte[] result = new byte[0];
+ try {
+ result = _getData(context, obj.alias, obj.datafileName, obj.ivFileName, 0);
+ } catch (BRKeystoreErrorException e) {
+ e.printStackTrace();
+ }
+
+ return result.length > 0 ? TypesConverter.byteArray2long(result) : 0;
+ }
+
+ public static boolean putLastPasscodeUsedTime(long time, Activity context) {
+ AliasObject obj = aliasObjectMap.get(PASS_TIME_ALIAS);
+ byte[] bytesToStore = TypesConverter.long2byteArray(time);
+ return bytesToStore.length != 0 && _setData(context, bytesToStore, obj.alias, obj.datafileName, obj.ivFileName, 0, false);
+ }
+
+ public static long getLastPasscodeUsedTime(final Activity context) {
+ AliasObject obj = aliasObjectMap.get(PASS_TIME_ALIAS);
+ byte[] result = new byte[0];
+ try {
+ result = _getData(context, obj.alias, obj.datafileName, obj.ivFileName, 0);
+ } catch (BRKeystoreErrorException e) {
+ e.printStackTrace();
+ }
+ return result.length > 0 ? TypesConverter.byteArray2long(result) : 0;
+ }
+
+ public static boolean phraseIsValid(String insertedPhrase, Activity activity) {
+ String normalizedPhrase = Normalizer.normalize(insertedPhrase.trim(), Normalizer.Form.NFKD);
+ if (!BRWalletManager.getInstance(activity).validatePhrase(activity, normalizedPhrase))
+ return false;
+ BRWalletManager m = BRWalletManager.getInstance(activity);
+ byte[] rawPhrase = normalizedPhrase.getBytes();
+ byte[] bytePhrase = TypesConverter.getNullTerminatedPhrase(rawPhrase);
+ byte[] pubKey = m.getMasterPubKey(bytePhrase);
+ byte[] pubKeyFromKeyStore = KeyStoreManager.getMasterPublicKey(activity);
+ Arrays.fill(bytePhrase, (byte) 0);
+ return Arrays.equals(pubKey, pubKeyFromKeyStore);
+ }
+
+ public static boolean resetWalletKeyStore(Context context) {
+ KeyStore keyStore;
+ try {
+ keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
+ keyStore.load(null);
+ int count = 0;
+ for (String a : aliasObjectMap.keySet()) {
+ removeAliasAndFiles(a, context);
+ count++;
+ }
+ Assert.assertEquals(count, 11);
+
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ return false;
+ } catch (KeyStoreException e) {
+ e.printStackTrace();
+ return false;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ } catch (java.security.cert.CertificateException e) {
+ e.printStackTrace();
+ }
+ return true;
+ }
+
+ public static void removeAliasAndFiles(String alias, Context context) {
+ KeyStore keyStore;
+ try {
+ boolean b1 = new File(getEncryptedDataFilePath(aliasObjectMap.get(alias).datafileName, context)).delete();
+ boolean b2 = new File(getEncryptedDataFilePath(aliasObjectMap.get(alias).ivFileName, context)).delete();
+ keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
+ keyStore.load(null);
+ keyStore.deleteEntry(alias);
+ } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
+ e.printStackTrace();
+ }
+
+
+ }
+
+ public static void showAuthenticationScreen(Activity context, int requestCode) {
+ // Create the Confirm Credentials screen. You can customize the title and description. Or
+ // we will provide a generic one for you if you leave it null
+ Log.e(TAG, "showAuthenticationScreen: " + requestCode);
+ KeyguardManager mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+ Intent intent = mKeyguardManager.createConfirmDeviceCredentialIntent(context.getString(R.string.auth_required), context.getString(R.string.auth_message));
+ if (intent != null) {
+ context.startActivityForResult(intent, requestCode);
+ } else {
+ throw new NullPointerException("no passcode is set");
+ }
+ }
+
+ public static byte[] readBytesFromFile(String path) {
+ byte[] bytes = null;
+ try {
+ File file = new File(path);
+ FileInputStream fin = new FileInputStream(file);
+ bytes = ByteReader.readBytesFromStream(fin);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return bytes;
+ }
+
+ public static boolean writeBytesToFile(String path, byte[] data) {
+
+ FileOutputStream fos = null;
+
+ try {
+ File file = new File(path);
+ fos = new FileOutputStream(file);
+
+ // Writes bytes from the specified byte array to this file output stream
+ fos.write(data);
+ return true;
+ } catch (FileNotFoundException e) {
+ System.out.println("File not found" + e);
+ } catch (IOException ioe) {
+ System.out.println("Exception while writing file " + ioe);
+ } finally {
+ // close the streams using close method
+ try {
+ if (fos != null) {
+ fos.close();
+ }
+ } catch (IOException ioe) {
+ System.out.println("Error while closing stream: " + ioe);
+ }
+
+ }
+ return false;
+ }
+
+ private static void showKeyStoreDialog(final String title, final String message, final String posButton, final String negButton,
+ final DialogInterface.OnClickListener posButtonListener,
+ final DialogInterface.OnClickListener negButtonListener,
+ final DialogInterface.OnDismissListener dismissListener) {
+ Log.e(TAG, "showKeyStoreDialog");
+ Activity app = MainActivity.app;
+ if (app == null) app = IntroActivity.app;
+ if (app == null) {
+ Log.e(TAG, "showCustomDialog: FAILED, context is null");
+ return;
+ }
+ final Activity finalApp = app;
+ finalApp.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (dialog != null && dialog.isShowing()) {
+ System.out.println("some");
+ if (dialog.getOwnerActivity() != null && !dialog.getOwnerActivity().isDestroyed())
+ dialog.dismiss();
+ else
+ return;
+ }
+ dialog = new android.app.AlertDialog.Builder(finalApp).
+ setTitle(title)
+ .setMessage(message)
+ .setPositiveButton(posButton, posButtonListener)
+ .setNegativeButton(negButton, negButtonListener)
+ .setOnDismissListener(dismissListener)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .show();
+ }
+ });
+ }
+
+ private static class AliasObject {
+ String alias;
+ String datafileName;
+ String ivFileName;
+
+ AliasObject(String alias, String datafileName, String ivFileName) {
+ this.alias = alias;
+ this.datafileName = datafileName;
+ this.ivFileName = ivFileName;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/breadwallet/tools/util/BRConstants.java b/app/src/main/java/com/breadwallet/tools/util/BRConstants.java
index fb8b52eb..2b05efe1 100644
--- a/app/src/main/java/com/breadwallet/tools/util/BRConstants.java
+++ b/app/src/main/java/com/breadwallet/tools/util/BRConstants.java
@@ -30,6 +30,7 @@ private BRConstants() {
* Permissions
*/
public static final int CAMERA_REQUEST_ID = 34;
+ public static final int GEO_REQUEST_ID = 35;
/**
* Request codes for auth
@@ -102,7 +103,7 @@ private BRConstants() {
public static final String WEB_LINK = "https://litewallet.io";
public static final String TOS_LINK = "https://litewallet.io/privacy";
- public static String CUSTOMER_SUPPORT_LINK = "https://support.litewallet.io";
+ public static String CUSTOMER_SUPPORT_LINK = "https://support.litewallet.io/hc/en-us/requests/new";
public static String BITREFILL_AFFILIATE_LINK = "https://www.bitrefill.com/";
/**
diff --git a/app/src/main/java/com/breadwallet/tools/util/Utils.java b/app/src/main/java/com/breadwallet/tools/util/Utils.java
index 38f2c6b2..4018fb75 100644
--- a/app/src/main/java/com/breadwallet/tools/util/Utils.java
+++ b/app/src/main/java/com/breadwallet/tools/util/Utils.java
@@ -285,7 +285,7 @@ else if (name == PartnerNames.OPSALL) {
AnalyticsManager.logCustomEventWithParams(BRConstants._20200112_ERR,params);
return "";
}
- /// Description: 1713522152
+ /// Description: 1715876807
public static long tieredOpsFee(Context app, long sendAmount) {
double sendAmountDouble = new Double(String.valueOf(sendAmount));
@@ -293,13 +293,17 @@ public static long tieredOpsFee(Context app, long sendAmount) {
CurrencyEntity currency = CurrencyDataSource.getInstance(app).getCurrencyByIso(usIso);
double doubleRate = currency.rate;
double usdInLTC = sendAmountDouble * doubleRate / 100_000_000.0;
+ usdInLTC = Math.floor(usdInLTC * 100) / 100;
- if (isBetween(usdInLTC, 0.00, 20.00)) {
- return (long) ((0.20 / doubleRate) * 100_000_000.0);
+ if (isBetween(usdInLTC, 0.00, 20.00)) {
+ double lowRate = usdInLTC * 0.01;
+ return (long) ((lowRate / doubleRate) * 100_000_000.0);
}
else if (isBetween(usdInLTC, 20.00, 50.00)) {
- return (long) ((0.25 / doubleRate) * 100_000_000.0);
- }
+ Timber.d("timber: usdInLTC 2: %s", usdInLTC);
+
+ return (long) ((0.30 / doubleRate) * 100_000_000.0);
+ }
else if (isBetween(usdInLTC, 50.00, 100.00)) {
return (long) ((1.00 / doubleRate) * 100_000_000.0);
}
diff --git a/app/src/main/jni/transition/PeerManager.c b/app/src/main/jni/transition/PeerManager.c
index 79117cef..f512cd9e 100644
--- a/app/src/main/jni/transition/PeerManager.c
+++ b/app/src/main/jni/transition/PeerManager.c
@@ -277,11 +277,6 @@ Java_com_breadwallet_wallet_BRPeerManager_create(JNIEnv *env, jobject thiz,
__android_log_print(ANDROID_LOG_DEBUG, "Message from C: ", "earliestKeyTime: %d",
earliestKeyTime);
_peerManager = BRPeerManagerNew(&BR_CHAIN_PARAMS, _wallet, (uint32_t) earliestKeyTime, _blocks,
- (size_t) blocksCount,
- _peers, (size_t) peersCount, (double) fpRate);
- BRPeerManagerSetCallbacks(_peerManager, NULL, syncStarted, syncStopped,
- txStatusUpdate,
- saveBlocks, savePeers, networkIsReachable, threadCleanup);
}
if (_peerManager == NULL) {
diff --git a/app/src/main/res/layout/button_buy_bitcoin.xml b/app/src/main/res/layout/button_buy_bitcoin.xml
new file mode 100644
index 00000000..8f2f02f3
--- /dev/null
+++ b/app/src/main/res/layout/button_buy_bitcoin.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index a2efd85a..daa2552a 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -33,7 +33,7 @@
Fehler
- Es liegt ein Problem mit dem KeyStore Ihres Android-Betriebssystems vor, bitte wenden Sie sich an contact@loafwallet.org
+ Es liegt ein Problem mit dem KeyStore Ihres Android-Betriebssystems vor, bitte wenden Sie sich an support.litewallet.io
Ihre mit Loaf verschlüsselten Daten wurden vor Kurzem für ungültig erklärt, da Ihre Android-Bildschirmsperre deaktiviert war.
@@ -197,7 +197,7 @@
Wallet verwalten
- Litecoin kaufen
+ Kaufen
Wallet sperren
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index b01411b9..8134a833 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -33,7 +33,7 @@
Error
- Hay un problema con el almacén de claves de tu sistema operativo Android. Ponte en contacto con contact@loafwallet.org.
+ Hay un problema con el almacén de claves de tu sistema operativo Android. Ponte en contacto con support.litewallet.io.
Tus datos codificados en Loaf fueron anulados recientemente debido a que no estaba habilitada tu pantalla de bloqueo de Android.
@@ -196,7 +196,7 @@
Gestionar cartera
- Comprar Litecoins
+ Comprar
Bloquear cartera
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index c49b5218..f17bf6ee 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -33,7 +33,7 @@
Erreur
- Un problème est survenu avec votre keystore Android OS, veuillez contacter contact@loafwallet.org
+ Un problème est survenu avec votre keystore Android OS, veuillez contacter support.litewallet.io
Vos données cryptées de Loaf ont été récemment invalidées, car le verrouillage de l\'écran de votre appareil Android a été désactivé.
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 35193f7c..89b08a24 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -33,7 +33,7 @@
Errore
- C’è un problema con il portachiavi del tuo sistema operativo Android, per favore, contatta contact@loafwallet.org
+ C’è un problema con il portachiavi del tuo sistema operativo Android, per favore, contatta support.litewallet.io
I tuoi dati criptati di Loaf sono stati invalidati recentemente perché la schermata di blocco del tuo Android era disabilitata.
@@ -197,7 +197,7 @@
Gestisci Portafoglio
- Acquista Litecoin
+ Acquista
Blocca Portafoglio
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 3c450028..9693f557 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -33,7 +33,7 @@
エラー
- Android OSのKeystoreに問題があります。contact@loafwallet.org にお問い合わせください。
+ Android OSのKeystoreに問題があります。support.litewallet.io にお問い合わせください。
お使いのAndroidロック画面が無効にされたため、暗号化されたブレッドのデータはつい最近無効になりました。
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index f6e4e73b..7c424729 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -33,7 +33,7 @@
오류
- 귀하의 안드로이드 OS 키스토어에 문제가 있습니다, contact@loafwallet.org로 연락해 주세요
+ 귀하의 안드로이드 OS 키스토어에 문제가 있습니다, support.litewallet.io로 연락해 주세요
귀하의 안드로이드 잠금 화면이 꺼져서 귀하의 암호화 된 데이터가 최근 무효화 되었습니다.
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 088a4c22..d93dde82 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -33,7 +33,7 @@
Erro
- Há um problema com a KeyStore do seu SO Android. Por favor, contacte contact@loafwallet.org
+ Há um problema com a KeyStore do seu SO Android. Por favor, contacte support.litewallet.io
Os seus dados encriptados pelo Loaf foram recentemente invalidados porque o seu ecrã de bloqueio do Android estava desativado.
@@ -197,7 +197,7 @@
Gerir Carteira
- Comprar Litecoins
+ Comprar
Bloquear Carteira
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index f7edf7ea..84f67968 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -33,7 +33,7 @@
Ошибка
- Имеется проблема с хранилищем ключей в вашей ОС Android, обратитесь по адресу: contact@loafwallet.org
+ Имеется проблема с хранилищем ключей в вашей ОС Android, обратитесь по адресу: support.litewallet.io
Ваши зашифрованные данные приложения Loaf были недавно аннулированы, так как была отключена блокировка экрана вашего устройства Android.
@@ -197,7 +197,7 @@
Управление кошельком
- Купить Litecoin
+ Купить
Заблокировать кошелек
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index c05a5cf0..010834f9 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -709,7 +709,7 @@
- "• Купуйте LTC багатьма фіатними парами\n• Сплачуйте кількома способами\n• Глобальний постачальник платежів"
+ "• Купуйте LTC багатьма фіатними парами\n• Сплачуйте кількома способами\n• Глобальний постачальник платежів"
Історія
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 9599fa54..61eb5b89 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -33,7 +33,7 @@
错误
- 您的安卓操作系统密钥库存在问题,请联系contact@loafwallet.org
+ 您的安卓操作系统密钥库存在问题,请联系support.litewallet.io
您的 Loaf 加密数据最近因为您的安卓系统屏幕锁定被禁用而失效。
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index de0f218c..0c5548d7 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -33,7 +33,7 @@
錯誤
- 您的 Android OS 金鑰儲存區有問題,請聯絡 contact@loafwallet.org
+ 您的 Android OS 金鑰儲存區有問題,請聯絡 support.litewallet.io
您的 Loaf 加密資料最近已被判定為無效,因爲您的 Android 螢幕鎖定已被停用。
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e9a11854..3353817e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -30,7 +30,7 @@
Error
- There is a problem with your Android OS keystore, please contact litewallet@litecoinfoundation.net
+ There is a problem with your Android OS keystore, please contact support.litewallet.io
Your Litewallet encrypted data was recently invalidated because your Android lock screen was disabled.