Skip to content

Commit

Permalink
Merge 77c2e27 into c020816
Browse files Browse the repository at this point in the history
  • Loading branch information
Aldin-SXR committed May 27, 2021
2 parents c020816 + 77c2e27 commit 1b5bb36
Show file tree
Hide file tree
Showing 17 changed files with 941 additions and 68 deletions.
139 changes: 136 additions & 3 deletions android/app/src/main/java/org/electroncash/electroncash3/ColdLoad.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@ package org.electroncash.electroncash3

import android.content.ClipboardManager
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import com.chaquo.python.Kwarg
import com.chaquo.python.PyException
import com.chaquo.python.PyObject
import com.google.zxing.integration.android.IntentIntegrator
import kotlinx.android.synthetic.main.load.*
import java.lang.IllegalArgumentException


val libTransaction by lazy { libMod("transaction") }
Expand All @@ -17,12 +23,13 @@ val libTransaction by lazy { libMod("transaction") }
// Valid transaction quickly show up in transactions.

class ColdLoadDialog : AlertDialogFragment() {

override fun onBuildDialog(builder: AlertDialog.Builder) {
builder.setTitle(R.string.load_transaction)
.setView(R.layout.load)
.setNegativeButton(android.R.string.cancel, null)
.setNeutralButton(R.string.qr_code, null)
.setPositiveButton(R.string.send, null)
.setPositiveButton(R.string.OK, null)
}

override fun onShowDialog() {
Expand All @@ -31,6 +38,7 @@ class ColdLoadDialog : AlertDialogFragment() {
updateUI()

dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { onOK() }
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener { scanQR(this) }
btnPaste.setOnClickListener {
val clipdata = getSystemService(ClipboardManager::class).primaryClip
Expand All @@ -43,22 +51,79 @@ class ColdLoadDialog : AlertDialogFragment() {

private fun updateUI() {
val currenttext = etTransaction.text
val tx: PyObject

//checks if text is blank. further validations can be added here
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = currenttext.isNotBlank()
if (currenttext.isNotBlank()) {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = true
tx = libTransaction.callAttr("Transaction", etTransaction.text.toString())

updateStatusText(tx)

// Check hex transaction signing status
if (canSign(tx)) {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.sign)
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = true
} else if (canBroadcast(tx)) {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.send)
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = true
} else {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false
}
}
}

// Receives the result of a QR scan.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)

// Try to decode the QR content as Base43; if that fails, treat it as is
val txHex: String = try {
baseDecode(result.contents, 43)
} catch (e: PyException) {
result.contents
}

if (result != null && result.contents != null) {
etTransaction.setText(result.contents)
etTransaction.setText(txHex)
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}

fun onOK() {
val tx = libTransaction.callAttr("Transaction", etTransaction.text.toString())

// If transaction can be broadcasted, broadcast it.
// Otherwise, prompt for signing. If the transaction hex is invalid,
// the OK button will be disabled, regardless.
try {
if (canBroadcast(tx)) {
broadcastSignedTransaction(tx)
} else {
signLoadedTransaction()
}
} catch (e: ToastException) {
e.show()
return
}
}

/**
* Sign a loaded transaction.
*/
private fun signLoadedTransaction() {
val dialog = SignPasswordDialog()
dialog.setArguments(Bundle().apply {
putString("tx", etTransaction.text.toString())
})
showDialog(this, dialog)
}

/**
* Broadcast a signed transaction.
*/
private fun broadcastSignedTransaction(tx: PyObject) {
try {
if (!daemonModel.isConnected()) {
throw ToastException(R.string.not_connected)
Expand All @@ -69,4 +134,72 @@ class ColdLoadDialog : AlertDialogFragment() {
dismiss()
} catch (e: ToastException) { e.show() }
}


/**
* Check if a loaded transaction is signed.
* Displays the signing status below the raw TX field.
* (signed, partially signed, empty or invalid)
*/
private fun updateStatusText(tx: PyObject) {
try {
val txInfo = daemonModel.wallet!!.callAttr("get_tx_info", tx)

// Check if the transaction can be processed by this wallet or not
if (txInfo["amount"] == null) {
idTxStatus.setText(R.string.transaction_unrelated)
} else {
idTxStatus.setText(txInfo["status"].toString())
}
} catch (e: PyException) {
idTxStatus.setText(R.string.invalid)
}
}
}

/* Check if the wallet can sign the transaction */
fun canSign(tx: PyObject): Boolean {
return try {
!tx.callAttr("is_complete").toBoolean() and
daemonModel.wallet!!.callAttr("can_sign", tx).toBoolean()
} catch (e: PyException) {
false
}
}

/* Check if the transaction is ready to be broadcasted */
fun canBroadcast(tx: PyObject): Boolean {
return try {
tx.callAttr("is_complete").toBoolean()
} catch (e: PyException) {
false
}
}

/**
* Sign a loaded transaction dialog.
*/
class SignPasswordDialog : PasswordDialog<Unit>() {

val coldLoadDialog by lazy { targetFragment as ColdLoadDialog }
val signSchnorr = daemonModel.walletType == "standard"

val tx by lazy { libTransaction.callAttr("Transaction", arguments!!.getString("tx"),
Kwarg("sign_schnorr", signSchnorr)) }
val wallet = daemonModel.wallet!!

override fun onPassword(password: String) {
wallet.callAttr("sign_transaction", tx, password)

postToUiThread {
coldLoadDialog.etTransaction.setText(tx.toString())
}
}

override fun onPostExecute(result: Unit) {
if (!canBroadcast(tx)) {
coldLoadDialog.dismiss()
copyToClipboard(tx.toString(), R.string.signed_transaction)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,14 @@ class DaemonModel(val config: PyObject) {
val walletName: String?
get() {
val wallet = this.wallet
return if (wallet == null) null else wallet.callAttr("basename").toString()
return wallet?.callAttr("basename")?.toString()
}
val walletType: String?
get() {
return if (wallet == null) null else commands.callAttr("get", "wallet_type").toString()
}
val scriptType: String?
get() = wallet?.get("txin_type").toString()

lateinit var watchdog: Runnable

Expand Down
43 changes: 39 additions & 4 deletions android/app/src/main/java/org/electroncash/electroncash3/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.observe
import com.chaquo.python.Kwarg
import kotlinx.android.synthetic.main.main.*
import kotlinx.android.synthetic.main.show_master_key.walletMasterKey
import kotlinx.android.synthetic.main.wallet_export.*
import kotlinx.android.synthetic.main.wallet_information.*
import kotlinx.android.synthetic.main.wallet_open.*
import kotlinx.android.synthetic.main.wallet_rename.*
import java.io.File
Expand Down Expand Up @@ -146,7 +148,11 @@ class MainActivity : AppCompatActivity(R.layout.main) {
}

fun onCaption(caption: Caption) {
val walletName = caption.walletName ?: app.getString(R.string.No_wallet)
// Get the wallet name + type
val walletName = if (daemonModel.walletType != null) {
caption.walletName + " [${daemonModel.walletType}]"
} else caption.walletName ?: app.getString(R.string.No_wallet)

if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
setTitle(walletName)
supportActionBar!!.setSubtitle(caption.subtitle)
Expand Down Expand Up @@ -231,6 +237,7 @@ class MainActivity : AppCompatActivity(R.layout.main) {
}
R.id.menuChangePassword -> showDialog(this, PasswordChangeDialog())
R.id.menuShowSeed -> { showDialog(this, SeedPasswordDialog()) }
R.id.menuWalletInformation -> { showDialog(this, WalletInformationDialog()) }
R.id.menuExportSigned -> {
try {
showDialog(this, SendDialog().apply {
Expand Down Expand Up @@ -654,15 +661,43 @@ class SeedPasswordDialog : PasswordDialog<SeedResult>() {
}
}


class SeedDialog : AlertDialogFragment() {
override fun onBuildDialog(builder: AlertDialog.Builder) {
builder.setTitle(R.string.Wallet_seed)
.setView(R.layout.wallet_new_2)
.setPositiveButton(android.R.string.ok, null)
.setView(R.layout.wallet_new_2)
.setPositiveButton(android.R.string.ok, null)
}

override fun onShowDialog() {
setupSeedDialog(this)
}
}

class WalletInformationDialog : AlertDialogFragment() {
override fun onBuildDialog(builder: AlertDialog.Builder) {

builder.setView(R.layout.wallet_information)
.setPositiveButton(android.R.string.ok, null)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

fabCopyMasterKey2.setOnClickListener {
val textToCopy = walletMasterKey.text
copyToClipboard(textToCopy, R.string.master_public_key)
}
}

override fun onShowDialog() {
super.onShowDialog()

val masterKey = daemonModel.commands.callAttr("getmpk").toString()
walletMasterKey.setText(masterKey)
walletMasterKey.setFocusable(false)

idWalletName.setText(daemonModel.walletName)
idWalletType.setText(daemonModel.walletType)
idScriptType.setText(daemonModel.scriptType)
}
}
Loading

0 comments on commit 1b5bb36

Please sign in to comment.