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.

315 lines
13 KiB

4 years ago
// 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
5 years ago
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 = ""
5 years ago
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))
5 years ago
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) {
5 years ago
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)
}
}
})
5 years ago
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]
5 years ago
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)
}
}
})
5 years ago
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() {
5 years ago
// 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
5 years ago
}
// Then if the amount is valid
5 years ago
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
5 years ago
}
println("Maxzspendable ${DataModel.mainResponseData?.maxzspendable}")
5 years ago
// 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) {
5 years ago
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))
5 years ago
alertDialog.apply {
setPositiveButton(getString(R.string.send_anyway)) { dialog, id -> doConfirm() }
setNegativeButton(getString(R.string.cancel)) { dialog, id -> dialog.cancel() }
5 years ago
}
alertDialog.create().show()
return
5 years ago
}
}
// 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
5 years ago
}
val memo = txtSendMemo.text.toString() + getReplyToAddressIfChecked(toAddr)
5 years ago
if (memo.length > 512) {
showErrorDialog(getString(R.string.memo_field_over_512))
return
5 years ago
}
if (toAddr.startsWith("R") && !memo.isBlank()) {
showErrorDialog(getString(R.string.cant_send_a_memo_to_a_taddr))
return
5 years ago
}
doConfirm()
}
private fun doConfirm() {
val toAddr = sendAddress.text.toString()
5 years ago
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)
5 years ago
}
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)
5 years ago
}
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
5 years ago
// 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)
5 years ago
}
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) {
5 years ago
// 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!!)
5 years ago
5 years ago
finish()
}
}
}
}
}
}