internal release 0.4.8
This commit is contained in:
parent
8481c7e067
commit
2e098e2ef2
|
@ -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
|
||||
|
|
Binary file not shown.
|
@ -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
|
||||
)
|
||||
}
|
|
@ -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())
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 -->
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue