internal release 0.4.8

This commit is contained in:
Kevin Gorham 2019-02-22 01:47:44 -05:00 committed by Kevin Gorham
parent 8481c7e067
commit 2e098e2ef2
11 changed files with 89 additions and 49 deletions

View File

@ -15,7 +15,7 @@ android {
minSdkVersion buildConfig.minSdkVersion
targetSdkVersion buildConfig.targetSdkVersion
versionCode 17 // todo: change this to 1_00_04 format, once we graduate beyond zero for the major version number because leading zeros indicate on octal number.
versionName "0.4.7-alpha"
versionName "0.4.8-alpha"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
multiDexEnabled true

View File

@ -0,0 +1,12 @@
package cash.z.android.wallet.extention
import android.text.format.DateUtils.SECOND_IN_MILLIS
import android.text.format.DateUtils.getRelativeTimeSpanString
internal inline fun Long.toRelativeTimeString(): CharSequence {
return getRelativeTimeSpanString(
this,
System.currentTimeMillis(),
SECOND_IN_MILLIS
)
}

View File

@ -12,6 +12,7 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import cash.z.android.wallet.R
import cash.z.android.wallet.extention.toAppColor
import cash.z.android.wallet.extention.toRelativeTimeString
import cash.z.wallet.sdk.ext.convertZatoshiToZec
import cash.z.wallet.sdk.ext.toZec
import java.text.SimpleDateFormat
@ -49,7 +50,7 @@ class TransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
val zecAbsoluteValue = tx.value.absoluteValue.convertZatoshiToZec(3)
val toOrFrom = if (tx.isSend) "to" else "from"
val srcOrDestination = address ?: "from shielded mystery person"
timestamp.text = if (!tx.isMined || tx.timeInSeconds == 0L) "Pending" else formatter.format(tx.timeInSeconds * 1000)
timestamp.text = if (!tx.isMined || tx.timeInSeconds == 0L) "Pending" else (tx.timeInSeconds * 1000L).toRelativeTimeString() //formatter.format(tx.timeInSeconds * 1000)
amount.text = "$sign$zecAbsoluteValue"
amount.setTextColor(amountColor.toAppColor())

View File

@ -1,6 +1,7 @@
package cash.z.android.wallet.ui.fragment
import android.os.Bundle
import android.os.Handler
import android.text.SpannableString
import android.text.Spanned
import android.util.Log
@ -23,22 +24,22 @@ import cash.z.android.wallet.R
import cash.z.android.wallet.databinding.FragmentHomeBinding
import cash.z.android.wallet.extention.*
import cash.z.android.wallet.sample.SampleProperties
import cash.z.android.wallet.ui.activity.MainActivity
import cash.z.android.wallet.ui.adapter.TransactionAdapter
import cash.z.android.wallet.ui.presenter.HomePresenter
import cash.z.android.wallet.ui.presenter.Presenter
import cash.z.android.wallet.ui.util.AlternatingRowColorDecoration
import cash.z.android.wallet.ui.util.AnimatorCompleteListener
import cash.z.android.wallet.ui.util.LottieLooper
import cash.z.android.wallet.ui.util.TopAlignedSpan
import cash.z.wallet.sdk.dao.WalletTransaction
import cash.z.wallet.sdk.data.*
import cash.z.wallet.sdk.data.ActiveSendTransaction
import cash.z.wallet.sdk.data.ActiveTransaction
import cash.z.wallet.sdk.data.TransactionState
import cash.z.wallet.sdk.data.twig
import cash.z.wallet.sdk.ext.*
import com.google.android.material.snackbar.Snackbar
import com.leinardi.android.speeddial.SpeedDialActionItem
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.android.ContributesAndroidInjector
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -61,6 +62,10 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
private var snackbar: Snackbar? = null
private var viewsInitialized = false
//testing this
private var clock: Handler = Handler()
private val tickIfNeeded = Ticker()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
@ -112,10 +117,12 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
launch {
homePresenter.start()
}
clock.postDelayed(tickIfNeeded, 1000L)
}
override fun onPause() {
super.onPause()
clock.removeCallbacks(tickIfNeeded)
homePresenter.stop()
binding.lottieZcashBadge.cancelAnimation()
}
@ -192,6 +199,7 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
override fun setActiveTransactions(activeTransactionMap: Map<ActiveTransaction, TransactionState>) {
if (activeTransactionMap.isEmpty()) {
twig("A.T.: setActiveTransactionsShown(false) because map is empty")
setActiveTransactionsShown(false)
return
}
@ -211,7 +219,7 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
twig("setting transaction state to ${transactionState::class.simpleName}")
var title = binding.includeContent.textActiveTransactionTitle.text?.toString() ?: ""
var subtitle = binding.includeContent.textActiveTransactionSubtitle.text?.toString() ?: ""
var subtitle: CharSequence = binding.includeContent.textActiveTransactionSubtitle.text?.toString() ?: ""
var isShown = binding.includeContent.textActiveTransactionHeader.visibility == View.VISIBLE
var isShownDelay = 10L
when (transactionState) {
@ -251,7 +259,7 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
binding.includeContent.lottieActiveTransaction.setAnimation(R.raw.lottie_send_success)
binding.includeContent.lottieActiveTransaction.playAnimation()
title = "ZEC Sent"
subtitle = "Today at 2:11pm"
subtitle = transactionState.timestamp.toRelativeTimeString()
binding.includeContent.textActiveTransactionValue.text = transaction.value.convertZatoshiToZecString(3)
binding.includeContent.textActiveTransactionValue.visibility = View.VISIBLE
binding.includeContent.buttonActiveTransactionCancel.visibility = View.GONE
@ -260,7 +268,7 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
isShown = false
} else {
title = "Confirmation Received"
subtitle = "Today at 2:12pm"
subtitle = transactionState.timestamp.toRelativeTimeString()
isShown = false;
isShownDelay = 5_000L
// take it out of the list in a bit and skip counting confirmation animation for now (i.e. one is enough)
@ -280,6 +288,7 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
}
binding.includeContent.textActiveTransactionTitle.text = title
binding.includeContent.textActiveTransactionSubtitle.text = subtitle
twig("A.T.: setActiveTransactionsShown($isShown, $isShownDelay) because ${transactionState}")
setActiveTransactionsShown(isShown, isShownDelay)
}
@ -290,9 +299,10 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
private fun setActiveTransactionsShown(isShown: Boolean, delay: Long = 0L) {
binding.includeContent.headerActiveTransaction.postDelayed({
binding.includeContent.groupActiveTransactionItems.visibility = if (isShown) View.VISIBLE else View.GONE
// do not animate if visibility is already in the right state
// binding.includeContent.headerActiveTransaction.animate().alpha(if(isShown) 1f else 0f).setDuration(250).setListener(
AnimatorCompleteListener{ binding.includeContent.groupActiveTransactionItems.visibility = if (isShown) View.VISIBLE else View.GONE }
// AnimatorCompleteListener{ }
// )
}, delay)
}
@ -452,6 +462,16 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
}
}
private inner class Ticker : Runnable {
override fun run() {
binding.includeContent.recyclerTransactions.apply {
if ((adapter?.itemCount ?: 0) > 0) {
adapter?.notifyDataSetChanged()
}
clock.postDelayed(this@Ticker, 1000L)
}
}
}
/**
* Defines the basic properties of each FAB button for use while initializing the FAB

View File

@ -1,6 +1,5 @@
package cash.z.android.wallet.ui.presenter
import android.util.Log
import cash.z.android.wallet.ui.fragment.HistoryFragment
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
import cash.z.wallet.sdk.dao.WalletTransaction
@ -27,22 +26,22 @@ class HistoryPresenter @Inject constructor(
}
override suspend fun start() {
Log.e("@TWIG", "historyPresenter starting!")
twig("historyPresenter starting!")
launchTransactionBinder(synchronizer.allTransactions())
}
override fun stop() {
Log.e("@TWIG", "historyPresenter stopping!")
twig("historyPresenter stopping!")
job.cancel()
}
private fun CoroutineScope.launchTransactionBinder(channel: ReceiveChannel<List<WalletTransaction>>) = launch {
Log.e("@TWIG", "transaction binder starting!")
twig("transaction binder starting!")
for (walletTransactionList in channel) {
Log.e("@TWIG", "received ${walletTransactionList.size} transactions for presenting")
twig("received ${walletTransactionList.size} transactions for presenting")
bind(walletTransactionList)
}
Log.e("@TWIG", "transaction binder exiting!")
twig("transaction binder exiting!")
}
@ -52,7 +51,9 @@ class HistoryPresenter @Inject constructor(
private fun bind(transactions: List<WalletTransaction>) {
twig("binding ${transactions.size} walletTransactions")
view.setTransactions(transactions.sortedByDescending { it.timeInSeconds })
view.setTransactions(transactions.sortedByDescending {
if (!it.isMined && it.isSend) Long.MAX_VALUE else it.timeInSeconds
})
}
}

View File

@ -5,10 +5,7 @@ import cash.z.android.wallet.ZcashWalletApplication
import cash.z.android.wallet.ui.fragment.HomeFragment
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
import cash.z.wallet.sdk.dao.WalletTransaction
import cash.z.wallet.sdk.data.ActiveSendTransaction
import cash.z.wallet.sdk.data.ActiveTransaction
import cash.z.wallet.sdk.data.Synchronizer
import cash.z.wallet.sdk.data.TransactionState
import cash.z.wallet.sdk.data.*
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.channels.ReceiveChannel
@ -31,42 +28,42 @@ class HomePresenter @Inject constructor(
}
override suspend fun start() {
Log.e("@TWIG-t", "homePresenter starting!")
twig("homePresenter starting!")
launchBalanceBinder(synchronizer.balance())
launchTransactionBinder(synchronizer.allTransactions())
launchActiveTransactionMonitor(synchronizer.activeTransactions())
}
override fun stop() {
Log.e("@TWIG-t", "homePresenter stopping!")
twig("homePresenter stopping!")
job.cancel()
}
private fun CoroutineScope.launchBalanceBinder(channel: ReceiveChannel<Long>) = launch {
var old: Long? = null
Log.e("@TWIG-t", "balance binder starting!")
twig("balance binder starting!")
for (new in channel) {
Log.e("@TWIG-t", "polled a balance item")
twig("polled a balance item")
bind(old, new).also { old = new }
}
Log.e("@TWIG", "balance binder exiting!")
twig("balance binder exiting!")
}
private fun CoroutineScope.launchTransactionBinder(channel: ReceiveChannel<List<WalletTransaction>>) = launch {
Log.e("@TWIG", "transaction binder starting!")
twig("transaction binder starting!")
for (walletTransactionList in channel) {
Log.e("@TWIG", "received ${walletTransactionList.size} transactions for presenting")
twig("received ${walletTransactionList.size} transactions for presenting")
bind(walletTransactionList)
}
Log.e("@TWIG", "transaction binder exiting!")
twig("transaction binder exiting!")
}
private fun CoroutineScope.launchActiveTransactionMonitor(channel: ReceiveChannel<Map<ActiveTransaction, TransactionState>>) = launch {
Log.e("@TWIG-v", "active transaction monitor starting!")
twig("active transaction monitor starting!")
for (i in channel) {
bind(i)
}
Log.e("@TWIG-v", "active transaction monitor exiting!")
twig("active transaction monitor exiting!")
}
@ -75,25 +72,25 @@ class HomePresenter @Inject constructor(
//
private fun bind(old: Long?, new: Long) = onMain {
Log.e("@TWIG-b", "binding balance of $new")
twig("binding balance of $new")
view.updateBalance(old ?: 0L, new)
}
private fun bind(transactions: List<WalletTransaction>) = onMain {
Log.e("@TWIG-b", "binding ${transactions.size} walletTransactions")
twig("binding ${transactions.size} walletTransactions")
view.setTransactions(transactions.sortedByDescending {
it.timeInSeconds
if (!it.isMined && it.isSend) Long.MAX_VALUE else it.timeInSeconds
})
}
private fun bind(activeTransactionMap: Map<ActiveTransaction, TransactionState>) = onMain {
Log.e("@TWIG-b", "binding a.t. map of size ${activeTransactionMap.size}")
twig("binding a.t. map of size ${activeTransactionMap.size}")
if (activeTransactionMap.isNotEmpty()) view.setActiveTransactions(activeTransactionMap)
}
fun onCancelActiveTransaction(transaction: ActiveSendTransaction) {
Log.e("@TWIG", "requesting to cancel send for transaction ${transaction.internalId}")
twig("requesting to cancel send for transaction ${transaction.internalId}")
val isTooLate = !synchronizer.cancelSend(transaction)
if (isTooLate) {
view.onCancelledTooLate()
@ -102,9 +99,9 @@ class HomePresenter @Inject constructor(
private fun onMain(block: () -> Unit) = launch {
withContext(Main) {
Log.e("@TWIG-t", "running task on main thread - start ${coroutineContext[Job]} | ${coroutineContext[CoroutineName]}")
twig("running task on main thread - start ${coroutineContext[Job]} | ${coroutineContext[CoroutineName]}")
block()
Log.e("@TWIG-t", "running task on main thread - complete")
twig("running task on main thread - complete")
}
}

View File

@ -1,7 +1,6 @@
package cash.z.android.wallet.ui.presenter
import cash.z.android.wallet.R
import cash.z.android.wallet.extention.toAppInt
import cash.z.android.wallet.extention.toAppString
import cash.z.android.wallet.sample.SampleProperties
import cash.z.android.wallet.ui.fragment.SendFragment
@ -43,7 +42,7 @@ class SendPresenter @Inject constructor(
* We require the user to send more than this amount. Right now, we just use the miner's fee as a minimum but other
* lower bounds may also be useful for validation.
*/
private val minimumZatoshiAllowed = 10_000L
private val minersFee = 10_000L
private var balanceJob: Job? = null
private var requiresValidation = true
var sendUiModel = SendUiModel()
@ -213,10 +212,12 @@ class SendPresenter @Inject constructor(
return true
}
fun bind(newZecBalance: Long) {
if (newZecBalance >= 0) {
twig("binding balance of $newZecBalance")
view.updateBalance(newZecBalance)
fun bind(newZatoshiBalance: Long) {
val available = newZatoshiBalance// - minersFee
if (available >= 0) {
twig("binding balance of $available")
view.updateBalance(available)
// updateModel(sendUiModel.copy(availableBalance = available))
}
}
@ -309,10 +310,16 @@ class SendPresenter @Inject constructor(
}
private fun validateZatoshiAmount(zatoshiValue: Long?): Boolean {
return if (zatoshiValue == null || zatoshiValue <= minimumZatoshiAllowed) {
return if (zatoshiValue == null || zatoshiValue <= minersFee) {
view.setAmountError("Please specify a larger amount")
requiresValidation = true
false
// } else if (sendUiModel.availableBalance != null
// && zatoshiValue >= sendUiModel.availableBalance!!) {
// view.setAmountError("Exceeds available balance of " +
// "${sendUiModel.availableBalance.convertZatoshiToZecString(3)}")
// requiresValidation = true
// false
} else {
view.setAmountError(null)
true
@ -332,6 +339,7 @@ class SendPresenter @Inject constructor(
data class SendUiModel(
val availableBalance: Long? = null,
var hasBeenUpdated: Boolean = false,
val isUsdSelected: Boolean = true,
val zatoshiValue: Long? = null,

View File

@ -24,6 +24,7 @@
android:paddingTop="30dp"
android:paddingEnd="16dp"
android:paddingBottom="16dp"
android:animateLayoutChanges="true"
android:transitionName="@string/transition_active_transaction_background">
<!-- Label: Current Activity -->

View File

@ -107,7 +107,7 @@
<TextView
android:id="@+id/text_balance_usd"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
tools:text="$5,459.32"

View File

@ -15,10 +15,10 @@ import javax.inject.Singleton
@Module
internal object SynchronizerModule {
const val MOCK_LOAD_DURATION = 3_000L
// const val MOCK_LOAD_DURATION = 12_000L
// const val MOCK_LOAD_DURATION = 3_000L
const val MOCK_LOAD_DURATION = 12_000L
const val MOCK_TX_INTERVAL = 20_000L
const val MOCK_ACTIVE_TX_STATE_CHANGE_INTERVAL = 4_000L
const val MOCK_ACTIVE_TX_STATE_CHANGE_INTERVAL = 7_000L
const val MOCK_IS_FIRST_RUN: Boolean = true