forked from hush/SilentDragonAndroid-old
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
13 KiB
314 lines
13 KiB
// Copyright 2019-2020 The Hush developers
|
|
package org.myhush.silentdragon
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.app.Activity
|
|
import android.app.AlertDialog
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.os.Bundle
|
|
import android.os.Handler
|
|
import android.provider.ContactsContract
|
|
import androidx.core.content.ContextCompat
|
|
import androidx.appcompat.app.AppCompatActivity
|
|
import android.text.Editable
|
|
import android.text.InputType
|
|
import android.text.SpannableStringBuilder
|
|
import android.text.TextWatcher
|
|
import android.view.inputmethod.EditorInfo
|
|
import android.view.inputmethod.InputMethodManager
|
|
import android.widget.TextView
|
|
import android.widget.Toast
|
|
import com.beust.klaxon.Klaxon
|
|
import kotlinx.android.synthetic.main.activity_send.*
|
|
import kotlinx.android.synthetic.main.content_send.*
|
|
import java.text.DecimalFormat
|
|
|
|
|
|
class SendActivity : AppCompatActivity() {
|
|
|
|
private val REQUEST_CONFIRM = 2
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
setContentView(R.layout.activity_send)
|
|
setSupportActionBar(toolbar)
|
|
|
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
|
|
title = getString(R.string.send_transaction)
|
|
|
|
// Clear the valid address prompt
|
|
txtValidAddress.text = ""
|
|
txtSendCurrencySymbol.text = ""
|
|
|
|
if (intent.getStringExtra("address") != null)
|
|
sendAddress.setText(intent.getStringExtra("address"), TextView.BufferType.EDITABLE)
|
|
|
|
if (intent.getDoubleExtra("amount", -1.0) > 0)
|
|
setAmountHUSH(intent.getDoubleExtra("amount", 0.0))
|
|
|
|
if (intent.getBooleanExtra("includeReplyTo", false))
|
|
chkIncludeReplyTo.isChecked = true
|
|
|
|
imageButton.setOnClickListener { view ->
|
|
val intent = Intent(this, QrReaderActivity::class.java)
|
|
intent.putExtra("REQUEST_CODE",
|
|
QrReaderActivity.REQUEST_ADDRESS
|
|
)
|
|
startActivityForResult(intent,
|
|
QrReaderActivity.REQUEST_ADDRESS
|
|
)
|
|
}
|
|
|
|
if (DataModel.currencyValues["USD"] == null)
|
|
ConnectionManager.initCurrencies()
|
|
|
|
if (DataModel.selectedCurrency == "BTC")
|
|
amountUSD.text = "${DataModel.currencySymbols[DataModel.selectedCurrency]} " + DecimalFormat("0.00000000").format(0)
|
|
else
|
|
{
|
|
amountUSD.text = "${DataModel.currencySymbols[DataModel.selectedCurrency]} " + DecimalFormat("0.00").format(0)
|
|
}
|
|
|
|
textViewFee.text = DecimalFormat("0.0000").format(0.0001) + " HUSH"
|
|
|
|
sendAddress.addTextChangedListener(object : TextWatcher {
|
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
|
override fun afterTextChanged(s: Editable?) {}
|
|
|
|
@SuppressLint("SetTextI18n")
|
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
|
if (DataModel.isValidAddress(s.toString())) {
|
|
txtValidAddress.text = "\u2713 " + getString(R.string.valid_address)
|
|
txtValidAddress.setTextColor(ContextCompat.getColor(applicationContext,
|
|
R.color.white_selected
|
|
))
|
|
} else {
|
|
txtValidAddress.text = getString(R.string.not_a_valid_hush_address)
|
|
txtValidAddress.setTextColor(ContextCompat.getColor(applicationContext, R.color.colorAccent))
|
|
}
|
|
|
|
if (s?.startsWith("R") == true) {
|
|
txtSendMemo.isEnabled = false
|
|
chkIncludeReplyTo.isEnabled = false
|
|
txtSendMemo.text = SpannableStringBuilder("")
|
|
txtSendMemoTitle.text = getString(R.string.no_memo_for_taddresses)
|
|
} else {
|
|
txtSendMemo.isEnabled = true
|
|
chkIncludeReplyTo.isEnabled = true
|
|
txtSendMemoTitle.text = getString(R.string.memo_optional)
|
|
}
|
|
}
|
|
})
|
|
|
|
amountHUSH.addTextChangedListener(object : TextWatcher {
|
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
|
override fun afterTextChanged(s: Editable?) {}
|
|
|
|
@SuppressLint("SetTextI18n")
|
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
|
val hush = s.toString().toDoubleOrNull()
|
|
val price = DataModel.currencyValues[DataModel.selectedCurrency]
|
|
val symbol = DataModel.currencySymbols[DataModel.selectedCurrency]
|
|
|
|
if (hush == null)
|
|
txtSendCurrencySymbol.text = "" // Let the placeholder show
|
|
else {
|
|
txtSendCurrencySymbol.text = "HUSH"
|
|
}
|
|
|
|
if (hush == null || price == null)
|
|
if (symbol == "BTC")
|
|
amountUSD.text = "$symbol " + DecimalFormat("0.00000000").format(0)
|
|
else {
|
|
amountUSD.text = "$symbol " + DecimalFormat("0.00").format(0)
|
|
}
|
|
else
|
|
if (symbol == "BTC")
|
|
amountUSD.text = "$symbol " + DecimalFormat("#,##0.00000000").format(hush * price)
|
|
else {
|
|
amountUSD.text = "$symbol " + DecimalFormat("#,##0.00").format(hush * price)
|
|
}
|
|
}
|
|
})
|
|
|
|
txtSendMemo.imeOptions = EditorInfo.IME_ACTION_DONE
|
|
txtSendMemo.setRawInputType(InputType.TYPE_CLASS_TEXT)
|
|
|
|
txtSendMemo.addTextChangedListener(object : TextWatcher {
|
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
|
override fun afterTextChanged(s: Editable?) {}
|
|
|
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
|
txtMemoSize.text = "${s?.length ?: 0} / 512"
|
|
if (s?.length ?: 0 > 512) {
|
|
txtMemoSize.setTextColor(ContextCompat.getColor(applicationContext,
|
|
R.color.colorAccent
|
|
))
|
|
} else {
|
|
txtMemoSize.setTextColor(ContextCompat.getColor(applicationContext,
|
|
R.color.white_selected
|
|
))
|
|
}
|
|
}
|
|
})
|
|
|
|
btnSend.setOnClickListener { view ->
|
|
doValidationsThenConfirm()
|
|
}
|
|
}
|
|
|
|
private fun doValidationsThenConfirm() {
|
|
// First, check if the address is correct.
|
|
val toAddr = sendAddress.text.toString()
|
|
if (!DataModel.isValidAddress(toAddr)) {
|
|
showErrorDialog(getString(R.string.invalid_destination_Hush_address))
|
|
return
|
|
}
|
|
|
|
// Then if the amount is valid
|
|
val amt = amountHUSH.text.toString()
|
|
|
|
// amount=0 xtns are valid
|
|
if (amt.toDoubleOrNull() == null || amt.toDouble() < 0.0 ) {
|
|
showErrorDialog(getString(R.string.invalid_amount))
|
|
return
|
|
}
|
|
|
|
println("Maxzspendable ${DataModel.mainResponseData?.maxzspendable}")
|
|
|
|
// Check if this is more than the maxzspendable
|
|
if (DataModel.mainResponseData?.maxzspendable != null) {
|
|
if (amt.toDouble() > DataModel.mainResponseData?.maxzspendable!! &&
|
|
amt.toDouble() <= DataModel.mainResponseData?.maxspendable ?: Double.MAX_VALUE) {
|
|
|
|
val alertDialog = AlertDialog.Builder(this@SendActivity)
|
|
alertDialog.setTitle(getString(R.string.send_from_taddr))
|
|
//alertDialog.setMessage("$amt ${DataModel.mainResponseData?.tokenName}" + "..."
|
|
alertDialog.setMessage(getString(R.string.more_than_shielded_address, amt, DataModel.mainResponseData?.tokenName))
|
|
alertDialog.apply {
|
|
setPositiveButton(getString(R.string.send_anyway)) { dialog, id -> doConfirm() }
|
|
setNegativeButton(getString(R.string.cancel)) { dialog, id -> dialog.cancel() }
|
|
}
|
|
|
|
alertDialog.create().show()
|
|
return
|
|
}
|
|
}
|
|
|
|
// Warning if spending more than total
|
|
if (amt.toDouble() > DataModel.mainResponseData?.maxspendable ?: Double.MAX_VALUE) {
|
|
//showErrorDialog("Can't spend more than ${DataModel.mainResponseData?.tokenName} " + "${DataModel.mainResponseData?.maxspendable} in a single Tx")
|
|
showErrorDialog(getString(R.string.max_spend_in_a_single_tx, DataModel.mainResponseData?.maxspendable, DataModel.mainResponseData?.tokenName ))
|
|
return
|
|
}
|
|
|
|
val memo = txtSendMemo.text.toString() + getReplyToAddressIfChecked(toAddr)
|
|
if (memo.length > 512) {
|
|
showErrorDialog(getString(R.string.memo_field_over_512))
|
|
return
|
|
}
|
|
|
|
if (toAddr.startsWith("R") && !memo.isBlank()) {
|
|
showErrorDialog(getString(R.string.cant_send_a_memo_to_a_taddr))
|
|
return
|
|
}
|
|
|
|
doConfirm()
|
|
}
|
|
|
|
private fun doConfirm() {
|
|
val toAddr = sendAddress.text.toString()
|
|
val amt = amountHUSH.text.toString()
|
|
val memo = txtSendMemo.text.toString() + getReplyToAddressIfChecked(toAddr)
|
|
|
|
val intent = Intent(this, TxDetailsActivity::class.java)
|
|
val tx = DataModel.TransactionItem(
|
|
"confirm", 0, amt, memo,
|
|
toAddr, "", 0
|
|
)
|
|
intent.putExtra("EXTRA_TXDETAILS", Klaxon().toJsonString(tx))
|
|
startActivityForResult(intent, REQUEST_CONFIRM)
|
|
}
|
|
|
|
private fun getReplyToAddressIfChecked(toAddr: String) : String {
|
|
if (chkIncludeReplyTo.isChecked && toAddr.startsWith("zs1")) {
|
|
return "\n" + getString(R.string.reply_to) + ":\n${DataModel.mainResponseData?.saplingAddress}"
|
|
} else {
|
|
return ""
|
|
}
|
|
}
|
|
|
|
fun showErrorDialog(msg: String) {
|
|
val alertDialog = AlertDialog.Builder(this@SendActivity).create()
|
|
alertDialog.setTitle(getString(R.string.error_sending_transaction))
|
|
alertDialog.setMessage(msg)
|
|
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, getString(R.string.ok)) {
|
|
dialog, _ -> dialog.dismiss() }
|
|
alertDialog.show()
|
|
}
|
|
|
|
private fun Double.format(digits: Int): String? = java.lang.String.format("%.${digits}f", this)
|
|
|
|
private fun setAmountHUSH(amt: Double) {
|
|
amountHUSH.setText((DecimalFormat("#.########").format(amt) + "${DataModel.mainResponseData?.tokenName}"))
|
|
setAmountUSD(amt)
|
|
}
|
|
|
|
private fun setAmountUSD(amt: Double?) {
|
|
if (amt == null) {
|
|
return
|
|
}
|
|
// Since there is a text-change listner on the USD field, we set the USD first, then override the
|
|
// HUSH field manually.
|
|
amountHUSH.setText((DecimalFormat("#.########").format(amt) + "${DataModel.mainResponseData?.tokenName}"))
|
|
Toast.makeText(this.applicationContext, amt.toString(), Toast.LENGTH_SHORT).show()
|
|
amountUSD.text = "${DataModel.currencySymbols[DataModel.selectedCurrency]} " + DecimalFormat("#,##0.00").format(amt)
|
|
}
|
|
|
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
when (requestCode) {
|
|
QrReaderActivity.REQUEST_ADDRESS -> {
|
|
if (resultCode == Activity.RESULT_OK) {
|
|
if (data?.scheme == "hush") {
|
|
sendAddress.setText(data.data?.host ?: "", TextView.BufferType.EDITABLE)
|
|
|
|
var amt = data.data?.getQueryParameter("amt") ?:
|
|
data.data?.getQueryParameter("amount")
|
|
|
|
// Remove all commas.
|
|
amt = amt?.replace(",", ".")
|
|
|
|
if (amt != null) {
|
|
setAmountUSD(amt.toDoubleOrNull())
|
|
}
|
|
|
|
val memo = data.data?.getQueryParameter("memo")
|
|
if (memo != null) {
|
|
txtSendMemo.setText(memo)
|
|
}
|
|
} else {
|
|
sendAddress.setText(data?.dataString ?: "", TextView.BufferType.EDITABLE)
|
|
}
|
|
|
|
amountHUSH.requestFocus()
|
|
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
|
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY)
|
|
}
|
|
}
|
|
REQUEST_CONFIRM -> {
|
|
if (resultCode == Activity.RESULT_OK) {
|
|
// Send async, so that we don't mess up the activity flow
|
|
Handler().post {
|
|
val tx = Klaxon().parse<DataModel.TransactionItem>(data?.dataString!!)
|
|
DataModel.sendTx(tx!!)
|
|
|
|
finish()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|