2019-12-23 11:16:00 -08:00
|
|
|
package cash.z.ecc.android.ui.send
|
|
|
|
|
|
|
|
import android.content.ClipboardManager
|
|
|
|
import android.content.Context
|
2020-01-09 23:53:16 -08:00
|
|
|
import android.content.res.ColorStateList
|
2019-12-23 11:16:00 -08:00
|
|
|
import android.os.Bundle
|
|
|
|
import android.view.LayoutInflater
|
|
|
|
import android.view.View
|
2020-01-08 00:45:45 -08:00
|
|
|
import android.widget.EditText
|
2020-01-09 23:53:16 -08:00
|
|
|
import androidx.core.widget.doAfterTextChanged
|
2019-12-23 11:16:00 -08:00
|
|
|
import cash.z.ecc.android.R
|
|
|
|
import cash.z.ecc.android.databinding.FragmentSendAddressBinding
|
2020-01-08 00:45:45 -08:00
|
|
|
import cash.z.ecc.android.di.viewmodel.activityViewModel
|
|
|
|
import cash.z.ecc.android.ext.*
|
2020-02-21 15:52:57 -08:00
|
|
|
import cash.z.ecc.android.feedback.Report
|
|
|
|
import cash.z.ecc.android.feedback.Report.Funnel.Send
|
|
|
|
import cash.z.ecc.android.feedback.Report.Tap.*
|
2019-12-23 11:16:00 -08:00
|
|
|
import cash.z.ecc.android.ui.base.BaseFragment
|
2020-06-10 04:49:38 -07:00
|
|
|
import cash.z.ecc.android.sdk.Synchronizer
|
|
|
|
import cash.z.ecc.android.sdk.block.CompactBlockProcessor.WalletBalance
|
|
|
|
import cash.z.ecc.android.sdk.ext.*
|
|
|
|
import cash.z.ecc.android.sdk.validate.AddressType
|
2020-01-08 00:45:45 -08:00
|
|
|
import kotlinx.coroutines.delay
|
|
|
|
import kotlinx.coroutines.launch
|
2019-12-23 11:16:00 -08:00
|
|
|
|
|
|
|
class SendAddressFragment : BaseFragment<FragmentSendAddressBinding>(),
|
|
|
|
ClipboardManager.OnPrimaryClipChangedListener {
|
2020-02-21 15:52:57 -08:00
|
|
|
override val screen = Report.Screen.SEND_ADDRESS
|
2019-12-23 11:16:00 -08:00
|
|
|
|
2020-01-09 23:53:16 -08:00
|
|
|
private var maxZatoshi: Long? = null
|
|
|
|
|
2020-01-08 00:45:45 -08:00
|
|
|
val sendViewModel: SendViewModel by activityViewModel()
|
2020-01-05 21:01:06 -08:00
|
|
|
|
2019-12-23 11:16:00 -08:00
|
|
|
override fun inflate(inflater: LayoutInflater): FragmentSendAddressBinding =
|
|
|
|
FragmentSendAddressBinding.inflate(inflater)
|
|
|
|
|
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
|
|
super.onViewCreated(view, savedInstanceState)
|
2020-02-21 15:52:57 -08:00
|
|
|
binding.backButtonHitArea.onClickNavTo(R.id.action_nav_send_address_to_nav_home) { tapped(SEND_ADDRESS_BACK) }
|
2019-12-23 11:16:00 -08:00
|
|
|
binding.buttonNext.setOnClickListener {
|
2020-02-21 15:52:57 -08:00
|
|
|
onSubmit().also { tapped(SEND_ADDRESS_NEXT) }
|
2019-12-23 11:16:00 -08:00
|
|
|
}
|
|
|
|
binding.textBannerAction.setOnClickListener {
|
2020-02-21 15:52:57 -08:00
|
|
|
onPaste().also { tapped(SEND_ADDRESS_PASTE) }
|
2019-12-23 11:16:00 -08:00
|
|
|
}
|
|
|
|
binding.textBannerMessage.setOnClickListener {
|
2020-02-21 15:52:57 -08:00
|
|
|
onPaste().also { tapped(SEND_ADDRESS_PASTE) }
|
2019-12-23 11:16:00 -08:00
|
|
|
}
|
2020-01-09 23:53:16 -08:00
|
|
|
binding.textMax.setOnClickListener {
|
2020-02-21 15:52:57 -08:00
|
|
|
onMax().also { tapped(SEND_ADDRESS_MAX) }
|
2020-01-09 23:53:16 -08:00
|
|
|
}
|
2020-01-08 00:45:45 -08:00
|
|
|
|
|
|
|
// Apply View Model
|
|
|
|
if (sendViewModel.zatoshiAmount > 0L) {
|
2020-07-02 04:40:44 -07:00
|
|
|
sendViewModel.zatoshiAmount.convertZatoshiToZecStringUniform(8).let { amount ->
|
2020-01-08 00:45:45 -08:00
|
|
|
binding.inputZcashAmount.setText(amount)
|
2019-12-23 11:16:00 -08:00
|
|
|
}
|
2020-01-08 00:45:45 -08:00
|
|
|
} else {
|
|
|
|
binding.inputZcashAmount.setText(null)
|
|
|
|
}
|
2020-01-09 23:53:16 -08:00
|
|
|
if (!sendViewModel.toAddress.isNullOrEmpty()) {
|
2020-01-08 00:45:45 -08:00
|
|
|
binding.inputZcashAddress.setText(sendViewModel.toAddress)
|
|
|
|
} else {
|
|
|
|
binding.inputZcashAddress.setText(null)
|
2019-12-23 11:16:00 -08:00
|
|
|
}
|
2020-01-08 00:45:45 -08:00
|
|
|
|
2020-02-21 15:52:57 -08:00
|
|
|
binding.inputZcashAddress.onEditorActionDone(::onSubmit).also { tapped(SEND_ADDRESS_DONE_ADDRESS) }
|
|
|
|
binding.inputZcashAmount.onEditorActionDone(::onSubmit).also { tapped(SEND_ADDRESS_DONE_AMOUNT) }
|
2020-01-08 00:45:45 -08:00
|
|
|
|
2020-06-28 07:35:07 -07:00
|
|
|
binding.inputZcashAmount.limitDecimalPlaces(8)
|
|
|
|
|
2020-01-09 23:53:16 -08:00
|
|
|
binding.inputZcashAddress.apply {
|
|
|
|
doAfterTextChanged {
|
2020-06-27 13:56:03 -07:00
|
|
|
val textStr = text.toString()
|
|
|
|
val trim = textStr.trim()
|
2020-01-13 17:58:09 -08:00
|
|
|
if (text.toString() != trim) {
|
2020-06-27 13:56:03 -07:00
|
|
|
val textView = binding.inputZcashAddress.findViewById<EditText>(R.id.input_zcash_address)
|
|
|
|
val cursorPosition = textView.selectionEnd;
|
|
|
|
textView.setText(trim)
|
|
|
|
textView.setSelection(cursorPosition-(textStr.length-trim.length))
|
2020-01-13 17:58:09 -08:00
|
|
|
}
|
|
|
|
onAddressChanged(trim)
|
2020-01-09 23:53:16 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
binding.textLayoutAddress.setEndIconOnClickListener {
|
2020-02-21 15:52:57 -08:00
|
|
|
mainActivity?.maybeOpenScan().also { tapped(SEND_ADDRESS_SCAN) }
|
2020-01-09 23:53:16 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun onAddressChanged(address: String) {
|
|
|
|
resumedScope.launch {
|
|
|
|
var type = when (sendViewModel.validateAddress(address)) {
|
2020-06-10 04:49:38 -07:00
|
|
|
is AddressType.Transparent -> "This is a valid transparent address" to R.color.zcashGreen
|
|
|
|
is AddressType.Shielded -> "This is a valid shielded address" to R.color.zcashGreen
|
|
|
|
is AddressType.Invalid -> "This address appears to be invalid" to R.color.zcashRed
|
2020-01-09 23:53:16 -08:00
|
|
|
}
|
|
|
|
if (address == sendViewModel.synchronizer.getAddress()) type =
|
|
|
|
"Warning, this appears to be your address!" to R.color.zcashRed
|
|
|
|
binding.textLayoutAddress.helperText = type.first
|
|
|
|
binding.textLayoutAddress.setHelperTextColor(ColorStateList.valueOf(type.second.toAppColor()))
|
|
|
|
}
|
2019-12-23 11:16:00 -08:00
|
|
|
}
|
|
|
|
|
2020-01-08 00:45:45 -08:00
|
|
|
|
|
|
|
private fun onSubmit(unused: EditText? = null) {
|
2020-01-05 21:01:06 -08:00
|
|
|
sendViewModel.toAddress = binding.inputZcashAddress.text.toString()
|
2020-01-08 00:45:45 -08:00
|
|
|
binding.inputZcashAmount.convertZecToZatoshi()?.let { sendViewModel.zatoshiAmount = it }
|
2020-01-09 23:53:16 -08:00
|
|
|
sendViewModel.validate(maxZatoshi).onFirstWith(resumedScope) {
|
2020-01-08 00:45:45 -08:00
|
|
|
if (it == null) {
|
2020-02-21 15:52:57 -08:00
|
|
|
sendViewModel.funnel(Send.AddressPageComplete)
|
2020-02-12 04:58:41 -08:00
|
|
|
mainActivity?.safeNavigate(R.id.action_nav_send_address_to_send_memo)
|
2020-01-08 00:45:45 -08:00
|
|
|
} else {
|
|
|
|
resumedScope.launch {
|
|
|
|
binding.textAddressError.text = it
|
|
|
|
delay(1500L)
|
2020-01-09 23:53:16 -08:00
|
|
|
binding.textAddressError.text = ""
|
2020-01-08 00:45:45 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-23 11:16:00 -08:00
|
|
|
}
|
|
|
|
|
2020-01-09 23:53:16 -08:00
|
|
|
private fun onMax() {
|
|
|
|
if (maxZatoshi != null) {
|
|
|
|
binding.inputZcashAmount.apply {
|
2020-01-13 17:58:09 -08:00
|
|
|
setText(maxZatoshi.convertZatoshiToZecString(8))
|
2020-01-09 23:53:16 -08:00
|
|
|
postDelayed({
|
|
|
|
requestFocus()
|
|
|
|
setSelection(text?.length ?: 0)
|
|
|
|
}, 10L)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-23 11:16:00 -08:00
|
|
|
override fun onAttach(context: Context) {
|
|
|
|
super.onAttach(context)
|
|
|
|
mainActivity?.clipboard?.addPrimaryClipChangedListener(this)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onDetach() {
|
|
|
|
super.onDetach()
|
|
|
|
mainActivity?.clipboard?.removePrimaryClipChangedListener(this)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onResume() {
|
|
|
|
super.onResume()
|
|
|
|
updateClipboardBanner()
|
2020-01-09 23:53:16 -08:00
|
|
|
sendViewModel.synchronizer.balances.collectWith(resumedScope) {
|
|
|
|
onBalanceUpdated(it)
|
|
|
|
}
|
|
|
|
binding.inputZcashAddress.text.toString().let {
|
|
|
|
if (!it.isNullOrEmpty()) onAddressChanged(it)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun onBalanceUpdated(balance: WalletBalance) {
|
|
|
|
binding.textLayoutAmount.helperText =
|
2020-03-26 06:43:28 -07:00
|
|
|
"You have ${balance.availableZatoshi.coerceAtLeast(0L).convertZatoshiToZecString(8)} available"
|
|
|
|
maxZatoshi = (balance.availableZatoshi - ZcashSdk.MINERS_FEE_ZATOSHI).coerceAtLeast(0L)
|
2019-12-23 11:16:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onPrimaryClipChanged() {
|
|
|
|
twig("clipboard changed!")
|
|
|
|
updateClipboardBanner()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun updateClipboardBanner() {
|
|
|
|
binding.groupBanner.goneIf(loadAddressFromClipboard() == null)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun onPaste() {
|
|
|
|
mainActivity?.clipboard?.let { clipboard ->
|
|
|
|
if (clipboard.hasPrimaryClip()) {
|
|
|
|
binding.inputZcashAddress.setText(clipboard.text())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun loadAddressFromClipboard(): String? {
|
|
|
|
mainActivity?.clipboard?.apply {
|
|
|
|
if (hasPrimaryClip()) {
|
|
|
|
text()?.let { text ->
|
|
|
|
if (text.startsWith("zs") && text.length > 70) {
|
|
|
|
return@loadAddressFromClipboard text.toString()
|
|
|
|
}
|
|
|
|
// treat t-addrs differently in the future
|
|
|
|
if (text.startsWith("t1") && text.length > 32) {
|
|
|
|
return@loadAddressFromClipboard text.toString()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun ClipboardManager.text(): CharSequence =
|
|
|
|
primaryClip!!.getItemAt(0).coerceToText(mainActivity)
|
2020-01-05 21:01:06 -08:00
|
|
|
}
|