587 lines
24 KiB
Kotlin
587 lines
24 KiB
Kotlin
package cy.agorise.bitsybitshareswallet.fragments
|
|
|
|
import android.content.*
|
|
import android.os.Bundle
|
|
import android.os.Handler
|
|
import android.preference.PreferenceManager
|
|
import android.util.Log
|
|
import android.view.LayoutInflater
|
|
import android.view.View
|
|
import android.view.ViewGroup
|
|
import androidx.appcompat.widget.Toolbar
|
|
import androidx.collection.LongSparseArray
|
|
import androidx.lifecycle.ViewModelProviders
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
import com.afollestad.materialdialogs.MaterialDialog
|
|
import com.afollestad.materialdialogs.callbacks.onDismiss
|
|
import com.afollestad.materialdialogs.customview.customView
|
|
import com.afollestad.materialdialogs.list.customListAdapter
|
|
import com.afollestad.materialdialogs.list.listItemsSingleChoice
|
|
import com.google.common.primitives.UnsignedLong
|
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
import cy.agorise.bitsybitshareswallet.BuildConfig
|
|
import cy.agorise.bitsybitshareswallet.R
|
|
import cy.agorise.bitsybitshareswallet.adapters.FullNodesAdapter
|
|
import cy.agorise.bitsybitshareswallet.repositories.AuthorityRepository
|
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
|
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
|
|
import cy.agorise.bitsybitshareswallet.viewmodels.SettingsFragmentViewModel
|
|
import cy.agorise.graphenej.*
|
|
import cy.agorise.graphenej.api.ConnectionStatusUpdate
|
|
import cy.agorise.graphenej.api.calls.BroadcastTransaction
|
|
import cy.agorise.graphenej.api.calls.GetAccounts
|
|
import cy.agorise.graphenej.api.calls.GetDynamicGlobalProperties
|
|
import cy.agorise.graphenej.models.DynamicGlobalProperties
|
|
import cy.agorise.graphenej.models.JsonRpcResponse
|
|
import cy.agorise.graphenej.operations.AccountUpgradeOperationBuilder
|
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
|
import io.reactivex.schedulers.Schedulers
|
|
import kotlinx.android.synthetic.main.fragment_settings.*
|
|
import org.bitcoinj.core.DumpedPrivateKey
|
|
import org.bitcoinj.core.ECKey
|
|
import java.text.NumberFormat
|
|
import javax.crypto.AEADBadTagException
|
|
|
|
class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatternEnteredListener {
|
|
|
|
companion object {
|
|
private const val TAG = "SettingsFragment"
|
|
|
|
// Constants used to perform security locked requests
|
|
private const val ACTION_CHANGE_SECURITY_LOCK = 1
|
|
private const val ACTION_SHOW_BRAINKEY = 2
|
|
private const val ACTION_UPGRADE_TO_LTM = 3
|
|
private const val ACTION_REMOVE_ACCOUNT = 4
|
|
|
|
// Constants used to organize NetworkService requests
|
|
private const val RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_NODES = 1
|
|
private const val RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_LTM = 2
|
|
private const val RESPONSE_BROADCAST_TRANSACTION = 3
|
|
}
|
|
|
|
private lateinit var mViewModel: SettingsFragmentViewModel
|
|
|
|
private var mUserAccount: UserAccount? = null
|
|
|
|
private var privateKey: String? = null
|
|
|
|
// Dialog displaying the list of nodes and their latencies
|
|
private var mNodesDialog: MaterialDialog? = null
|
|
|
|
// NodesDialog's RecyclerView LayoutManager used to always keep showing the first node of the list.
|
|
private var mNodesDialogLinearLayoutManager: LinearLayoutManager? = null
|
|
|
|
/** Adapter that holds the FullNode list used in the Bitshares nodes modal */
|
|
private var nodesAdapter: FullNodesAdapter? = null
|
|
|
|
// Map used to keep track of request and response id pairs
|
|
private val responseMap = LongSparseArray<Int>()
|
|
|
|
/** Transaction to upgrade to LTM */
|
|
private var ltmTransaction: Transaction? = null
|
|
|
|
private val mHandler = Handler()
|
|
|
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
|
setHasOptionsMenu(true)
|
|
|
|
val nightMode = PreferenceManager.getDefaultSharedPreferences(context)
|
|
.getBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, false)
|
|
|
|
// Make sure the toolbar show the correct colors in both day and night modes
|
|
val toolbar: Toolbar? = activity?.findViewById(R.id.toolbar)
|
|
toolbar?.setBackgroundResource(if (!nightMode) R.color.colorPrimary else R.color.colorToolbarDark)
|
|
|
|
return inflater.inflate(R.layout.fragment_settings, container, false)
|
|
}
|
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
super.onViewCreated(view, savedInstanceState)
|
|
|
|
val crashlytics = FirebaseCrashlytics.getInstance()
|
|
crashlytics.setCustomKey(Constants.CRASHLYTICS_KEY_LAST_SCREEN, TAG)
|
|
|
|
val userId = PreferenceManager.getDefaultSharedPreferences(context)
|
|
.getString(Constants.KEY_CURRENT_ACCOUNT_ID, "") ?: ""
|
|
|
|
// Configure ViewModel
|
|
mViewModel= ViewModelProviders.of(this).get(SettingsFragmentViewModel::class.java)
|
|
|
|
mViewModel.getUserAccount(userId).observe(this,
|
|
androidx.lifecycle.Observer<cy.agorise.bitsybitshareswallet.database.entities.UserAccount>{ userAccount ->
|
|
if (userAccount != null) {
|
|
mUserAccount = UserAccount(userAccount.id, userAccount.name)
|
|
btnUpgradeToLTM.isEnabled = !userAccount.isLtm // Disable button if already LTM
|
|
}
|
|
})
|
|
|
|
mViewModel.getWIF(userId, AuthorityType.ACTIVE.ordinal).observe(this,
|
|
androidx.lifecycle.Observer<String> { encryptedWIF ->
|
|
context?.let {
|
|
try {
|
|
privateKey = CryptoUtils.decrypt(it, encryptedWIF)
|
|
} catch (e: AEADBadTagException) {
|
|
Log.e(TAG, "AEADBadTagException. Class: " + e.javaClass + ", Msg: " + e.message)
|
|
} catch (e: IllegalStateException) {
|
|
crashlytics.recordException(e)
|
|
}
|
|
}
|
|
})
|
|
|
|
initAutoCloseSwitch()
|
|
|
|
initNightModeSwitch()
|
|
|
|
tvNetworkStatus.setOnClickListener { v -> showNodesDialog(v) }
|
|
|
|
// Obtain the current Security Lock Option selected and display it in the screen
|
|
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
|
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
|
// Security Lock Options
|
|
// 0 -> None
|
|
// 1 -> PIN
|
|
// 2 -> Pattern
|
|
|
|
tvSecurityLockSelected.text = resources.getStringArray(R.array.security_lock_options)[securityLockSelected]
|
|
|
|
tvSecurityLock.setOnClickListener { onSecurityLockTextSelected() }
|
|
tvSecurityLockSelected.setOnClickListener { onSecurityLockTextSelected() }
|
|
|
|
btnViewBrainKey.setOnClickListener { onShowBrainKeyButtonSelected() }
|
|
|
|
val lastAccountBackup = PreferenceManager.getDefaultSharedPreferences(context)
|
|
.getLong(Constants.KEY_LAST_ACCOUNT_BACKUP, 0L)
|
|
|
|
val now = System.currentTimeMillis()
|
|
|
|
if (lastAccountBackup + Constants.ACCOUNT_BACKUP_PERIOD < now)
|
|
tvBackupWarning.visibility = View.VISIBLE
|
|
|
|
btnUpgradeToLTM.setOnClickListener { onUpgradeToLTMButtonSelected() }
|
|
|
|
btnRemoveAccount.setOnClickListener { onRemoveAccountButtonSelected() }
|
|
}
|
|
|
|
private fun showNodesDialog(v: View) {
|
|
if (mNetworkService != null) {
|
|
val fullNodes = mNetworkService!!.nodes
|
|
|
|
nodesAdapter = FullNodesAdapter(v.context)
|
|
nodesAdapter?.add(fullNodes)
|
|
|
|
// PublishSubject used to announce full node latencies updates
|
|
val fullNodePublishSubject = mNetworkService?.nodeLatencyObservable ?: return
|
|
|
|
val nodesDisposable = fullNodePublishSubject
|
|
.subscribeOn(Schedulers.io())
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
.subscribe(
|
|
{ fullNode ->
|
|
mNodesDialogLinearLayoutManager?.scrollToPositionWithOffset(0, 0)
|
|
|
|
if (!fullNode.isRemoved)
|
|
nodesAdapter?.add(fullNode)
|
|
else
|
|
nodesAdapter?.remove(fullNode)
|
|
}, {
|
|
Log.e(TAG, "nodeLatencyObserver.onError.Msg: " + it.message)
|
|
}
|
|
)
|
|
|
|
mNodesDialogLinearLayoutManager = LinearLayoutManager(v.context)
|
|
|
|
mNodesDialog = MaterialDialog(v.context).show {
|
|
title(text = String.format("%s v%s", getString(R.string.app_name), BuildConfig.VERSION_NAME))
|
|
message(text = getString(R.string.title__bitshares_nodes_dialog, "-------"))
|
|
customListAdapter(nodesAdapter as FullNodesAdapter, mNodesDialogLinearLayoutManager)
|
|
negativeButton(android.R.string.ok)
|
|
onDismiss {
|
|
mHandler.removeCallbacks(mRequestDynamicGlobalPropertiesTask)
|
|
nodesDisposable.dispose()
|
|
}
|
|
}
|
|
|
|
// Registering a recurrent task used to poll for dynamic global properties requests
|
|
mHandler.post(mRequestDynamicGlobalPropertiesTask)
|
|
}
|
|
}
|
|
|
|
override fun onStart() {
|
|
super.onStart()
|
|
|
|
if (mNetworkService?.isConnected == true)
|
|
showConnectedState()
|
|
else
|
|
showDisconnectedState()
|
|
}
|
|
|
|
override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) {
|
|
if (responseMap.containsKey(response.id)) {
|
|
when (responseMap[response.id]) {
|
|
RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_NODES -> handleDynamicGlobalPropertiesNodes(response.result)
|
|
RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_LTM -> handleDynamicGlobalPropertiesLTM(response.result)
|
|
RESPONSE_BROADCAST_TRANSACTION -> handleBroadcastTransaction(response)
|
|
}
|
|
responseMap.remove(response.id)
|
|
}
|
|
}
|
|
|
|
override fun handleConnectionStatusUpdate(connectionStatusUpdate: ConnectionStatusUpdate) {
|
|
when (connectionStatusUpdate.updateCode) {
|
|
ConnectionStatusUpdate.CONNECTED -> {
|
|
showConnectedState()
|
|
}
|
|
ConnectionStatusUpdate.DISCONNECTED -> {
|
|
showDisconnectedState()
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun showConnectedState() {
|
|
tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null,
|
|
resources.getDrawable(R.drawable.ic_connected, null), null)
|
|
}
|
|
|
|
private fun showDisconnectedState() {
|
|
tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null,
|
|
resources.getDrawable(R.drawable.ic_disconnected, null), null)
|
|
}
|
|
|
|
/** Handles the result of the [GetDynamicGlobalProperties] api call to obtain the current block number and update
|
|
* it in the Nodes Dialog */
|
|
private fun handleDynamicGlobalPropertiesNodes(result: Any?) {
|
|
if (result is DynamicGlobalProperties) {
|
|
if (mNodesDialog != null && mNodesDialog?.isShowing == true) {
|
|
val blockNumber = NumberFormat.getInstance().format(result.head_block_number)
|
|
mNodesDialog?.message(text = getString(R.string.title__bitshares_nodes_dialog, blockNumber))
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun handleDynamicGlobalPropertiesLTM(result: Any?) {
|
|
if (result is DynamicGlobalProperties) {
|
|
val expirationTime = (result.time.time / 1000) + Transaction.DEFAULT_EXPIRATION_TIME
|
|
val headBlockId = result.head_block_id
|
|
val headBlockNumber = result.head_block_number
|
|
|
|
ltmTransaction?.blockData = BlockData(headBlockNumber, headBlockId, expirationTime)
|
|
|
|
val id = mNetworkService?.sendMessage(BroadcastTransaction(ltmTransaction), BroadcastTransaction.REQUIRED_API)
|
|
if (id != null) responseMap.append(id, RESPONSE_BROADCAST_TRANSACTION)
|
|
|
|
// TODO use an indicator to show that a transaction is in progress
|
|
}
|
|
}
|
|
|
|
/** Handles the result of the [BroadcastTransaction] api call to find out if the Transaction to upgrade the
|
|
* current account to LTM was successful or not */
|
|
private fun handleBroadcastTransaction(message: JsonRpcResponse<*>) {
|
|
if (message.result == null && message.error == null) {
|
|
// Looks like the upgrade to LTM was successful, we need to update the current account information from
|
|
// the blockchain and show a success dialog
|
|
mNetworkService?.sendMessage(GetAccounts(mUserAccount), GetAccounts.REQUIRED_API)
|
|
|
|
context?.let { context ->
|
|
MaterialDialog(context).show {
|
|
title(R.string.title__account_upgraded)
|
|
message(R.string.msg__account_upgraded)
|
|
positiveButton(android.R.string.ok)
|
|
}
|
|
}
|
|
} else if (message.error != null) {
|
|
// The upgrade to LTM wasn't successful, show a dialog to the user explaining the situation
|
|
context?.let { context ->
|
|
MaterialDialog(context).show {
|
|
title(R.string.title__upgrade_account_error)
|
|
message(R.string.msg__upgrade_account_error)
|
|
positiveButton(android.R.string.ok)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Task used to obtain frequent updates on the global dynamic properties object
|
|
*/
|
|
private val mRequestDynamicGlobalPropertiesTask = object : Runnable {
|
|
override fun run() {
|
|
val id = mNetworkService?.sendMessage(GetDynamicGlobalProperties(), GetDynamicGlobalProperties.REQUIRED_API)
|
|
if (id != null) responseMap.append(id, RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_NODES)
|
|
|
|
mHandler.postDelayed(this, Constants.BLOCK_PERIOD)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetches the relevant preference from the SharedPreferences and configures the corresponding switch accordingly,
|
|
* and adds a listener to the said switch to store the preference in case the user changes it.
|
|
*/
|
|
private fun initAutoCloseSwitch() {
|
|
val autoCloseOn = PreferenceManager.getDefaultSharedPreferences(context)
|
|
.getBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, true)
|
|
|
|
switchAutoClose.isChecked = autoCloseOn
|
|
|
|
switchAutoClose.setOnCheckedChangeListener { buttonView, isChecked ->
|
|
PreferenceManager.getDefaultSharedPreferences(buttonView.context).edit()
|
|
.putBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, isChecked).apply()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetches the relevant preference from the SharedPreferences and configures the corresponding switch accordingly,
|
|
* and adds a listener to the said switch to store the preference in case the user changes it. Also makes a call to
|
|
* recreate the activity and apply the selected theme.
|
|
*/
|
|
private fun initNightModeSwitch() {
|
|
val nightModeOn = PreferenceManager.getDefaultSharedPreferences(context)
|
|
.getBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, false)
|
|
|
|
switchNightMode.isChecked = nightModeOn
|
|
|
|
switchNightMode.setOnCheckedChangeListener { buttonView, isChecked ->
|
|
|
|
PreferenceManager.getDefaultSharedPreferences(buttonView.context).edit()
|
|
.putBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, isChecked).apply()
|
|
|
|
// Recreates the activity to apply the selected theme
|
|
activity?.recreate()
|
|
}
|
|
}
|
|
|
|
private fun onSecurityLockTextSelected() {
|
|
if (!verifySecurityLock(ACTION_CHANGE_SECURITY_LOCK))
|
|
showChooseSecurityLockDialog()
|
|
}
|
|
|
|
/**
|
|
* Encapsulates the logic required to do actions possibly locked by the Security Lock. If PIN/Pattern is selected
|
|
* then it prompts for it.
|
|
*
|
|
* @param actionIdentifier Identifier used to know why a verify security lock was launched
|
|
* @return true if the action was handled, false otherwise
|
|
*/
|
|
private fun verifySecurityLock(actionIdentifier: Int): Boolean {
|
|
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
|
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
|
// Security Lock Options
|
|
// 0 -> None
|
|
// 1 -> PIN
|
|
// 2 -> Pattern
|
|
|
|
// Args used for both PIN and Pattern options
|
|
val args = Bundle()
|
|
args.putInt(BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK,
|
|
BaseSecurityLockDialog.STEP_SECURITY_LOCK_VERIFY)
|
|
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, actionIdentifier)
|
|
|
|
return when (securityLockSelected) {
|
|
0 -> { /* None */
|
|
false
|
|
}
|
|
1 -> { /* PIN */
|
|
val pinFrag = PINSecurityLockDialog()
|
|
pinFrag.arguments = args
|
|
pinFrag.show(childFragmentManager, "pin_security_lock_tag")
|
|
true
|
|
}
|
|
else -> { /* Pattern */
|
|
val patternFrag = PatternSecurityLockDialog()
|
|
patternFrag.arguments = args
|
|
patternFrag.show(childFragmentManager, "pattern_security_lock_tag")
|
|
true
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun onPINPatternEntered(actionIdentifier: Int) {
|
|
when (actionIdentifier) {
|
|
ACTION_CHANGE_SECURITY_LOCK -> showChooseSecurityLockDialog()
|
|
ACTION_SHOW_BRAINKEY -> getBrainkey()
|
|
ACTION_UPGRADE_TO_LTM -> showUpgradeToLTMDialog()
|
|
ACTION_REMOVE_ACCOUNT -> showRemoveAccountDialog()
|
|
}
|
|
}
|
|
|
|
override fun onPINPatternChanged() {
|
|
// Obtain the new Security Lock Option selected and display it in the screen
|
|
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
|
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
|
// Security Lock Options
|
|
// 0 -> None
|
|
// 1 -> PIN
|
|
// 2 -> Pattern
|
|
|
|
tvSecurityLockSelected.text = resources.getStringArray(R.array.security_lock_options)[securityLockSelected]
|
|
}
|
|
|
|
/**
|
|
* Shows a dialog so the user can select its desired Security Lock option.
|
|
*/
|
|
private fun showChooseSecurityLockDialog() {
|
|
// Obtain the current Security Lock Option selected and display it in the screen
|
|
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
|
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
|
// Security Lock Options
|
|
// 0 -> None
|
|
// 1 -> PIN
|
|
// 2 -> Pattern
|
|
|
|
context?.let {
|
|
MaterialDialog(it).show {
|
|
title(R.string.title__security_dialog)
|
|
listItemsSingleChoice(R.array.security_lock_options, initialSelection = securityLockSelected) {_, index, _ ->
|
|
// Args used for both PIN and Pattern options
|
|
val args = Bundle()
|
|
args.putInt(BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK,
|
|
BaseSecurityLockDialog.STEP_SECURITY_LOCK_CREATE)
|
|
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, -1)
|
|
|
|
when (index) {
|
|
0 -> { /* None */
|
|
PreferenceManager.getDefaultSharedPreferences(context).edit()
|
|
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0).apply() // 0 -> None
|
|
|
|
// Call this function to update the UI
|
|
onPINPatternChanged()
|
|
}
|
|
1 -> { /* PIN */
|
|
val pinFrag = PINSecurityLockDialog()
|
|
pinFrag.arguments = args
|
|
pinFrag.show(childFragmentManager, "pin_security_lock_tag")
|
|
}
|
|
else -> { /* Pattern */
|
|
val patternFrag = PatternSecurityLockDialog()
|
|
patternFrag.arguments = args
|
|
patternFrag.show(childFragmentManager, "pattern_security_lock_tag")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun onShowBrainKeyButtonSelected() {
|
|
if (!verifySecurityLock(ACTION_SHOW_BRAINKEY))
|
|
getBrainkey()
|
|
}
|
|
|
|
private fun onUpgradeToLTMButtonSelected() {
|
|
if (!verifySecurityLock(ACTION_UPGRADE_TO_LTM))
|
|
showUpgradeToLTMDialog()
|
|
}
|
|
|
|
private fun onRemoveAccountButtonSelected() {
|
|
if (!verifySecurityLock(ACTION_REMOVE_ACCOUNT))
|
|
showRemoveAccountDialog()
|
|
}
|
|
|
|
/**
|
|
* Obtains the brainKey from the authorities db table for the current user account and if it is not null it passes
|
|
* the brainKey to a method to show it in a nice MaterialDialog
|
|
*/
|
|
private fun getBrainkey() {
|
|
context?.let {
|
|
val userId = PreferenceManager.getDefaultSharedPreferences(it)
|
|
.getString(Constants.KEY_CURRENT_ACCOUNT_ID, "") ?: ""
|
|
|
|
val authorityRepository = AuthorityRepository(it)
|
|
|
|
mDisposables.add(authorityRepository.get(userId)
|
|
.subscribeOn(Schedulers.io())
|
|
.map { authority ->
|
|
val plainBrainKey = CryptoUtils.decrypt(it, authority.encryptedBrainKey)
|
|
val plainSequenceNumber = CryptoUtils.decrypt(it, authority.encryptedSequenceNumber)
|
|
val sequenceNumber = Integer.parseInt(plainSequenceNumber)
|
|
BrainKey(plainBrainKey, sequenceNumber)
|
|
}
|
|
.subscribeOn(Schedulers.computation())
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
.subscribe { brainkey ->
|
|
showBrainKeyDialog(brainkey)
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Shows the plain brainkey in a dialog so that the user can view and Copy it.
|
|
*/
|
|
private fun showBrainKeyDialog(brainKey: BrainKey) {
|
|
context?.let { context ->
|
|
val dialog = MaterialDialog(context)
|
|
.title(text = "BrainKey")
|
|
.message(text = brainKey.brainKey)
|
|
.customView(R.layout.dialog_copy_brainkey)
|
|
.cancelable(false)
|
|
.positiveButton(R.string.button__copied) {
|
|
val now = System.currentTimeMillis()
|
|
PreferenceManager.getDefaultSharedPreferences(it.context).edit()
|
|
.putLong(Constants.KEY_LAST_ACCOUNT_BACKUP, now).apply()
|
|
tvBackupWarning.visibility = View.GONE
|
|
}
|
|
|
|
dialog.show()
|
|
}
|
|
}
|
|
|
|
private fun showUpgradeToLTMDialog() {
|
|
context?.let { context ->
|
|
val content = getString(R.string.msg__account_upgrade_dialog, mUserAccount?.name)
|
|
MaterialDialog(context).show {
|
|
message(text = content)
|
|
negativeButton(android.R.string.cancel)
|
|
positiveButton(android.R.string.ok) {
|
|
val operation = AccountUpgradeOperationBuilder()
|
|
.setIsUpgrade(true)
|
|
.setFee(AssetAmount(UnsignedLong.ZERO, Asset("1.3.0"))) // 0 BTS
|
|
.setAccountToUpgrade(mUserAccount).build()
|
|
|
|
val operations = ArrayList<BaseOperation>()
|
|
operations.add(operation)
|
|
|
|
val currentPrivateKey = ECKey.fromPrivate(
|
|
DumpedPrivateKey.fromBase58(null, privateKey).key.privKeyBytes)
|
|
ltmTransaction = Transaction(currentPrivateKey, null, operations)
|
|
|
|
val id = mNetworkService?.sendMessage(GetDynamicGlobalProperties(), GetDynamicGlobalProperties.REQUIRED_API)
|
|
if (id != null) responseMap.append(id, RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_LTM)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun showRemoveAccountDialog() {
|
|
context?.let { context ->
|
|
MaterialDialog(context).show {
|
|
title(R.string.title__remove_account)
|
|
message(R.string.msg__remove_account_confirmation)
|
|
negativeButton(android.R.string.cancel)
|
|
positiveButton(android.R.string.ok) {
|
|
removeAccount(it.context)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun removeAccount(context: Context) {
|
|
// Clears the database.
|
|
mViewModel.clearDatabase(context)
|
|
|
|
// Clears the shared preferences.
|
|
val pref = PreferenceManager.getDefaultSharedPreferences(context)
|
|
pref.edit().clear().apply()
|
|
|
|
// Marks the license as agreed, so that it is not shown to the user again.
|
|
pref.edit().putInt(
|
|
Constants.KEY_LAST_AGREED_LICENSE_VERSION, Constants.CURRENT_LICENSE_VERSION).apply()
|
|
|
|
// Restarts the activity, which will restart the whole application since it uses a
|
|
// single activity architecture.
|
|
val intent = activity?.intent
|
|
activity?.finish()
|
|
activity?.startActivity(intent)
|
|
}
|
|
}
|
|
|