Home screen improvements and bug fixes.

- Implemented long press to clear amount
- fixed bugs around No Funds being shown when available was zero but change was pending confirmation
- colored the gray z-icon on the home screen
- added color emphasis when change is pending
- added profile button
This commit is contained in:
Kevin Gorham 2020-01-09 11:02:47 -05:00
parent 4c8adf5180
commit d5c8d17c3d
No known key found for this signature in database
GPG Key ID: CCA55602DF49FC38
8 changed files with 80 additions and 20 deletions

View File

@ -0,0 +1,13 @@
package cash.z.ecc.android.ext
import android.text.Spannable
import android.text.Spanned
import android.text.style.ForegroundColorSpan
import androidx.core.text.toSpannable
fun String.toColoredSpan(colorResId: Int, coloredPortion: String): Spannable {
return toSpannable().apply {
val start = this@toColoredSpan.indexOf(coloredPortion)
setSpan(ForegroundColorSpan(colorResId.toAppColor()), start, start + coloredPortion.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}

View File

@ -3,17 +3,19 @@ package cash.z.ecc.android.ui.home
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.widget.TextView
import androidx.core.text.toSpannable
import androidx.lifecycle.lifecycleScope
import cash.z.ecc.android.R
import cash.z.ecc.android.databinding.FragmentHomeBinding
import cash.z.ecc.android.di.viewmodel.activityViewModel
import cash.z.ecc.android.di.viewmodel.viewModel
import cash.z.ecc.android.ext.disabledIf
import cash.z.ecc.android.ext.goneIf
import cash.z.ecc.android.ext.onClickNavTo
import cash.z.ecc.android.ext.*
import cash.z.ecc.android.ext.toAppColor
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.ecc.android.ui.home.HomeFragment.BannerAction.*
import cash.z.ecc.android.ui.send.SendViewModel
@ -87,7 +89,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
buttonNumberPadDecimal.asKey(),
buttonNumberPadBack.asKey()
)
hitAreaReceive.onClickNavTo(R.id.action_nav_home_to_nav_receive)
hitAreaReceive.onClickNavTo(R.id.action_nav_home_to_nav_profile)
iconDetail.onClickNavTo(R.id.action_nav_home_to_nav_detail)
textDetail.onClickNavTo(R.id.action_nav_home_to_nav_detail)
hitAreaScan.onClickNavTo(R.id.action_nav_home_to_nav_scan)
@ -98,17 +100,33 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
buttonSend.setOnClickListener {
onSend()
}
setSendAmount("0")
}
binding.buttonNumberPadBack.setOnLongClickListener {
onClearAmount()
true
}
// if (::uiModel.isInitialized) {
// twig("uiModel exists!")
// onModelUpdated(HomeViewModel.UiModel(), uiModel)
// }
}
private fun onClearAmount() {
repeat(binding.textSendAmount.text.length) {
resumedScope.launch {
_typedChars.send('<')
}
}
}
override fun onResume() {
super.onResume()
viewModel.initialize(typedChars)
twig("HomeFragment.onResume resumeScope.isActive: ${resumedScope.isActive} $resumedScope")
onClearAmount()
viewModel.uiModels.scanReduce { old, new ->
onModelUpdated(old, new)
new
@ -163,8 +181,11 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
}
}
/**
* @param amount the amount to send represented as ZEC, without the dollar sign.
*/
fun setSendAmount(amount: String) {
binding.textSendAmount.text = "\$$amount"
binding.textSendAmount.text = "\$$amount".toColoredSpan(R.color.text_light_dimmed, "$")
sendViewModel.zatoshiAmount = amount.safelyConvertToBigDecimal().convertZecToZatoshi()
binding.buttonSend.disabledIf(amount == "0")
}
@ -175,7 +196,8 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
binding.textBalanceDescription.apply {
goneIf(availableBalance < 0)
text = if (availableBalance != -1L && (availableBalance < totalBalance)) {
"(expecting +${(totalBalance - availableBalance).convertZatoshiToZecString()} ZEC in change)"
val change = (totalBalance - availableBalance).convertZatoshiToZecString()
"(expecting +$change ZEC in change)".toColoredSpan(R.color.text_light, "+$change")
} else {
"(enter an amount to send)"
}
@ -221,7 +243,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
}
private fun onSynced(uiModel: HomeViewModel.UiModel) {
if (!uiModel.hasFunds) {
if (!uiModel.hasBalance) {
onNoFunds()
} else {
setBanner("")

View File

@ -57,7 +57,7 @@ class HomeViewModel @Inject constructor() : ViewModel() {
}
uiModels = synchronizer.run {
combine(status, progress, balances, zec) { s, p, b, z->
UiModel(s, p, b.available, b.total, z)
UiModel(s, p, b.availableZatoshi, b.totalZatoshi, z)
}
}.conflate()
}
@ -81,6 +81,7 @@ class HomeViewModel @Inject constructor() : ViewModel() {
): Parcelable {
// Note: the wallet is effectively empty if it cannot cover the miner's fee
val hasFunds: Boolean get() = availableBalance > (MINERS_FEE_ZATOSHI.toDouble() / ZATOSHI_PER_ZEC) // 0.0001
val hasBalance: Boolean get() = totalBalance > (MINERS_FEE_ZATOSHI.toDouble() / ZATOSHI_PER_ZEC) // 0.0001
val isSynced: Boolean get() = status == SYNCED
val isSendEnabled: Boolean get() = isSynced && hasFunds
}

View File

@ -4,7 +4,6 @@ import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.widget.Toast
import cash.z.android.qrecycler.QRecycler
import cash.z.ecc.android.R
import cash.z.ecc.android.databinding.FragmentReceiveNewBinding
@ -12,7 +11,7 @@ import cash.z.ecc.android.di.viewmodel.viewModel
import cash.z.ecc.android.ext.onClickNavBack
import cash.z.ecc.android.ext.onClickNavTo
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.wallet.sdk.ext.abbreviatedAddress
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
import cash.z.wallet.sdk.ext.twig
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
@ -63,7 +62,7 @@ class ReceiveFragment : BaseFragment<FragmentReceiveNewBinding>() {
.withCorrectionLevel(QRecycler.CorrectionLevel.MEDIUM)
.into(binding.receiveQrCode)
binding.receiveAddress.text = address.abbreviatedAddress(12, 12)
binding.receiveAddress.text = address.toAbbreviatedAddress(12, 12)
// address.distribute(8) { i, part ->
// setAddressPart(i, part)

View File

@ -47,6 +47,9 @@ class SendViewModel @Inject constructor() : ViewModel() {
zatoshiAmount < ZcashSdk.MINERS_FEE_ZATOSHI -> {
emit("Please enter a larger amount")
}
synchronizer.getAddress() == toAddress -> {
emit("That appears to be your address!")
}
else -> emit(null)
}
}

View File

@ -32,7 +32,7 @@ import kotlinx.coroutines.withContext
class BackupFragment : BaseFragment<FragmentBackupBinding>() {
val walletSetup: WalletSetupViewModel by activityViewModel(false)
private var hasBackUp: Boolean? = null
private var hasBackUp: Boolean = true //TODO: implement backup and then check for it here-ish
override fun inflate(inflater: LayoutInflater): FragmentBackupBinding =
FragmentBackupBinding.inflate(inflater)
@ -80,7 +80,7 @@ class BackupFragment : BaseFragment<FragmentBackupBinding>() {
if (showMessage) {
Toast.makeText(activity, "Backup verification coming soon!", Toast.LENGTH_LONG).show()
}
mainActivity?.navController?.popBackStack(R.id.wallet_setup_navigation, true)
mainActivity?.navController?.popBackStack()
}
private fun applySpan(vararg textViews: TextView) = lifecycleScope.launch {

View File

@ -74,7 +74,9 @@
android:layout_height="wrap_content"
android:text="(enter an amount to send)"
android:visibility="gone"
tools:visibility="visible"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
android:textColor="@color/text_light_dimmed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_balance_available" />
@ -291,14 +293,14 @@
android:layout_height="0dp"
android:elevation="6dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.03825136612"
app:layout_constraintHorizontal_bias="0.8883"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0.8883"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.06420765027"
app:layout_constraintWidth_percent="0.05582524272"
app:srcCompat="@drawable/ic_receive_funds" />
app:layout_constraintVertical_bias="0.064"
app:layout_constraintWidth_percent="0.08"
app:srcCompat="@drawable/ic_account_circle" />
<ImageView
android:id="@+id/icon_detail"
@ -359,7 +361,7 @@
android:layout_height="0dp"
android:gravity="center"
android:includeFontPadding="false"
android:text="$0"
tools:text="$0"
android:textAppearance="@style/Zcash.TextAppearance.Zec"
android:textSize="72dp"
android:maxLines="1"

View File

@ -88,7 +88,6 @@
android:id="@+id/icon_qr_logo"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:onClick="copyAddress"
android:scaleType="fitCenter"
android:src="@drawable/ic_zcash_primary"
@ -135,6 +134,27 @@
app:layout_constraintStart_toEndOf="@id/receive_address"
app:layout_constraintTop_toTopOf="@id/receive_address" />
<Space
android:id="@+id/space_address_median"
android:layout_width="1dp"
android:layout_height="1dp"
app:layout_constraintTop_toBottomOf="@id/receive_address"
app:layout_constraintBottom_toTopOf="@id/button_scan"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintVertical_bias="0.4"/>
<View
android:id="@+id/hit_area_address"
android:layout_width="0dp"
android:layout_height="0dp"
android:onClick="copyAddress"
app:layout_constraintStart_toStartOf="@id/receive_address"
app:layout_constraintEnd_toEndOf="@id/background_qr"
app:layout_constraintTop_toTopOf="@id/receive_title"
app:layout_constraintBottom_toTopOf="@id/space_address_median"
tools:background="@color/spacer" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_scan"
android:layout_width="0dp"