Migrate from password manager to key chain (#36)
* Update gradle Change .gitignore & clean project * Change password manager * Change password managerpull/2/head
parent
71f0f79fb9
commit
3402342531
@ -1,22 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project version="4"> |
|
||||||
<component name="CompilerConfiguration"> |
|
||||||
<resourceExtensions /> |
|
||||||
<wildcardResourcePatterns> |
|
||||||
<entry name="!?*.java" /> |
|
||||||
<entry name="!?*.form" /> |
|
||||||
<entry name="!?*.class" /> |
|
||||||
<entry name="!?*.groovy" /> |
|
||||||
<entry name="!?*.scala" /> |
|
||||||
<entry name="!?*.flex" /> |
|
||||||
<entry name="!?*.kt" /> |
|
||||||
<entry name="!?*.clj" /> |
|
||||||
<entry name="!?*.aj" /> |
|
||||||
</wildcardResourcePatterns> |
|
||||||
<annotationProcessing> |
|
||||||
<profile default="true" name="Default" enabled="false"> |
|
||||||
<processorPath useClasspath="true" /> |
|
||||||
</profile> |
|
||||||
</annotationProcessing> |
|
||||||
</component> |
|
||||||
</project> |
|
@ -1,3 +0,0 @@ |
|||||||
<component name="CopyrightManager"> |
|
||||||
<settings default="" /> |
|
||||||
</component> |
|
@ -1,19 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project version="4"> |
|
||||||
<component name="GradleSettings"> |
|
||||||
<option name="linkedExternalProjectsSettings"> |
|
||||||
<GradleProjectSettings> |
|
||||||
<option name="distributionType" value="DEFAULT_WRAPPED" /> |
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" /> |
|
||||||
<option name="modules"> |
|
||||||
<set> |
|
||||||
<option value="$PROJECT_DIR$" /> |
|
||||||
<option value="$PROJECT_DIR$/app" /> |
|
||||||
<option value="$PROJECT_DIR$/tn" /> |
|
||||||
</set> |
|
||||||
</option> |
|
||||||
<option name="resolveModulePerSourceSet" value="false" /> |
|
||||||
</GradleProjectSettings> |
|
||||||
</option> |
|
||||||
</component> |
|
||||||
</project> |
|
@ -1,36 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project version="4"> |
|
||||||
<component name="NullableNotNullManager"> |
|
||||||
<option name="myDefaultNullable" value="android.support.annotation.Nullable" /> |
|
||||||
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" /> |
|
||||||
<option name="myNullables"> |
|
||||||
<value> |
|
||||||
<list size="4"> |
|
||||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" /> |
|
||||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" /> |
|
||||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" /> |
|
||||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" /> |
|
||||||
</list> |
|
||||||
</value> |
|
||||||
</option> |
|
||||||
<option name="myNotNulls"> |
|
||||||
<value> |
|
||||||
<list size="4"> |
|
||||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" /> |
|
||||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" /> |
|
||||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" /> |
|
||||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" /> |
|
||||||
</list> |
|
||||||
</value> |
|
||||||
</option> |
|
||||||
</component> |
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> |
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" /> |
|
||||||
</component> |
|
||||||
<component name="ProjectType"> |
|
||||||
<option name="id" value="Android" /> |
|
||||||
</component> |
|
||||||
<component name="SvnBranchConfigurationManager"> |
|
||||||
<option name="mySupportsUserInfoFilter" value="true" /> |
|
||||||
</component> |
|
||||||
</project> |
|
@ -1,10 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project version="4"> |
|
||||||
<component name="ProjectModuleManager"> |
|
||||||
<modules> |
|
||||||
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" /> |
|
||||||
<module fileurl="file://$PROJECT_DIR$/tn/tn.iml" filepath="$PROJECT_DIR$/tn/tn.iml" /> |
|
||||||
<module fileurl="file://$PROJECT_DIR$/trust-wallet-android.iml" filepath="$PROJECT_DIR$/trust-wallet-android.iml" /> |
|
||||||
</modules> |
|
||||||
</component> |
|
||||||
</project> |
|
@ -1,12 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project version="4"> |
|
||||||
<component name="RunConfigurationProducerService"> |
|
||||||
<option name="ignoredProducers"> |
|
||||||
<set> |
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" /> |
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" /> |
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" /> |
|
||||||
</set> |
|
||||||
</option> |
|
||||||
</component> |
|
||||||
</project> |
|
@ -1,7 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project version="4"> |
|
||||||
<component name="VcsDirectoryMappings"> |
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" /> |
|
||||||
<mapping directory="$PROJECT_DIR$/app" vcs="svn" /> |
|
||||||
</component> |
|
||||||
</project> |
|
@ -0,0 +1,30 @@ |
|||||||
|
package com.wallet.crypto.trustapp.controller; |
||||||
|
|
||||||
|
import android.support.annotation.Nullable; |
||||||
|
|
||||||
|
public class ServiceErrorException extends Exception { |
||||||
|
|
||||||
|
public static final int INVALID_DATA = 1; |
||||||
|
public static final int KEY_STORE_ERROR = 1001; |
||||||
|
public static final int FAIL_TO_SAVE_IV_FILE = 1002; |
||||||
|
public static final int KEY_STORE_SECRET = 1003; |
||||||
|
public static final int USER_NOT_AUTHENTICATED = 1004; |
||||||
|
public static final int KEY_IS_GONE = 1005; |
||||||
|
public static final int IV_OR_ALIAS_NO_ON_DISK = 1006; |
||||||
|
public static final int INVALID_KEY = 1007; |
||||||
|
|
||||||
|
public final int code; |
||||||
|
|
||||||
|
public ServiceErrorException(int code, @Nullable String message, Throwable throwable) { |
||||||
|
super(message, throwable); |
||||||
|
this.code = code; |
||||||
|
} |
||||||
|
|
||||||
|
public ServiceErrorException(int code, @Nullable String message) { |
||||||
|
this(code, message, null); |
||||||
|
} |
||||||
|
|
||||||
|
public ServiceErrorException(int code) { |
||||||
|
this(code, null); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,308 @@ |
|||||||
|
package com.wallet.crypto.trustapp.util; |
||||||
|
|
||||||
|
import android.app.Activity; |
||||||
|
import android.app.KeyguardManager; |
||||||
|
import android.content.Context; |
||||||
|
import android.content.Intent; |
||||||
|
import android.security.keystore.KeyGenParameterSpec; |
||||||
|
import android.security.keystore.KeyProperties; |
||||||
|
import android.security.keystore.UserNotAuthenticatedException; |
||||||
|
import android.util.Log; |
||||||
|
|
||||||
|
import com.wallet.crypto.trustapp.R; |
||||||
|
import com.wallet.crypto.trustapp.controller.ServiceErrorException; |
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream; |
||||||
|
import java.io.File; |
||||||
|
import java.io.FileInputStream; |
||||||
|
import java.io.FileNotFoundException; |
||||||
|
import java.io.FileOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.security.InvalidAlgorithmParameterException; |
||||||
|
import java.security.InvalidKeyException; |
||||||
|
import java.security.KeyStore; |
||||||
|
import java.security.KeyStoreException; |
||||||
|
import java.security.NoSuchAlgorithmException; |
||||||
|
import java.security.UnrecoverableKeyException; |
||||||
|
import java.security.cert.CertificateException; |
||||||
|
|
||||||
|
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; |
||||||
|
|
||||||
|
import static com.wallet.crypto.trustapp.controller.ServiceErrorException.INVALID_KEY; |
||||||
|
import static com.wallet.crypto.trustapp.controller.ServiceErrorException.IV_OR_ALIAS_NO_ON_DISK; |
||||||
|
import static com.wallet.crypto.trustapp.controller.ServiceErrorException.KEY_IS_GONE; |
||||||
|
import static com.wallet.crypto.trustapp.controller.ServiceErrorException.KEY_STORE_ERROR; |
||||||
|
import static com.wallet.crypto.trustapp.controller.ServiceErrorException.USER_NOT_AUTHENTICATED; |
||||||
|
|
||||||
|
public class KS { |
||||||
|
private static final String TAG = "KS"; |
||||||
|
|
||||||
|
private static final String ANDROID_KEY_STORE = "AndroidKeyStore"; |
||||||
|
private static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC; |
||||||
|
private static final int AUTH_DURATION_SEC = 600; |
||||||
|
private static final String PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7; |
||||||
|
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding"; |
||||||
|
|
||||||
|
private synchronized static boolean setData( |
||||||
|
Context context, |
||||||
|
byte[] data, |
||||||
|
String alias, |
||||||
|
String aliasFile, |
||||||
|
String aliasIV) throws ServiceErrorException { |
||||||
|
if (data == null) { |
||||||
|
throw new ServiceErrorException( |
||||||
|
ServiceErrorException.INVALID_DATA, "keystore insert data is null"); |
||||||
|
} |
||||||
|
KeyStore keyStore; |
||||||
|
try { |
||||||
|
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(true) |
||||||
|
.setUserAuthenticationValidityDurationSeconds(AUTH_DURATION_SEC) |
||||||
|
.setRandomizedEncryptionRequired(true) |
||||||
|
.setEncryptionPaddings(PADDING) |
||||||
|
.build()); |
||||||
|
keyGenerator.generateKey(); |
||||||
|
} |
||||||
|
String encryptedDataFilePath = getFilePath(context, aliasFile); |
||||||
|
SecretKey secret = (SecretKey) keyStore.getKey(alias, null); |
||||||
|
if (secret == null) { |
||||||
|
throw new ServiceErrorException( |
||||||
|
ServiceErrorException.KEY_STORE_SECRET, |
||||||
|
"secret is null on setData: " + alias); |
||||||
|
} |
||||||
|
Cipher inCipher = Cipher.getInstance(CIPHER_ALGORITHM); |
||||||
|
inCipher.init(Cipher.ENCRYPT_MODE, secret); |
||||||
|
byte[] iv = inCipher.getIV(); |
||||||
|
String path = getFilePath(context, aliasIV); |
||||||
|
boolean success = writeBytesToFile(path, iv); |
||||||
|
if (!success) { |
||||||
|
keyStore.deleteEntry(alias); |
||||||
|
throw new ServiceErrorException( |
||||||
|
ServiceErrorException.FAIL_TO_SAVE_IV_FILE, |
||||||
|
"Failed to save the iv file for: " + alias); |
||||||
|
} |
||||||
|
CipherOutputStream cipherOutputStream = null; |
||||||
|
try { |
||||||
|
cipherOutputStream = new CipherOutputStream( |
||||||
|
new FileOutputStream(encryptedDataFilePath), |
||||||
|
inCipher); |
||||||
|
cipherOutputStream.write(data); |
||||||
|
} catch (Exception ex) { |
||||||
|
throw new ServiceErrorException( |
||||||
|
ServiceErrorException.KEY_STORE_ERROR, |
||||||
|
"Failed to save the file for: " + alias); |
||||||
|
} finally { |
||||||
|
if (cipherOutputStream != null) { |
||||||
|
cipherOutputStream.close(); |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} catch (UserNotAuthenticatedException e) { |
||||||
|
throw new ServiceErrorException(USER_NOT_AUTHENTICATED); |
||||||
|
} catch (ServiceErrorException ex) { |
||||||
|
Log.d(TAG, "Key store error", ex); |
||||||
|
throw ex; |
||||||
|
} catch (Exception ex) { |
||||||
|
Log.d(TAG, "Key store error", ex); |
||||||
|
throw new ServiceErrorException(KEY_STORE_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private synchronized static byte[] getData( |
||||||
|
final Context context, |
||||||
|
String alias, |
||||||
|
String aliasFile, |
||||||
|
String aliasIV) |
||||||
|
throws ServiceErrorException { |
||||||
|
KeyStore keyStore; |
||||||
|
String encryptedDataFilePath = getFilePath(context, aliasFile); |
||||||
|
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(); |
||||||
|
if (!fileExists) { |
||||||
|
return null;/* file also not there, fine then */ |
||||||
|
} |
||||||
|
throw new ServiceErrorException( |
||||||
|
KEY_IS_GONE, |
||||||
|
"file is present but the key is gone: " + alias); |
||||||
|
} |
||||||
|
|
||||||
|
boolean ivExists = new File(getFilePath(context, aliasIV)).exists(); |
||||||
|
boolean aliasExists = new File(getFilePath(context, aliasFile)).exists(); |
||||||
|
if (!ivExists || !aliasExists) { |
||||||
|
removeAliasAndFiles(context, alias, aliasFile, aliasIV); |
||||||
|
//report it if one exists and not the other.
|
||||||
|
if (ivExists != aliasExists) { |
||||||
|
throw new ServiceErrorException( |
||||||
|
IV_OR_ALIAS_NO_ON_DISK, |
||||||
|
"file is present but the key is gone: " + alias); |
||||||
|
} else { |
||||||
|
throw new ServiceErrorException( |
||||||
|
IV_OR_ALIAS_NO_ON_DISK, |
||||||
|
"!ivExists && !aliasExists: " + alias); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
byte[] iv = readBytesFromFile(getFilePath(context, aliasIV)); |
||||||
|
if (iv == null || iv.length == 0) { |
||||||
|
throw new NullPointerException("iv is missing for " + alias); |
||||||
|
} |
||||||
|
Cipher outCipher = Cipher.getInstance(CIPHER_ALGORITHM); |
||||||
|
outCipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv)); |
||||||
|
CipherInputStream cipherInputStream = new CipherInputStream(new FileInputStream(encryptedDataFilePath), outCipher); |
||||||
|
return readBytesFromStream(cipherInputStream); |
||||||
|
} catch (InvalidKeyException e) { |
||||||
|
if (e instanceof UserNotAuthenticatedException) { |
||||||
|
// showAuthenticationScreen(context, requestCode);
|
||||||
|
throw new ServiceErrorException(USER_NOT_AUTHENTICATED); |
||||||
|
} else { |
||||||
|
throw new ServiceErrorException(INVALID_KEY); |
||||||
|
} |
||||||
|
} catch (IOException | CertificateException | KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException e) { |
||||||
|
throw new ServiceErrorException(KEY_STORE_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private synchronized static String getFilePath(Context context, String fileName) { |
||||||
|
return new File(context.getFilesDir(), fileName).getAbsolutePath(); |
||||||
|
} |
||||||
|
|
||||||
|
private 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; |
||||||
|
} |
||||||
|
|
||||||
|
public synchronized static void removeAliasAndFiles(Context context, String alias, String dataFileName, String ivFileName) { |
||||||
|
KeyStore keyStore; |
||||||
|
try { |
||||||
|
keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); |
||||||
|
keyStore.load(null); |
||||||
|
keyStore.deleteEntry(alias); |
||||||
|
new File(getFilePath(context, dataFileName)).delete(); |
||||||
|
new File(getFilePath(context, ivFileName)).delete(); |
||||||
|
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static byte[] readBytesFromStream(InputStream in) { |
||||||
|
// this dynamically extends to take the bytes you read
|
||||||
|
ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream(); |
||||||
|
// this is storage overwritten on each iteration with bytes
|
||||||
|
int bufferSize = 1024; |
||||||
|
byte[] buffer = new byte[bufferSize]; |
||||||
|
// we need to know how may bytes were read to write them to the byteBuffer
|
||||||
|
int len; |
||||||
|
try { |
||||||
|
while ((len = in.read(buffer)) != -1) { |
||||||
|
byteBuffer.write(buffer, 0, len); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} finally { |
||||||
|
try { |
||||||
|
byteBuffer.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
if (in != null) try { |
||||||
|
in.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
// and then we can return your byte array.
|
||||||
|
return byteBuffer.toByteArray(); |
||||||
|
} |
||||||
|
|
||||||
|
private static byte[] readBytesFromFile(String path) { |
||||||
|
byte[] bytes = null; |
||||||
|
FileInputStream fin; |
||||||
|
try { |
||||||
|
File file = new File(path); |
||||||
|
fin = new FileInputStream(file); |
||||||
|
bytes = readBytesFromStream(fin); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
return bytes; |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean put(Context context, String address, String password) throws ServiceErrorException { |
||||||
|
return setData(context, password.getBytes(), address, address, address+"iv"); |
||||||
|
} |
||||||
|
|
||||||
|
public static byte[] get(Context context, String address) throws ServiceErrorException { |
||||||
|
return getData(context, address, address, address+"iv"); |
||||||
|
} |
||||||
|
|
||||||
|
public static void showAuthenticationScreen(Context 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: "); |
||||||
|
if (context instanceof Activity) { |
||||||
|
Activity app = (Activity) context; |
||||||
|
KeyguardManager mKeyguardManager = (KeyguardManager) app.getSystemService(Context.KEYGUARD_SERVICE); |
||||||
|
if (mKeyguardManager == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
Intent intent = mKeyguardManager |
||||||
|
.createConfirmDeviceCredentialIntent( |
||||||
|
context.getString(R.string.unlock_screen_title_android), |
||||||
|
context.getString(R.string.unlock_screen_prompt_android)); |
||||||
|
if (intent != null) { |
||||||
|
app.startActivityForResult(intent, requestCode); |
||||||
|
} else { |
||||||
|
Log.e(TAG, "showAuthenticationScreen: failed to create intent for auth"); |
||||||
|
app.finish(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Log.e(TAG, "showAuthenticationScreen: context is not activity!"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package com.wallet.crypto.trustapp.util; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.content.SharedPreferences; |
||||||
|
import android.preference.PreferenceManager; |
||||||
|
|
||||||
|
import com.wallet.crypto.trustapp.controller.ServiceErrorException; |
||||||
|
import com.wallet.pwd.trustapp.PasswordManager; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
public class PMMigrateHelper { |
||||||
|
public static void migrate(Context context) throws ServiceErrorException { |
||||||
|
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); |
||||||
|
Map<String, ?> passwords = pref.getAll(); |
||||||
|
for (String key : passwords.keySet()) { |
||||||
|
if (key.contains("-pwd")) { |
||||||
|
String address = key.replace("-pwd", ""); |
||||||
|
try { |
||||||
|
KS.put(context, address.toLowerCase(), PasswordManager.getPassword(address, context)); |
||||||
|
pref.edit().remove(key).apply(); |
||||||
|
} catch (ServiceErrorException ex) { |
||||||
|
throw ex; |
||||||
|
} catch (Exception ex) { |
||||||
|
ex.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<FrameLayout |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:padding="32dp" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
> |
||||||
|
<LinearLayout |
||||||
|
android:layout_gravity="center" |
||||||
|
android:orientation="horizontal" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
> |
||||||
|
<ProgressBar |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
/> |
||||||
|
<TextView |
||||||
|
android:text="@string/sending_progress" |
||||||
|
android:layout_gravity="center_vertical" |
||||||
|
android:layout_marginLeft="8dp" |
||||||
|
android:layout_marginEnd="8dp" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
/> |
||||||
|
</LinearLayout> |
||||||
|
</FrameLayout> |
Loading…
Reference in new issue