Add transparent wallet support.

This commit is contained in:
Kevin Gorham 2020-03-26 09:50:42 -04:00
parent 6ee39f1987
commit f7d4663770
No known key found for this signature in database
GPG Key ID: CCA55602DF49FC38
12 changed files with 905 additions and 0 deletions

View File

@ -0,0 +1,371 @@
package cash.z.ecc.android.ui.send
import android.content.ClipboardManager
import android.content.Context
import android.content.res.ColorStateList
import android.os.Bundle
import android.text.Spanned
import android.text.style.ForegroundColorSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.core.view.forEach
import androidx.core.widget.doAfterTextChanged
import cash.z.ecc.android.R
import cash.z.ecc.android.databinding.FragmentSendBinding
import cash.z.ecc.android.di.viewmodel.activityViewModel
import cash.z.ecc.android.ext.*
import cash.z.ecc.android.feedback.Report
import cash.z.ecc.android.feedback.Report.Funnel.Send
import cash.z.ecc.android.feedback.Report.Tap.*
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.wallet.sdk.Synchronizer
import cash.z.wallet.sdk.block.CompactBlockProcessor.WalletBalance
import cash.z.wallet.sdk.ext.*
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class SendFragment : BaseFragment<FragmentSendBinding>(),
ClipboardManager.OnPrimaryClipChangedListener {
override val screen = Report.Screen.SEND
private var maxZatoshi: Long? = null
private var useShieldedFunds: Boolean = true
set(value) {
// if we're switching to shielded then no need to prevent it
// otherwise, do not switch to transparent unless the user agrees to reset the memo
if (value || resetMemo()) {
field = value
sendViewModel.useShieldedFunds = value
applyMemo()
}
}
val sendViewModel: SendViewModel by activityViewModel()
val pasteLimit = 20
override fun inflate(inflater: LayoutInflater): FragmentSendBinding =
FragmentSendBinding.inflate(inflater)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.backButtonHitArea.onClickNavTo(R.id.action_nav_send_to_nav_home) { tapped(SEND_BACK) }
binding.sendButtonHitArea.setOnClickListener {
onSubmit().also { tapped(SEND_NEXT) }
}
// binding.textBannerAction.setOnClickListener {
// onPaste().also { tapped(SEND_PASTE) }
// }
// binding.textBannerMessage.setOnClickListener {
// onPaste().also { tapped(SEND_PASTE) }
// }
binding.textPaste.setOnClickListener {
onPaste().also { tapped(SEND_PASTE) }
}
binding.textMax.setOnClickListener {
onMax().also { tapped(SEND_MAX) }
}
binding.inputZcashAddress.onEditorActionDone(::onSubmit).also { tapped(SEND_DONE_ADDRESS) }
binding.inputZcashAmount.onEditorActionDone(::onSubmit).also { tapped(SEND_DONE_AMOUNT) }
binding.inputZcashAddress.apply {
doAfterTextChanged {
val trim = text.toString().trim()
if (text.toString() != trim) {
binding.inputZcashAddress
.findViewById<EditText>(R.id.input_zcash_address).setText(trim)
}
onAddressChanged(trim)
}
}
binding.inputMemo.doAfterTextChanged {
sendViewModel.memo = binding.inputMemo.text.toString()
updateMemoCount()
}
binding.textLayoutAddress.setEndIconOnClickListener {
mainActivity?.maybeOpenScan().also { tapped(SEND_SCAN) }
}
// new behaviors
binding.boxShieldedFunds.isClickable = true
binding.boxTransparentFunds.isClickable = true
useShieldedFunds = true
binding.boxShieldedFunds.setOnClickListener {
if (!it.isSelected) {
useShieldedFunds = true
}
}
binding.boxTransparentFunds.setOnClickListener {
if (!it.isSelected) {
useShieldedFunds = false
}
}
binding.textSubheader.text.toColoredSpan(R.color.colorPrimaryVariant, "shielded").let { spannable ->
val start = binding.textSubheader.text.indexOf("transparent")
spannable.setSpan(ForegroundColorSpan(R.color.colorSecondaryVariant.toAppColor()), start, start + "transparent".length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
binding.textSubheader.text = spannable
}
// memo
sendViewModel.afterInitFromAddress {
binding.textIncludedAddress.text = "sent from ${sendViewModel.fromAddress}"
}
binding.buttonAddMemo.setOnClickListener {
onAddMemo((maxZatoshi ?: 0L) > 0L)
}
binding.clearMemo.setOnClickListener {
onClearMemo().also { tapped(SEND_MEMO_CLEAR) }
}
binding.checkIncludeAddress.setOnCheckedChangeListener { _, _->
onIncludeAddressInMemo(binding.checkIncludeAddress.isChecked)
}
}
private fun selectGroup(group: ViewGroup, isSelected: Boolean) {
group.isSelected = isSelected
group.forEach {
it.isSelected = isSelected
}
}
private fun onAddressChanged(address: String) {
if (address.length <= pasteLimit) {
updateClipboardBanner()
} else {
sendViewModel.toAddress = binding.inputZcashAddress.text.toString()
resumedScope.launch {
var type = when (sendViewModel.validateAddress(address)) {
is Synchronizer.AddressType.Transparent -> "This is a valid transparent address" to R.color.zcashGreen
is Synchronizer.AddressType.Shielded -> "This is a valid shielded address" to R.color.zcashGreen
is Synchronizer.AddressType.Invalid -> "This address appears to be invalid" to R.color.zcashRed
}
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()))
}
}
}
private fun onSubmit(unused: EditText? = null) {
// TODO: tech debt: improve this logic to be driven by the model rather than this clumsy storage of data in the UI
sendViewModel.toAddress = binding.inputZcashAddress.text.toString()
binding.inputZcashAmount.convertZecToZatoshi()?.let { sendViewModel.zatoshiAmount = it }
sendViewModel.memo = if (useShieldedFunds) binding.inputMemo.text.toString() else ""
sendViewModel.validate(maxZatoshi).onFirstWith(resumedScope) {
if (it == null) {
sendViewModel.funnel(Send.SendPageComplete)
mainActivity?.safeNavigate(R.id.action_nav_send_to_send_confirm)
} else {
resumedScope.launch {
binding.textAddressError.text = it
delay(3000L)
binding.textAddressError.text = ""
}
}
}
}
private fun onMax() {
if (maxZatoshi != null) {
binding.inputZcashAmount.apply {
setText(maxZatoshi.convertZatoshiToZecString(8))
postDelayed({
requestFocus()
setSelection(text?.length ?: 0)
}, 10L)
}
}
}
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()
applyModel()
updateClipboardBanner()
sendViewModel.synchronizer.balances.collectWith(resumedScope) {
onBalanceUpdated(it)
}
}
private fun applyModel() {
useShieldedFunds = sendViewModel.useShieldedFunds
if (sendViewModel.zatoshiAmount > 0L) {
sendViewModel.zatoshiAmount.convertZatoshiToZecString(8).let { amount ->
binding.inputZcashAmount.setText(amount)
}
} else {
binding.inputZcashAmount.setText(null)
}
binding.inputZcashAddress.setText(sendViewModel.toAddress)
applyMemo()
}
fun applyMemo() {
if (sendViewModel.isMemoAdded) {
binding.groupMemo.visibility = View.VISIBLE
binding.buttonAddMemo.visibility = View.GONE
} else {
binding.groupMemo.visibility = View.GONE
binding.buttonAddMemo.visibility = View.VISIBLE
}
selectGroup(binding.boxShieldedFunds, sendViewModel.useShieldedFunds)
selectGroup(binding.boxTransparentFunds, !sendViewModel.useShieldedFunds)
binding.inputMemo.setText(sendViewModel.memo)
binding.checkIncludeAddress.isChecked = sendViewModel.includeFromAddress
binding.textIncludedAddress.goneIf(!sendViewModel.useShieldedFunds || !sendViewModel.includeFromAddress || !sendViewModel.isMemoAdded)
}
private fun onBalanceUpdated(balance: WalletBalance) {
val zecString= balance.availableZatoshi.coerceAtLeast(0L).convertZatoshiToZecString(8)
binding.textLayoutAmount.helperText =
"You have $zecString available"
binding.textShieldedFundsAmount.text = "\$${zecString}"
maxZatoshi = (balance.availableZatoshi - ZcashSdk.MINERS_FEE_ZATOSHI).coerceAtLeast(0L)
}
override fun onPrimaryClipChanged() {
twig("clipboard changed!")
updateClipboardBanner()
}
private fun updateClipboardBanner() {
val invalidAddressOnClipboard = loadAddressFromClipboard() == null
val hidePaste = invalidAddressOnClipboard || (binding.inputZcashAddress.text ?: "").length >= pasteLimit - 1
binding.textPaste.goneIf(hidePaste)
binding.textLayoutAddress.helperText =
"${if (hidePaste) "E" else "Paste or e"}nter a valid Zcash address"
binding.textLayoutAddress.setHelperTextColor(ColorStateList.valueOf(R.color.text_light_dimmed.toAppColor()))
}
private fun onAddMemo(hasShieldedFunds: Boolean) {
if (!useShieldedFunds) {
//TODO: track dialog reference
val builder = MaterialAlertDialogBuilder(mainActivity)
.setTitle("Shielded Transaction Required")
.setMessage("Memos are not supported for transparent transactions. To add a memo, you must send shielded funds.")
.setCancelable(true)
.setPositiveButton("Ok") { dialog, _ ->
dialog.dismiss()
}
if (hasShieldedFunds) {
builder.setNegativeButton("Switch to Shielded") { dialog, _ ->
dialog.dismiss()
useShieldedFunds = true
onAddMemo(hasShieldedFunds)
}
}
builder.show()
} else {
sendViewModel.isMemoAdded = true
applyMemo()
binding.inputMemo.requestFocus()
}
}
private fun onClearMemo() {
sendViewModel.isMemoAdded = false
binding.inputMemo.setText("")
sendViewModel.memo = ""
binding.groupMemo.visibility = View.GONE
binding.buttonAddMemo.visibility = View.VISIBLE
binding.textIncludedAddress.visibility = View.GONE
}
private fun resetMemo(): Boolean {
if (sendViewModel.memo.isNullOrEmpty()) {
onClearMemo()
return true
} else {
MaterialAlertDialogBuilder(mainActivity)
.setTitle("Are you sure?")
.setMessage("Memos are not supported for transparent transactions. If you switch to transparent funds, your memo will be lost.")
.setCancelable(true)
.setPositiveButton("Clear Memo") { dialog, _ ->
dialog.dismiss()
onClearMemo()
useShieldedFunds = false
}
.setNegativeButton("Cancel") { dialog, _ ->
dialog.dismiss()
}
.show()
return false
}
}
private fun onIncludeAddressInMemo(checked: Boolean) {
binding.textIncludedAddress.goneIf(!checked)
sendViewModel.includeFromAddress = checked
if (checked) {
tapped(SEND_MEMO_INCLUDE)
// getString(R.string.send_memo_included_message)
} else {
tapped(SEND_MEMO_EXCLUDE)
// getString(R.string.send_memo_excluded_message)
}
updateMemoCount()
}
private fun updateMemoCount() {
var count = sendViewModel.createMemoToSend().length
binding.textMemoCount.text = "$count/512"
val color = if (count > 512) R.color.zcashRed else R.color.text_light_dimmed
binding.textMemoCount.setTextColor(color.toAppColor())
}
private fun onPaste() {
mainActivity?.clipboard?.let { clipboard ->
if (clipboard.hasPrimaryClip()) {
binding.inputZcashAddress.setText(clipboard.text())
}
}
binding.textPaste.visibility = View.GONE
}
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)
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="false" android:color="@color/background_unselected_stroke"/>
<item android:state_pressed="false" android:color="@color/colorPrimary"/>
<item android:state_pressed="true" android:color="@color/colorPrimaryVariant" />
</selector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="false" android:color="@color/background_unselected_stroke"/>
<item android:state_pressed="false" android:color="@color/colorSecondary"/>
<item android:state_pressed="true" android:color="@color/colorSecondaryVariant" />
</selector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="false" android:color="@color/colorPrimaryVariant"/>
<item android:state_pressed="false" android:color="@color/colorPrimary"/>
<item android:state_pressed="true" android:color="@color/colorPrimaryVariant" />
</selector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="false" android:color="@color/colorSecondaryVariant"/>
<item android:state_pressed="false" android:color="@color/colorSecondary"/>
<item android:state_pressed="true" android:color="@color/colorSecondaryVariant" />
</selector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="false" android:color="@color/text_light_dimmed"/>
<item android:state_pressed="false" android:color="@color/text_light"/>
<item android:state_pressed="true" android:color="@color/text_light_dimmed" />
</selector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="0dp" />
<stroke android:width="1dp" android:color="@color/selector_button_border_primary"/>
</shape>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="0dp" />
<stroke android:width="1dp" android:color="@color/selector_button_border_secondary"/>
</shape>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M13,7h-2v4L7,11v2h4v4h2v-4h4v-2h-4L13,7zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>

View File

@ -0,0 +1,480 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scroll_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/background_home"
android:paddingBottom="500dp">
<!-- Back Button -->
<ImageView
android:id="@+id/back_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.05"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.065"
app:srcCompat="@drawable/ic_arrow_back_black_24dp"
app:tint="@color/text_light" />
<View
android:id="@+id/back_button_hit_area"
android:layout_width="56dp"
android:layout_height="56dp"
android:clickable="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.01"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.045" />
<!-- send Button -->
<ImageView
android:id="@+id/send_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.95"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.065"
app:srcCompat="@drawable/ic_send_24dp"
app:tint="@color/text_light" />
<View
android:id="@+id/send_button_hit_area"
android:layout_width="56dp"
android:layout_height="56dp"
android:clickable="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.99"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.045" />
<TextView
android:id="@+id/text_header"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:autoSizeTextType="uniform"
android:fontFamily="sans-serif-thin"
android:maxLines="1"
android:text="We need some details!"
android:textColor="@color/text_light"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="@+id/text_layout_amount"
app:layout_constraintStart_toStartOf="@+id/text_layout_amount"
app:layout_constraintTop_toBottomOf="@id/back_button_hit_area" />
<TextView
android:id="@+id/text_subheader"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:autoSizeTextType="uniform"
android:maxLines="2"
android:text="This wallet has both a shielded and a transparent address.\nPlease fill out all the transaction details and click send above."
android:textColor="@color/text_light_dimmed"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="@+id/text_layout_amount"
app:layout_constraintStart_toStartOf="@+id/text_layout_amount"
app:layout_constraintTop_toBottomOf="@id/text_header" />
<!-- Shielded Funds Box -->
<LinearLayout
android:id="@+id/box_shielded_funds"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:background="@drawable/background_button_square_primary"
android:orientation="vertical"
android:paddingBottom="12dp"
android:paddingTop="8dp"
app:layout_constraintEnd_toStartOf="@id/box_transparent_funds"
app:layout_constraintStart_toStartOf="@id/text_layout_amount"
app:layout_constraintTop_toBottomOf="@id/text_subheader">
<TextView
android:id="@+id/text_shielded_funds_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Shielded Funds"
android:textColor="@color/selector_button_text_primary" />
<TextView
android:id="@+id/text_shielded_funds_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:autoSizeTextType="uniform"
android:fontFamily="@font/zboto"
android:maxLines="1"
android:text="$20.94"
android:textColor="@color/selector_selectable_text_light"
android:textSize="20sp" />
</LinearLayout>
<!-- Transparent Funds Box -->
<LinearLayout
android:id="@+id/box_transparent_funds"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:background="@drawable/background_button_square_secondary"
android:orientation="vertical"
android:paddingBottom="12dp"
android:paddingTop="8dp"
app:layout_constraintEnd_toEndOf="@id/text_layout_address"
app:layout_constraintStart_toEndOf="@id/box_shielded_funds"
app:layout_constraintTop_toTopOf="@id/box_shielded_funds">
<TextView
android:id="@+id/text_transparent_funds_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Transparent Funds"
android:textColor="@color/selector_button_text_secondary" />
<TextView
android:id="@+id/text_transparent_funds_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:autoSizeTextType="uniform"
android:fontFamily="@font/zboto"
android:maxLines="1"
android:text="$12.42785435"
android:textColor="@color/selector_selectable_text_light"
android:textSize="20sp" />
</LinearLayout>
<!-- Input: Amount -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/text_layout_amount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:hint="Amount"
android:theme="@style/Zcash.Overlay.TextInputLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/box_shielded_funds"
app:layout_constraintWidth_percent="0.84"
tools:helperText="You have 23.23 ZEC available">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/input_zcash_amount"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:imeOptions="actionDone"
android:inputType="numberDecimal"
android:maxLength="20"
android:singleLine="true"
android:textColor="@color/text_light"
android:textColorHint="@color/text_light_dimmed" />
</com.google.android.material.textfield.TextInputLayout>
<!-- Input: Address -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/text_layout_address"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:hint="To"
android:theme="@style/Zcash.Overlay.TextInputLayout"
app:endIconDrawable="@drawable/ic_qrcode_24dp"
app:endIconMode="custom"
app:helperText="Paste or enter a valid Zcash address"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_layout_amount"
app:layout_constraintWidth_percent="0.84">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/input_zcash_address"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:imeOptions="actionDone"
android:inputType="textNoSuggestions"
android:maxLength="255"
android:singleLine="true"
android:textColor="@color/text_light"
android:textColorHint="@color/text_light_dimmed" />
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/button_add_memo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_add_circle_outline_24dp"
android:paddingBottom="16dp"
android:paddingEnd="16dp"
android:paddingStart="4dp"
android:paddingTop="16dp"
android:text="Add Memo"
android:textAllCaps="true"
android:textColor="@color/text_light"
app:layout_constraintStart_toStartOf="@id/text_layout_address"
app:layout_constraintTop_toBottomOf="@id/text_layout_address"
tools:visibility="gone" />
<TextView
android:id="@+id/text_max"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:fontFamily="@font/inconsolata"
android:padding="16dp"
android:text="MAX"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/text_layout_amount"
app:layout_constraintEnd_toEndOf="@id/text_layout_amount"
app:layout_constraintTop_toTopOf="@id/text_layout_amount" />
<TextView
android:id="@+id/text_address_error"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:autoSizeTextType="uniform"
android:fontFamily="@font/inconsolata"
android:gravity="center"
android:maxLines="2"
android:paddingEnd="4dp"
android:paddingStart="4dp"
android:textColor="@color/zcashRed"
android:textSize="12dp"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="@+id/back_button"
app:layout_constraintEnd_toStartOf="@id/send_button"
app:layout_constraintStart_toEndOf="@+id/back_button"
tools:text="Please enter short msg" />
<!-- Scan QR code -->
<ImageView
android:id="@+id/image_scan_qr"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="24dp"
android:paddingEnd="1dp"
android:paddingStart="6dp"
android:paddingTop="10dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/text_layout_address"
app:layout_constraintEnd_toEndOf="@id/text_layout_address"
app:layout_constraintTop_toTopOf="@id/text_layout_address"
app:srcCompat="@drawable/ic_qrcode_24dp"
app:tint="@color/zcashWhite" />
<TextView
android:id="@+id/text_paste"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:fontFamily="@font/inconsolata"
android:padding="16dp"
android:text="PASTE"
android:textColor="@color/text_light_dimmed"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/text_layout_address"
app:layout_constraintEnd_toEndOf="@id/text_layout_address"
app:layout_constraintHorizontal_bias="0.84"
app:layout_constraintStart_toStartOf="@id/text_layout_address"
app:layout_constraintTop_toTopOf="@id/text_layout_address"
app:layout_constraintVertical_bias="0.3" />
<!-- <com.google.android.material.button.MaterialButton-->
<!-- android:id="@+id/button_next"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginTop="16dp"-->
<!-- android:text="Next"-->
<!-- android:textColor="@color/text_dark"-->
<!-- app:layout_constraintEnd_toEndOf="@+id/text_layout_address"-->
<!-- app:layout_constraintTop_toBottomOf="@+id/text_layout_amount" />-->
<!-- -->
<!-- Banner -->
<!-- -->
<!-- <TextView-->
<!-- android:id="@+id/text_banner_message"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:background="@drawable/background_banner"-->
<!-- android:elevation="6dp"-->
<!-- android:paddingBottom="8dp"-->
<!-- android:paddingStart="16dp"-->
<!-- android:paddingTop="8dp"-->
<!-- android:text="Address on clipboard!"-->
<!-- android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"-->
<!-- android:textColor="@color/text_light"-->
<!-- android:visibility="invisible"-->
<!-- app:layout_constraintEnd_toEndOf="@+id/text_layout_address"-->
<!-- app:layout_constraintHorizontal_bias="0.0"-->
<!-- app:layout_constraintStart_toStartOf="@+id/text_layout_address"-->
<!-- app:layout_constraintTop_toBottomOf="@id/back_button_hit_area" />-->
<!-- <TextView-->
<!-- android:id="@+id/text_banner_action"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginEnd="16dp"-->
<!-- android:elevation="6dp"-->
<!-- android:text="Paste"-->
<!-- android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"-->
<!-- android:textColor="@color/colorPrimary"-->
<!-- android:visibility="invisible"-->
<!-- app:layout_constraintBaseline_toBaselineOf="@id/text_banner_message"-->
<!-- app:layout_constraintEnd_toEndOf="@id/text_banner_message" />-->
<!-- -->
<!-- Memo -->
<!-- -->
<View
android:id="@+id/background_memo"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="32dp"
android:background="@drawable/background_banner"
android:elevation="6dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.24"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_layout_address"
app:layout_constraintWidth_percent="0.84" />
<EditText
android:id="@+id/input_memo"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/transparent"
android:elevation="6dp"
android:gravity="top"
android:hint="Add a memo here"
android:imeActionLabel="add memo"
android:imeOptions="actionDone"
android:inputType="textMultiLine"
android:maxLength="512"
android:paddingBottom="8dp"
android:paddingEnd="32dp"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/text_light"
app:layout_constraintBottom_toTopOf="@id/text_included_address"
app:layout_constraintEnd_toEndOf="@id/background_memo"
app:layout_constraintStart_toStartOf="@id/background_memo"
app:layout_constraintTop_toTopOf="@id/background_memo"
tools:text="this\nis\nsome\ntext\nthat\nspans\nmany\nlines" />
<TextView
android:id="@+id/text_included_address"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:elevation="6dp"
android:paddingBottom="8dp"
android:paddingEnd="16dp"
android:paddingStart="16dp"
android:textColor="@color/text_light_dimmed"
app:layout_constraintBottom_toBottomOf="@id/background_memo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintWidth_percent="0.84"
tools:text="sent from z23lk4jjl2k3jl43kkj542l3kl4hj2l3k1j41l2kjk423lkj423lklhk2jrhiuhrh2j4hh2hkj23hkj4" />
<ImageView
android:id="@+id/clear_memo"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
android:elevation="6dp"
android:src="@drawable/ic_close_black_24dp"
app:layout_constraintEnd_toEndOf="@id/background_memo"
app:layout_constraintTop_toTopOf="@id/background_memo"
app:tint="@color/text_light" />
<CheckBox
android:id="@+id/check_include_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:padding="0dp"
android:text="Include your sending address in memo"
app:layout_constraintStart_toStartOf="@+id/background_memo"
app:layout_constraintTop_toBottomOf="@+id/background_memo" />
<TextView
android:id="@+id/text_memo_count"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:autoSizeTextType="uniform"
android:maxLines="2"
android:text="0/512"
android:textColor="@color/text_light_dimmed"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="@id/background_memo"
app:layout_constraintTop_toBottomOf="@id/background_memo" />
<!-- <TextView-->
<!-- android:id="@+id/text_info_shielded"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginTop="16dp"-->
<!-- android:gravity="center"-->
<!-- android:text="Your transaction is shielded and your address is not available to recipient."-->
<!-- app:layout_constraintEnd_toEndOf="@id/background_memo"-->
<!-- app:layout_constraintStart_toStartOf="@id/background_memo"-->
<!-- app:layout_constraintTop_toBottomOf="@id/check_include_address" />-->
<!-- <androidx.constraintlayout.widget.Group-->
<!-- android:id="@+id/group_banner"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:visibility="visible"-->
<!-- app:constraint_referenced_ids="text_banner_message, text_banner_action"-->
<!-- tools:visibility="visible" />-->
<androidx.constraintlayout.widget.Group
android:id="@+id/group_memo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="check_include_address, background_memo, input_memo, clear_memo, text_memo_count"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -8,8 +8,11 @@
<!-- colors here should map to a material design value -->
<color name="colorPrimary">@color/zcashYellow</color>
<color name="colorPrimaryVariant">#80FFB900</color>
<color name="colorPrimaryMedium">#805E08</color>
<color name="colorPrimaryDark">#4D3805</color>
<color name="colorSecondary">#4A90E2</color>
<color name="colorSecondaryVariant">#804A90E2</color>
<color name="colorAccent">#A1A1A1</color>
<color name="colorSurface">@color/text_light</color>
<color name="colorOnSurface">@color/background_banner</color>
@ -59,6 +62,7 @@
<color name="background_banner">@color/zcashBlack_dark</color>
<color name="background_banner_stroke">#282828</color>
<color name="background_unselected_stroke">@color/zcashBlack_54</color>
<color name="scan_overlay_background">@color/zcashBlack_87</color>
<color name="spacer">#1FBB666A</color>
<color name="text_send_amount_disabled">@color/text_light</color>