- Simplified Database structure to include only what is going to be used for the wallet.
- Properly distroy NetworkService connection when ConnectedActivty is distroyed to avoid crashes. - Created UserAccountRepository and AuthorityRepository to handle their corresponding database operations in an asynchronous way, because Room enforces database operations to be done in a thread other than the UI thread. - In ImportBrainkeyActivity when a proper brainkey is given save the imported account into the database, put a flag that tells the app there is a current account imported and send the user to MainActivity.
This commit is contained in:
parent
847e8a8d7f
commit
175d48f8c6
14 changed files with 244 additions and 108 deletions
|
@ -47,6 +47,7 @@ dependencies {
|
||||||
kapt "androidx.room:room-compiler:$room_version"
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
|
|
||||||
implementation 'org.bitcoinj:bitcoinj-core:0.14.3'
|
implementation 'org.bitcoinj:bitcoinj-core:0.14.3'
|
||||||
|
implementation 'com.moldedbits.r2d2:r2d2:1.0.1'
|
||||||
implementation 'com.google.zxing:core:3.3.1'
|
implementation 'com.google.zxing:core:3.3.1'
|
||||||
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
|
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
|
||||||
implementation 'com.afollestad.material-dialogs:core:2.0.0-rc1'
|
implementation 'com.afollestad.material-dialogs:core:2.0.0-rc1'
|
||||||
|
|
|
@ -7,7 +7,6 @@ import android.content.ServiceConnection
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.PersistableBundle
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
@ -84,6 +83,11 @@ abstract class ConnectedActivity : AppCompatActivity(), ServiceConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
if (!mDisposable!!.isDisposed) mDisposable!!.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
|
@ -99,6 +103,16 @@ abstract class ConnectedActivity : AppCompatActivity(), ServiceConnection {
|
||||||
// .getLong(Constants.KEY_ACCOUNT_OPERATION_COUNT, -1)
|
// .getLong(Constants.KEY_ACCOUNT_OPERATION_COUNT, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
// Unbinding from network service
|
||||||
|
if (mShouldUnbindNetwork) {
|
||||||
|
unbindService(this)
|
||||||
|
mShouldUnbindNetwork = false
|
||||||
|
}
|
||||||
|
// mHandler.removeCallbacks(mCheckMissingPaymentsTask)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Task used to perform a redundant payment check.
|
* Task used to perform a redundant payment check.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
package cy.agorise.bitsybitshareswallet.activities
|
package cy.agorise.bitsybitshareswallet.activities
|
||||||
|
|
||||||
import android.content.DialogInterface
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import com.afollestad.materialdialogs.list.listItems
|
import com.afollestad.materialdialogs.list.listItemsSingleChoice
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
|
import cy.agorise.bitsybitshareswallet.daos.BitsyDatabase
|
||||||
|
import cy.agorise.bitsybitshareswallet.models.Authority
|
||||||
|
import cy.agorise.bitsybitshareswallet.repositories.AuthorityRepository
|
||||||
|
import cy.agorise.bitsybitshareswallet.repositories.UserAccountRepository
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import cy.agorise.graphenej.Address
|
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
|
||||||
import cy.agorise.graphenej.BrainKey
|
import cy.agorise.graphenej.*
|
||||||
import cy.agorise.graphenej.UserAccount
|
|
||||||
import cy.agorise.graphenej.api.ConnectionStatusUpdate
|
import cy.agorise.graphenej.api.ConnectionStatusUpdate
|
||||||
import cy.agorise.graphenej.api.calls.GetAccounts
|
import cy.agorise.graphenej.api.calls.GetAccounts
|
||||||
import cy.agorise.graphenej.api.calls.GetKeyReferences
|
import cy.agorise.graphenej.api.calls.GetKeyReferences
|
||||||
|
@ -23,6 +25,9 @@ import kotlinx.android.synthetic.main.activity_import_brainkey.*
|
||||||
import org.bitcoinj.core.ECKey
|
import org.bitcoinj.core.ECKey
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
// TODO Add method to load the 20? most important assets
|
||||||
|
// TODO add progress bar or something while the user waits for the import response from the node
|
||||||
|
|
||||||
class ImportBrainkeyActivity : ConnectedActivity() {
|
class ImportBrainkeyActivity : ConnectedActivity() {
|
||||||
private val TAG = "ImportBrainkeyActivity"
|
private val TAG = "ImportBrainkeyActivity"
|
||||||
|
|
||||||
|
@ -179,17 +184,19 @@ class ImportBrainkeyActivity : ConnectedActivity() {
|
||||||
MaterialDialog(this)
|
MaterialDialog(this)
|
||||||
.title(R.string.dialog__account_candidates_title)
|
.title(R.string.dialog__account_candidates_title)
|
||||||
.message(R.string.dialog__account_candidates_content)
|
.message(R.string.dialog__account_candidates_content)
|
||||||
.listItems(items = candidates) { dialog, index, _ ->
|
.listItemsSingleChoice (items = candidates, initialSelection = -1) { _, index, _ ->
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
// If one account was selected, we keep a reference to it and
|
// If one account was selected, we keep a reference to it and
|
||||||
// store the account properties
|
// store the account properties
|
||||||
// TODO make sure this is reached
|
|
||||||
mUserAccount = mUserAccountCandidates!![index]
|
mUserAccount = mUserAccountCandidates!![index]
|
||||||
onAccountSelected(accountPropertiesList[index])
|
onAccountSelected(accountPropertiesList[index])
|
||||||
dialog.dismiss()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.negativeButton(android.R.string.cancel) { mKeyReferencesAttempts = 0 }
|
.positiveButton(android.R.string.ok)
|
||||||
|
.negativeButton(android.R.string.cancel) {
|
||||||
|
mKeyReferencesAttempts = 0
|
||||||
|
}
|
||||||
|
.cancelable(false)
|
||||||
.show()
|
.show()
|
||||||
} else if (accountPropertiesList.size == 1) {
|
} else if (accountPropertiesList.size == 1) {
|
||||||
onAccountSelected(accountPropertiesList[0])
|
onAccountSelected(accountPropertiesList[0])
|
||||||
|
@ -216,25 +223,76 @@ class ImportBrainkeyActivity : ConnectedActivity() {
|
||||||
private fun onAccountSelected(accountProperties: AccountProperties) {
|
private fun onAccountSelected(accountProperties: AccountProperties) {
|
||||||
mUserAccount!!.name = accountProperties.name
|
mUserAccount!!.name = accountProperties.name
|
||||||
|
|
||||||
Toast.makeText(this, "Account: "+accountProperties.name, Toast.LENGTH_SHORT).show()
|
val encryptedPIN = CryptoUtils.encrypt(this, tietPin.text!!.toString())
|
||||||
|
|
||||||
val password = tietPin.text!!.toString()
|
// Stores the user selected PIN encrypted
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
.edit()
|
||||||
|
.putString(Constants.KEY_ENCRYPTED_PIN, encryptedPIN)
|
||||||
|
.apply()
|
||||||
|
|
||||||
// Stores the accounts this key refers to
|
// Stores the accounts this key refers to
|
||||||
// database.putOwnedUserAccounts(applicationContext, mUserAccount, password)
|
val id = accountProperties.id
|
||||||
|
val name = accountProperties.name
|
||||||
|
val isLTM = accountProperties.membership_expiration_date == Constants.LIFETIME_EXPIRATION_DATE
|
||||||
|
|
||||||
|
val userAccount = cy.agorise.bitsybitshareswallet.models.UserAccount(id, name, isLTM)
|
||||||
|
|
||||||
|
val userAccountRepository = UserAccountRepository(application)
|
||||||
|
userAccountRepository.insert(userAccount)
|
||||||
|
|
||||||
// Stores the id of the currently active user account
|
// Stores the id of the currently active user account
|
||||||
// PreferenceManager.getDefaultSharedPreferences(applicationContext)
|
PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
// .edit()
|
.edit()
|
||||||
// .putString(Constants.KEY_CURRENT_ACCOUNT_ID, mUserAccount!!.objectId)
|
.putString(Constants.KEY_CURRENT_ACCOUNT_ID, mUserAccount!!.objectId)
|
||||||
// .apply()
|
.apply()
|
||||||
|
|
||||||
// Trying to store all possible authorities (owner, active and memo)
|
// Trying to store all possible authorities (owner, active and memo) into the database
|
||||||
// for (i in 0..2) {
|
val ownerAuthority = accountProperties.owner
|
||||||
// mBrainKey.setSequenceNumber(i)
|
val activeAuthority = accountProperties.active
|
||||||
// saveAccountAuthorities(mBrainKey, accountProperties)
|
val options = accountProperties.options
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO move to MainActivity
|
for (i in 0..2) {
|
||||||
|
mBrainKey!!.sequenceNumber = i
|
||||||
|
val publicKey = PublicKey(ECKey.fromPublicOnly(mBrainKey!!.privateKey.pubKey))
|
||||||
|
|
||||||
|
if (ownerAuthority.keyAuths.keys.contains(publicKey)) {
|
||||||
|
addAuthorityToDatabase(accountProperties.id, AuthorityType.OWNER.ordinal, mBrainKey!!)
|
||||||
|
}
|
||||||
|
if (activeAuthority.keyAuths.keys.contains(publicKey)) {
|
||||||
|
addAuthorityToDatabase(accountProperties.id, AuthorityType.ACTIVE.ordinal, mBrainKey!!)
|
||||||
|
}
|
||||||
|
if (options.memoKey == publicKey) {
|
||||||
|
addAuthorityToDatabase(accountProperties.id, AuthorityType.MEMO.ordinal, mBrainKey!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stores a flag into the SharedPreferences to tell the app there is an active account and there is no need
|
||||||
|
// to show this activity again, until the account is removed.
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
.edit()
|
||||||
|
.putBoolean(Constants.KEY_INITIAL_SETUP_DONE, true)
|
||||||
|
.apply()
|
||||||
|
|
||||||
|
// Send the user to the MainActivity
|
||||||
|
val intent = Intent(this, MainActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given BrainKey encrypted as AuthorityType of userId.
|
||||||
|
*/
|
||||||
|
private fun addAuthorityToDatabase(userId: String, authorityType: Int, brainKey: BrainKey) {
|
||||||
|
val brainKeyWords = brainKey.brainKey
|
||||||
|
val sequenceNumber = brainKey.sequenceNumber
|
||||||
|
|
||||||
|
val encryptedBrainKey = CryptoUtils.encrypt(this, brainKeyWords)
|
||||||
|
val encryptedSequenceNumber = CryptoUtils.encrypt(this, sequenceNumber.toString())
|
||||||
|
|
||||||
|
val authority = Authority(0, userId, authorityType, encryptedBrainKey, encryptedSequenceNumber)
|
||||||
|
|
||||||
|
val authorityRepository = AuthorityRepository(application)
|
||||||
|
authorityRepository.insert(authority)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,24 +10,20 @@ import cy.agorise.bitsybitshareswallet.models.*
|
||||||
Asset::class,
|
Asset::class,
|
||||||
Authority::class,
|
Authority::class,
|
||||||
Balance::class,
|
Balance::class,
|
||||||
BrainKey::class,
|
|
||||||
EquivalentValue::class,
|
EquivalentValue::class,
|
||||||
Operation::class,
|
Operation::class,
|
||||||
Transfer::class,
|
Transfer::class,
|
||||||
UserAccount::class,
|
UserAccount::class
|
||||||
UserAccountAuthority::class
|
|
||||||
], version = 1, exportSchema = false)
|
], version = 1, exportSchema = false)
|
||||||
abstract class BitsyDatabase : RoomDatabase() {
|
abstract class BitsyDatabase : RoomDatabase() {
|
||||||
|
|
||||||
abstract fun assetDao(): AssetDao
|
abstract fun assetDao(): AssetDao
|
||||||
abstract fun authorityDao(): AuthorityDao
|
abstract fun authorityDao(): AuthorityDao
|
||||||
abstract fun balanceDao(): BalanceDao
|
abstract fun balanceDao(): BalanceDao
|
||||||
abstract fun brainKeyDao(): BrainKeyDao
|
|
||||||
abstract fun equivalentValueDao(): EquivalentValueDao
|
abstract fun equivalentValueDao(): EquivalentValueDao
|
||||||
abstract fun operationDao(): OperationDao
|
abstract fun operationDao(): OperationDao
|
||||||
abstract fun transferDao(): TransferDao
|
abstract fun transferDao(): TransferDao
|
||||||
abstract fun userAccountDao(): UserAccountDao
|
abstract fun userAccountDao(): UserAccountDao
|
||||||
abstract fun userAccountAuthorityDao(): UserAccountAuthorityDao
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
@ -48,9 +44,5 @@ abstract class BitsyDatabase : RoomDatabase() {
|
||||||
|
|
||||||
return INSTANCE
|
return INSTANCE
|
||||||
}
|
}
|
||||||
|
|
||||||
fun destroyInstance() {
|
|
||||||
INSTANCE = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
package cy.agorise.bitsybitshareswallet.daos
|
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.Query
|
|
||||||
import cy.agorise.bitsybitshareswallet.models.BrainKey
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface BrainKeyDao {
|
|
||||||
@Insert
|
|
||||||
fun insert(brainKey: BrainKey)
|
|
||||||
|
|
||||||
@Query("SELECT * FROM brain_keys")
|
|
||||||
fun getAllBrainKeys(): LiveData<List<BrainKey>>
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package cy.agorise.bitsybitshareswallet.daos
|
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.Query
|
|
||||||
import cy.agorise.bitsybitshareswallet.models.Authority
|
|
||||||
import cy.agorise.bitsybitshareswallet.models.UserAccountAuthority
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface UserAccountAuthorityDao {
|
|
||||||
@Insert
|
|
||||||
fun insert(userAccountAuthority: UserAccountAuthority)
|
|
||||||
|
|
||||||
// @Query("SELECT * FROM authorities INNER JOIN user_accounts__authorities ON user_accounts.id=user_accounts__authorities.user_account_id WHERE user_accounts__authorities.user_account_id=:userAccountId")
|
|
||||||
// fun getAuthoritiesForUserAccount(userAccountId: String): LiveData<List<Authority>>
|
|
||||||
}
|
|
|
@ -8,6 +8,8 @@ import androidx.room.PrimaryKey
|
||||||
data class Authority (
|
data class Authority (
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
@ColumnInfo(name = "id") val id: Long,
|
@ColumnInfo(name = "id") val id: Long,
|
||||||
@ColumnInfo(name = "encrypted_private_key") val encryptedBrainkey: String,
|
@ColumnInfo(name = "user_id") val userId: String,
|
||||||
@ColumnInfo(name = "user_id") val userId: String
|
@ColumnInfo(name = "authority_type") val authorityType: Int,
|
||||||
|
@ColumnInfo(name = "encrypted_brain_key") val encryptedBrainKey: String,
|
||||||
|
@ColumnInfo(name = "encrypted_sequence_number") val encryptedSequenceNumber: String
|
||||||
)
|
)
|
|
@ -1,13 +0,0 @@
|
||||||
package cy.agorise.bitsybitshareswallet.models
|
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
|
|
||||||
@Entity(tableName="brain_keys")
|
|
||||||
data class BrainKey(
|
|
||||||
@PrimaryKey
|
|
||||||
@ColumnInfo(name = "public_key") val publicKey: String,
|
|
||||||
@ColumnInfo(name = "encrypted_brain_key") val encryptedBrainKey: String,
|
|
||||||
@ColumnInfo(name = "sequence_number") val sequenceNumber: Long
|
|
||||||
)
|
|
|
@ -9,6 +9,5 @@ data class UserAccount (
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
@ColumnInfo(name = "id") val id: String,
|
@ColumnInfo(name = "id") val id: String,
|
||||||
@ColumnInfo(name = "name") val name: String,
|
@ColumnInfo(name = "name") val name: String,
|
||||||
@ColumnInfo(name = "is_ltm") val isLtm: Boolean,
|
@ColumnInfo(name = "is_ltm") val isLtm: Boolean
|
||||||
@ColumnInfo(name = "weight_threshold") val weightThreshold: Int
|
|
||||||
)
|
)
|
|
@ -1,25 +0,0 @@
|
||||||
package cy.agorise.bitsybitshareswallet.models
|
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.ForeignKey
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Table to create a N:N relationship between [UserAccount] and [Authority]
|
|
||||||
*/
|
|
||||||
@Entity(tableName = "user_accounts__authorities",
|
|
||||||
primaryKeys = ["user_account_id", "authority_id"],
|
|
||||||
foreignKeys = [ForeignKey(
|
|
||||||
entity = UserAccount::class,
|
|
||||||
parentColumns = ["id"],
|
|
||||||
childColumns = ["user_account_id"]
|
|
||||||
), ForeignKey(
|
|
||||||
entity = Authority::class,
|
|
||||||
parentColumns = ["id"],
|
|
||||||
childColumns = ["authority_id"]
|
|
||||||
)])
|
|
||||||
data class UserAccountAuthority (
|
|
||||||
@ColumnInfo(name = "user_account_id") val userAccountId: String,
|
|
||||||
@ColumnInfo(name = "authority_id") val authorityId: Long,
|
|
||||||
@ColumnInfo(name = "weight") val weight: Int
|
|
||||||
)
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package cy.agorise.bitsybitshareswallet.repositories
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import cy.agorise.bitsybitshareswallet.daos.AuthorityDao
|
||||||
|
import cy.agorise.bitsybitshareswallet.daos.BitsyDatabase
|
||||||
|
import cy.agorise.bitsybitshareswallet.models.Authority
|
||||||
|
|
||||||
|
class AuthorityRepository internal constructor(application: Application) {
|
||||||
|
|
||||||
|
private val mAuthorityDao: AuthorityDao
|
||||||
|
|
||||||
|
init {
|
||||||
|
val db = BitsyDatabase.getDatabase(application)
|
||||||
|
mAuthorityDao = db!!.authorityDao()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insert(authority: Authority) {
|
||||||
|
insertAsyncTask(mAuthorityDao).execute(authority)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class insertAsyncTask internal constructor(private val mAsyncTaskDao: AuthorityDao) :
|
||||||
|
AsyncTask<Authority, Void, Void>() {
|
||||||
|
|
||||||
|
override fun doInBackground(vararg authorities: Authority): Void? {
|
||||||
|
mAsyncTaskDao.insert(authorities[0])
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package cy.agorise.bitsybitshareswallet.repositories
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import cy.agorise.bitsybitshareswallet.daos.BitsyDatabase
|
||||||
|
import cy.agorise.bitsybitshareswallet.daos.UserAccountDao
|
||||||
|
import cy.agorise.bitsybitshareswallet.models.UserAccount
|
||||||
|
|
||||||
|
class UserAccountRepository internal constructor(application: Application) {
|
||||||
|
|
||||||
|
private val mUserAccountDao: UserAccountDao
|
||||||
|
|
||||||
|
init {
|
||||||
|
val db = BitsyDatabase.getDatabase(application)
|
||||||
|
mUserAccountDao = db!!.userAccountDao()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insert(userAccount: UserAccount) {
|
||||||
|
insertAsyncTask(mUserAccountDao).execute(userAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class insertAsyncTask internal constructor(private val mAsyncTaskDao: UserAccountDao) :
|
||||||
|
AsyncTask<UserAccount, Void, Void>() {
|
||||||
|
|
||||||
|
override fun doInBackground(vararg userAccounts: UserAccount): Void? {
|
||||||
|
mAsyncTaskDao.insert(userAccounts[0])
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,15 @@ object Constants {
|
||||||
/** The minimum required length for a PIN number */
|
/** The minimum required length for a PIN number */
|
||||||
const val MIN_PIN_LENGTH = 6
|
const val MIN_PIN_LENGTH = 6
|
||||||
|
|
||||||
|
/** The user selected encrypted PIN */
|
||||||
|
const val KEY_ENCRYPTED_PIN = "key_encrypted_pin"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LTM accounts come with an expiration date expressed as this string.
|
||||||
|
* This is used to recognize such accounts from regular ones.
|
||||||
|
*/
|
||||||
|
const val LIFETIME_EXPIRATION_DATE = "1969-12-31T23:59:59"
|
||||||
|
|
||||||
/** Key used to store if the initial setup is already done or not */
|
/** Key used to store if the initial setup is already done or not */
|
||||||
const val KEY_INITIAL_SETUP_DONE = "key_initial_setup_done"
|
const val KEY_INITIAL_SETUP_DONE = "key_initial_setup_done"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package cy.agorise.bitsybitshareswallet.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.preference.PreferenceManager
|
||||||
|
|
||||||
|
import com.moldedbits.r2d2.R2d2
|
||||||
|
|
||||||
|
import javax.crypto.AEADBadTagException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that provides encryption/decryption support by using the key management framework provided
|
||||||
|
* by the KeyStore system.
|
||||||
|
*
|
||||||
|
* The implemented scheme was taken from [this](https://medium.com/@ericfu/securely-storing-secrets-in-an-android-application-501f030ae5a3)> blog post.
|
||||||
|
*
|
||||||
|
* @see [Android Keystore System](https://developer.android.com/training/articles/keystore.html)
|
||||||
|
*/
|
||||||
|
|
||||||
|
object CryptoUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts and stores a key-value pair in the shared preferences
|
||||||
|
* @param context The application context
|
||||||
|
* @param key The key to be used to reference the data
|
||||||
|
* @param value The actual value to be stored
|
||||||
|
*/
|
||||||
|
fun put(context: Context, key: String, value: String) {
|
||||||
|
val r2d2 = R2d2(context)
|
||||||
|
val encrypted = r2d2.encryptData(value)
|
||||||
|
PreferenceManager
|
||||||
|
.getDefaultSharedPreferences(context)
|
||||||
|
.edit()
|
||||||
|
.putString(key, encrypted)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves and decrypts an encrypted value from the shared preferences
|
||||||
|
* @param context The application context
|
||||||
|
* @param key The key used to reference the data
|
||||||
|
* @return The plaintext version of the encrypted data
|
||||||
|
*/
|
||||||
|
operator fun get(context: Context, key: String): String {
|
||||||
|
val r2d2 = R2d2(context)
|
||||||
|
val encrypted = PreferenceManager.getDefaultSharedPreferences(context).getString(key, null)
|
||||||
|
return r2d2.decryptData(encrypted)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts some data
|
||||||
|
* @param context The application context
|
||||||
|
* @param plaintext The plaintext version of the data
|
||||||
|
* @return Encrypted data
|
||||||
|
*/
|
||||||
|
fun encrypt(context: Context, plaintext: String): String {
|
||||||
|
val r2d2 = R2d2(context)
|
||||||
|
return r2d2.encryptData(plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts some data
|
||||||
|
* @param context The application context
|
||||||
|
* @param ciphertext The ciphertext version of the data
|
||||||
|
* @return Decrypted data
|
||||||
|
*/
|
||||||
|
@Throws(AEADBadTagException::class)
|
||||||
|
fun decrypt(context: Context, ciphertext: String): String {
|
||||||
|
val r2d2 = R2d2(context)
|
||||||
|
return r2d2.decryptData(ciphertext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue