laptop froze. committing it all just in case
This commit is contained in:
parent
65a3cd601a
commit
1b192ebc79
|
@ -33,6 +33,7 @@ android {
|
|||
dimension 'network'
|
||||
applicationId 'cash.z.android.zcon1.testnet'
|
||||
matchingFallbacks = ['debug', 'zcashtestnet']
|
||||
resValue("string", String.join("i", "", "n", "t", "al"), "-zswag")
|
||||
}
|
||||
|
||||
zcashmainnet {
|
||||
|
@ -97,6 +98,7 @@ dependencies {
|
|||
implementation "androidx.room:room-runtime:2.1.0-alpha06"
|
||||
implementation "androidx.room:room-common:2.1.0-alpha06"
|
||||
implementation 'com.google.guava:guava:27.0.1-android'
|
||||
implementation 'com.mixpanel.android:mixpanel-android:5.6.3'
|
||||
kapt "androidx.room:room-compiler:2.1.0-alpha06"
|
||||
|
||||
// Dagger
|
||||
|
|
Binary file not shown.
|
@ -3,7 +3,10 @@ package cash.z.android.wallet.di.module
|
|||
import android.content.SharedPreferences
|
||||
import android.preference.PreferenceManager
|
||||
import cash.z.android.wallet.BuildConfig
|
||||
import cash.z.android.wallet.ChipBucket
|
||||
import cash.z.android.wallet.InMemoryChipBucket
|
||||
import cash.z.android.wallet.ZcashWalletApplication
|
||||
import cash.z.android.wallet.data.StaticTransactionRepository
|
||||
import cash.z.android.wallet.extention.toDbPath
|
||||
import cash.z.android.wallet.sample.*
|
||||
import cash.z.android.wallet.sample.SampleProperties.COMPACT_BLOCK_PORT
|
||||
|
@ -55,15 +58,16 @@ internal object SynchronizerModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideWalletConfig(prefs: SharedPreferences): WalletConfig {
|
||||
val walletName = prefs.getString(PREFS_WALLET_DISPLAY_NAME, BobWallet.displayName)
|
||||
twig("FOUND WALLET DISPLAY NAME : $walletName")
|
||||
return when(walletName) {
|
||||
AliceWallet.displayName -> AliceWallet
|
||||
BobWallet.displayName -> BobWallet // Default wallet
|
||||
CarolWallet.displayName -> CarolWallet
|
||||
DaveWallet.displayName -> DaveWallet
|
||||
else -> WalletConfig.create(walletName)
|
||||
}
|
||||
return CarolWallet
|
||||
// val walletName = prefs.getString(PREFS_WALLET_DISPLAY_NAME, BobWallet.displayName)
|
||||
// twig("FOUND WALLET DISPLAY NAME : $walletName")
|
||||
// return when(walletName) {
|
||||
// AliceWallet.displayName -> AliceWallet
|
||||
// BobWallet.displayName -> BobWallet // Default wallet
|
||||
// CarolWallet.displayName -> CarolWallet
|
||||
// DaveWallet.displayName -> DaveWallet
|
||||
// else -> WalletConfig.create(walletName)
|
||||
// }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
@ -128,11 +132,13 @@ internal object SynchronizerModule {
|
|||
@Singleton
|
||||
fun provideRepository(
|
||||
application: ZcashWalletApplication,
|
||||
rustBackend: RustBackendWelding,
|
||||
walletConfig: WalletConfig
|
||||
): TransactionRepository {
|
||||
return PollingTransactionRepository(
|
||||
application,
|
||||
walletConfig.dataDbName,
|
||||
rustBackend,
|
||||
DEFAULT_TRANSACTION_POLL_FREQUENCY_MILLIS
|
||||
)
|
||||
}
|
||||
|
@ -191,4 +197,11 @@ internal object SynchronizerModule {
|
|||
wallet
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideChipBucket(): ChipBucket {
|
||||
return InMemoryChipBucket()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<!-- for analytics to know when to send information -->
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<!--<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />-->
|
||||
<!--<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />-->
|
||||
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
package cash.z.android.wallet
|
||||
|
||||
import cash.z.android.wallet.ui.util.Analytics
|
||||
import cash.z.android.wallet.ui.util.Analytics.PokerChipFunnel.*
|
||||
import cash.z.android.wallet.ui.util.Analytics.trackFunnelStep
|
||||
import cash.z.wallet.sdk.data.twig
|
||||
import java.util.concurrent.CopyOnWriteArraySet
|
||||
import cash.z.wallet.sdk.ext.ZATOSHI
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
|
||||
interface ChipBucket {
|
||||
fun add(chip: PokerChip)
|
||||
fun remove(chip: PokerChip)
|
||||
fun redeem(chip: PokerChip)
|
||||
fun forEach(block: (PokerChip) -> Unit)
|
||||
fun save()
|
||||
fun restore(): ChipBucket
|
||||
fun findChip(id: String): PokerChip?
|
||||
|
||||
fun valuePending(): Long
|
||||
fun valueRedeemed(): Long
|
||||
fun setOnBucketChangedListener(listener: OnBucketChangedListener)
|
||||
fun removeOnBucketChangedListener(listener: OnBucketChangedListener)
|
||||
|
||||
interface OnBucketChangedListener {
|
||||
fun onBucketChanged(bucket: ChipBucket)
|
||||
}
|
||||
}
|
||||
|
||||
class InMemoryChipBucket : ChipBucket {
|
||||
|
||||
private var listener: ChipBucket.OnBucketChangedListener? = null
|
||||
private val chips = CopyOnWriteArraySet<PokerChip>()
|
||||
|
||||
override fun add(chip: PokerChip) {
|
||||
chips.add(chip)
|
||||
listener?.onBucketChanged(this)
|
||||
trackFunnelStep(Collected(chip))
|
||||
}
|
||||
|
||||
override fun remove(chip: PokerChip) {
|
||||
chips.remove(chip)
|
||||
listener?.onBucketChanged(this)
|
||||
trackFunnelStep(Aborted(chip))
|
||||
}
|
||||
|
||||
override fun redeem(chip: PokerChip) {
|
||||
if (!chip.isRedeemed()) {
|
||||
chips.remove(chip)
|
||||
chips.add(chip.copy(redeemed = System.currentTimeMillis()))
|
||||
listener?.onBucketChanged(this)
|
||||
trackFunnelStep(Redeemed(chip, true))
|
||||
}
|
||||
}
|
||||
|
||||
override fun forEach(block: (PokerChip) -> Unit) {
|
||||
if (chips.isNotEmpty()) {
|
||||
for (chip in chips) {
|
||||
block(chip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun save() {}
|
||||
|
||||
override fun restore(): InMemoryChipBucket = this
|
||||
|
||||
override fun findChip(id: String) = chips.find { it.id == id }
|
||||
|
||||
override fun valuePending(): Long {
|
||||
if(chips.isEmpty()) return -1L
|
||||
return chips.fold(0L) { acc, pokerChip ->
|
||||
if(pokerChip.isRedeemed()) acc else acc + pokerChip.zatoshiValue
|
||||
}
|
||||
}
|
||||
|
||||
override fun valueRedeemed(): Long {
|
||||
if(chips.isEmpty()) return -1L
|
||||
return chips.fold(0L) { acc, pokerChip ->
|
||||
if (pokerChip.isRedeemed()) acc + pokerChip.zatoshiValue else acc
|
||||
}
|
||||
}
|
||||
|
||||
override fun setOnBucketChangedListener(listener: ChipBucket.OnBucketChangedListener) {
|
||||
if (listener !== this.listener) {
|
||||
this.listener = listener
|
||||
listener?.onBucketChanged(this)
|
||||
twig("bucket listener set to $listener")
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeOnBucketChangedListener(listener: ChipBucket.OnBucketChangedListener) {
|
||||
if (listener === this.listener) {
|
||||
twig("removing bucket listener $listener")
|
||||
this.listener = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// suppress b/c we're ok w/ copies we just don't want the long constructor to be suggested in the IDE, for convenience
|
||||
@Suppress("DataClassPrivateConstructor")
|
||||
data class PokerChip private constructor(val id: String = "", val redeemed: Long = -1L, val created: Long = -1L) {
|
||||
constructor(id: String) : this(id, created = System.currentTimeMillis())
|
||||
|
||||
init {
|
||||
require(id.startsWith("r-") || id.startsWith("b-"))
|
||||
}
|
||||
|
||||
val maskedId: String get() = id.split("-").let { "${it[0]}-${it[1]}-${it[2]}-**masked**" }
|
||||
val chipColor: ChipColor get() = ChipColor.from(this)!! // acceptable !! because of the require contract
|
||||
val zatoshiValue: Long get() = chipColor.zatoshiValue
|
||||
|
||||
fun isRedeemed(): Boolean {
|
||||
return redeemed > 0
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return id.hashCode()
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
return other is PokerChip && other.id == id
|
||||
}
|
||||
|
||||
enum class ChipColor(val zatoshiValue: Long, val idPrefix: String) {
|
||||
RED(5L * ZATOSHI, "r-"),
|
||||
BLACK(25L * ZATOSHI, "b-");
|
||||
|
||||
companion object {
|
||||
fun from(chip: PokerChip): ChipColor? {
|
||||
for (color in values()) {
|
||||
if(chip.id.startsWith(color.idPrefix)) return color
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PokerChipSeedProvider(val chipId: String) : ReadOnlyProperty<Any?, ByteArray> {
|
||||
constructor(chip: PokerChip) : this(chip.id)
|
||||
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): ByteArray {
|
||||
val salt = ZcashWalletApplication.instance.getString(R.string.initial)
|
||||
return "$chipId$salt".toByteArray()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package cash.z.android.wallet.data
|
||||
|
||||
import cash.z.android.wallet.ZcashWalletApplication
|
||||
import cash.z.wallet.sdk.data.PollingTransactionRepository
|
||||
import cash.z.wallet.sdk.data.twig
|
||||
import cash.z.wallet.sdk.db.DerivedDataDb
|
||||
import cash.z.wallet.sdk.jni.RustBackendWelding
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
// TODO: reverse this hierarchy so that Polling depends on this class and has all the overhead specific to polling, leaving this class more streamlined and efficient
|
||||
class StaticTransactionRepository(dataDbName: String, rustBackend: RustBackendWelding, dbCallback: (DerivedDataDb) -> Unit = {}) :
|
||||
PollingTransactionRepository(ZcashWalletApplication.instance, dataDbName, rustBackend,2000L, dbCallback) {
|
||||
|
||||
override fun start(parentScope: CoroutineScope) {
|
||||
twig("starting repository ignored because this DB does not poll")
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
twig("stopping repository ignored because this DB does not poll")
|
||||
}
|
||||
|
||||
}
|
|
@ -38,7 +38,8 @@ import javax.inject.Singleton
|
|||
|
||||
// Zcon1 Fragments
|
||||
Zcon1HomeFragmentModule::class,
|
||||
Zcon1CartFragmentModule::class
|
||||
Zcon1CartFragmentModule::class,
|
||||
Zcon1FeedbackFragmentModule::class
|
||||
]
|
||||
)
|
||||
interface ApplicationComponent : AndroidInjector<ZcashWalletApplication> {
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package cash.z.android.wallet.extention
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import cash.z.android.wallet.ZcashWalletApplication
|
||||
|
||||
internal inline fun tryIgnore(block: () -> Unit) {
|
||||
|
@ -17,4 +20,9 @@ internal inline fun String.truncate(): String {
|
|||
|
||||
internal inline fun String.toDbPath(): String {
|
||||
return ZcashWalletApplication.instance.getDatabasePath(this).absolutePath
|
||||
}
|
||||
|
||||
fun Context.copyToClipboard(text: CharSequence) {
|
||||
val clipboard: ClipboardManager = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
clipboard.primaryClip = ClipData.newPlainText("Zcon1", text)
|
||||
}
|
|
@ -16,17 +16,23 @@ import androidx.navigation.NavOptions
|
|||
import androidx.navigation.Navigation
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.navigateUp
|
||||
import cash.z.android.wallet.R
|
||||
import cash.z.android.wallet.Zcon1Store
|
||||
import cash.z.android.wallet.*
|
||||
import cash.z.android.wallet.databinding.ActivityMainBinding
|
||||
import cash.z.android.wallet.di.annotation.ActivityScope
|
||||
import cash.z.android.wallet.extention.Toaster
|
||||
import cash.z.android.wallet.extention.alert
|
||||
import cash.z.android.wallet.extention.copyToClipboard
|
||||
import cash.z.android.wallet.sample.WalletConfig
|
||||
import cash.z.android.wallet.ui.fragment.ScanFragment
|
||||
import cash.z.android.wallet.ui.presenter.BalancePresenter
|
||||
import cash.z.android.wallet.ui.presenter.MainPresenter
|
||||
import cash.z.android.wallet.ui.presenter.MainPresenterModule
|
||||
import cash.z.android.wallet.ui.util.Analytics
|
||||
import cash.z.android.wallet.ui.util.Analytics.PokerChipFunnel.StartSweep
|
||||
import cash.z.android.wallet.ui.util.Analytics.PokerChipFunnel.Swept
|
||||
import cash.z.android.wallet.ui.util.Analytics.Tap.*
|
||||
import cash.z.android.wallet.ui.util.Analytics.trackAction
|
||||
import cash.z.android.wallet.ui.util.Analytics.trackFunnelStep
|
||||
import cash.z.android.wallet.ui.util.Broom
|
||||
import cash.z.wallet.sdk.data.Synchronizer
|
||||
import cash.z.wallet.sdk.data.twig
|
||||
|
@ -51,6 +57,9 @@ class MainActivity : BaseActivity(), Animator.AnimatorListener, ScanFragment.Bar
|
|||
@Inject
|
||||
lateinit var broom: Broom
|
||||
|
||||
@Inject
|
||||
lateinit var chipBucket: ChipBucket
|
||||
|
||||
lateinit var binding: ActivityMainBinding
|
||||
lateinit var loadMessages: List<String>
|
||||
|
||||
|
@ -63,6 +72,7 @@ class MainActivity : BaseActivity(), Animator.AnimatorListener, ScanFragment.Bar
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
chipBucket.restore()
|
||||
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
|
||||
binding.activity = this
|
||||
initAppBar()
|
||||
|
@ -79,6 +89,7 @@ class MainActivity : BaseActivity(), Animator.AnimatorListener, ScanFragment.Bar
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
chipBucket.restore()
|
||||
launch {
|
||||
balancePresenter.start(this, synchronizer)
|
||||
mainPresenter.start()
|
||||
|
@ -89,11 +100,13 @@ class MainActivity : BaseActivity(), Animator.AnimatorListener, ScanFragment.Bar
|
|||
super.onPause()
|
||||
balancePresenter.stop()
|
||||
mainPresenter.stop()
|
||||
chipBucket.save()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
synchronizer.stop()
|
||||
Analytics.clear()
|
||||
}
|
||||
|
||||
private fun initAppBar() {
|
||||
|
@ -280,6 +293,7 @@ class MainActivity : BaseActivity(), Animator.AnimatorListener, ScanFragment.Bar
|
|||
}
|
||||
|
||||
fun onScanQr(view: View) {
|
||||
trackAction(TAPPED_SCAN_QR_HOME)
|
||||
supportFragmentManager.beginTransaction()
|
||||
.add(R.id.camera_placeholder, ScanFragment(), "camera_fragment")
|
||||
.addToBackStack("camera_fragment_scanning")
|
||||
|
@ -287,19 +301,76 @@ class MainActivity : BaseActivity(), Animator.AnimatorListener, ScanFragment.Bar
|
|||
}
|
||||
|
||||
fun onSendFeedback(view: View) {
|
||||
Toaster.short("Feedback sent! (j/k)")
|
||||
trackAction(TAPPED_GIVE_FEEDBACK)
|
||||
navController.navigate(R.id.nav_feedback_fragment)
|
||||
}
|
||||
|
||||
override fun onBarcodeScanned(value: String) {
|
||||
Toaster.short(value)
|
||||
playSound(Random.nextBoolean())
|
||||
exitScanMode()
|
||||
|
||||
// Toaster.short("qr scanned")
|
||||
// launch {
|
||||
// val result = broom.sweep(SampleSeedProvider("testreferencebob"))
|
||||
// Toaster.short("Sweep result? $result")
|
||||
// }
|
||||
// For now, empty happens when back is pressed
|
||||
if (value.isEmpty()) return
|
||||
chipBucket.findChip(value)?.let {existingChip ->
|
||||
if (existingChip.isRedeemed()) {
|
||||
alert("Previously Redeemed!", "We scanned this one already and the funds went to this wallet.")
|
||||
} else {
|
||||
alert(
|
||||
title = "Still Processing",
|
||||
message = "We scanned this one already and it is still processing. Would you rather wait until it finishes or abort and try again later?",
|
||||
positiveButtonResId = R.string.wait,
|
||||
negativeButtonResId = R.string.abort,
|
||||
negativeAction = {
|
||||
onAbortChip(existingChip)
|
||||
}
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
alert(
|
||||
title = "You found a token!",
|
||||
message = "Would you like to magically convert this poker chip into digital money?"
|
||||
) {
|
||||
playSound(Random.nextBoolean())
|
||||
funQuote()
|
||||
launch {
|
||||
val chip = PokerChip(value)
|
||||
chipBucket.add(chip)
|
||||
val result = sweepChip(chip)
|
||||
twig("Sweep result? $result")
|
||||
trackFunnelStep(Swept(chip, result))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun onAbortChip(chip: PokerChip) {
|
||||
// TODO: don't remove until we're sure we can because this triggers a funnel event
|
||||
chipBucket.remove(chip)
|
||||
}
|
||||
|
||||
private suspend fun sweepChip(chip: PokerChip): String? {
|
||||
trackFunnelStep(StartSweep(chip))
|
||||
val provider = PokerChipSeedProvider(chip)
|
||||
return broom.sweep(provider, chip.zatoshiValue)
|
||||
}
|
||||
|
||||
val scanQuote = arrayOf(
|
||||
"You're the Scrooge McDuck of Zcon1!",
|
||||
"We're rich!",
|
||||
"Show me the money! Oh wait, you just did. Literally.",
|
||||
"Doing magic. Actual magic.",
|
||||
"This is TAZmania!"
|
||||
)
|
||||
private fun funQuote() {
|
||||
var message = scanQuote.random()
|
||||
// if(message == scanQuote[0]) message = scanQuote.random() // simple way to make 0 more rare
|
||||
Toaster.short(message)
|
||||
}
|
||||
|
||||
override fun isTargetBarcode(value: String?): Boolean {
|
||||
if(value == null) return false
|
||||
return value.startsWith("r-") || value.startsWith("b-")
|
||||
}
|
||||
|
||||
private fun playSound(isLarge: Boolean) {
|
||||
|
@ -352,6 +423,23 @@ class MainActivity : BaseActivity(), Animator.AnimatorListener, ScanFragment.Bar
|
|||
Toaster.short(processing.state.toString())
|
||||
}
|
||||
|
||||
fun onFeedbackSubmit(view: View) {
|
||||
trackAction(TAPPED_SUBMIT_FEEDBACK)
|
||||
Toaster.short("Feedback Submitted! (j/k)")
|
||||
navController.navigateUp()
|
||||
}
|
||||
fun onFeedbackCancel(view: View) {
|
||||
Toaster.short("Feedback cancelled")
|
||||
trackAction(TAPPED_CANCEL_FEEDBACK)
|
||||
navController.navigateUp()
|
||||
}
|
||||
|
||||
fun copyAddress(view: View) {
|
||||
trackAction(TAPPED_COPY_ADDRESS)
|
||||
Toaster.short("Address copied!")
|
||||
copyToClipboard(synchronizer.getAddress())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Module
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package cash.z.android.wallet.ui.adapter
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
@ -14,10 +13,10 @@ import cash.z.android.wallet.R
|
|||
import cash.z.android.wallet.extention.toAppColor
|
||||
import cash.z.android.wallet.extention.toRelativeTimeString
|
||||
import cash.z.android.wallet.extention.truncate
|
||||
import cash.z.wallet.sdk.ext.convertZatoshiToZec
|
||||
import cash.z.wallet.sdk.ext.toZec
|
||||
import java.text.SimpleDateFormat
|
||||
import cash.z.wallet.sdk.dao.WalletTransaction
|
||||
import cash.z.wallet.sdk.ext.MINERS_FEE_ZATOSHI
|
||||
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
|
@ -41,25 +40,33 @@ class TransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
|
|||
private val timestamp = itemView.findViewById<TextView>(R.id.text_transaction_timestamp)
|
||||
private val amount = itemView.findViewById<TextView>(R.id.text_transaction_amount)
|
||||
private val address = itemView.findViewById<TextView>(R.id.text_transaction_address)
|
||||
private val memo = itemView.findViewById<TextView>(R.id.text_transaction_memo)
|
||||
private val formatter = SimpleDateFormat("M/d h:mma", Locale.getDefault())
|
||||
|
||||
fun bind(tx: WalletTransaction) {
|
||||
val isChip = tx.isPokerChip()
|
||||
val useSend = tx.isSend && !isChip
|
||||
val isHistory = icon != null
|
||||
val sign = if (tx.isSend) "-" else "+"
|
||||
val amountColor = if (tx.isSend) R.color.text_dark_dimmed else R.color.colorPrimary
|
||||
val transactionColor = if (tx.isSend) R.color.send_associated else R.color.receive_associated
|
||||
val transactionIcon = if (tx.isSend) R.drawable.ic_sent_transaction else R.drawable.ic_received_transaction
|
||||
val zecAbsoluteValue = tx.value.absoluteValue.convertZatoshiToZec(6)
|
||||
val toOrFrom = if (tx.isSend) "to" else "from"
|
||||
val sign = if (useSend) "- " else "+ "
|
||||
val amountColor = if (useSend) R.color.colorAccent else R.color.zcashPurple_accent
|
||||
val transactionColor = if (useSend) R.color.send_associated else R.color.receive_associated
|
||||
val transactionIcon = if (useSend || (isChip && tx.address != "Redeemed")) R.drawable.ic_sent_transaction else R.drawable.ic_received_transaction
|
||||
val zecAbsoluteValue = tx.value.absoluteValue + if(tx.isSend) MINERS_FEE_ZATOSHI else 0
|
||||
val toOrFrom = if (useSend) "to" else "from"
|
||||
val srcOrDestination = tx.address?.truncate() ?: "shielded mystery person"
|
||||
timestamp.text = if (!tx.isMined || tx.timeInSeconds == 0L) "Pending"
|
||||
else (if (isHistory) formatter.format(tx.timeInSeconds * 1000) else (tx.timeInSeconds * 1000L).toRelativeTimeString())
|
||||
amount.text = "$sign$zecAbsoluteValue"
|
||||
timestamp.text = if ((!tx.isMined || tx.timeInSeconds == 0L) && !isChip) "Pending"
|
||||
else (if (isHistory) formatter.format(tx.timeInSeconds * 1000) else (tx.timeInSeconds * 1000L).toRelativeTimeString())
|
||||
amount.text = "$sign${zecAbsoluteValue.convertZatoshiToZecString(4)}"
|
||||
amount.setTextColor(amountColor.toAppColor())
|
||||
|
||||
// maybes - and if this gets to be too much, then pass in a custom holder when constructing the adapter, instead
|
||||
status?.setBackgroundColor(transactionColor.toAppColor())
|
||||
address?.text = "$toOrFrom $srcOrDestination"
|
||||
address?.text = if (isChip) tx.address else "$toOrFrom $srcOrDestination"
|
||||
memo?.text = tx.memo
|
||||
icon?.setImageResource(transactionIcon)
|
||||
}
|
||||
}
|
||||
|
||||
private fun WalletTransaction.isPokerChip(): Boolean {
|
||||
return memo?.contains("Poker") == true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
package cash.z.android.wallet.ui.dialog
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.Window
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import cash.z.android.wallet.R
|
||||
import cash.z.android.wallet.databinding.DialogStatusBinding
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
|
||||
class StatusDialog(
|
||||
availableBalance: Long,
|
||||
syncingBalance: Long,
|
||||
pendingChipBalance: Long,
|
||||
summary: String
|
||||
) : DialogFragment() {
|
||||
|
||||
init {
|
||||
arguments = Bundle().apply {
|
||||
putLong(ARG_BALANCE_AVAILABLE, availableBalance)
|
||||
putLong(ARG_BALANCE_SYNCING, syncingBalance)
|
||||
putLong(ARG_BALANCE_PENDING, pendingChipBalance)
|
||||
putString(ARG_SUMMARY, summary)
|
||||
}
|
||||
}
|
||||
|
||||
lateinit var binding: DialogStatusBinding
|
||||
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
binding = DataBindingUtil.inflate(activity!!.layoutInflater, R.layout.dialog_status, null, false)
|
||||
val dialog = MaterialAlertDialogBuilder(context)
|
||||
.setView(binding.root)
|
||||
.create()
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
val lp = dialog.window.attributes
|
||||
lp.gravity = Gravity.TOP
|
||||
lp.y = 0
|
||||
|
||||
return dialog
|
||||
}
|
||||
//
|
||||
// override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
// return super.onCreateView(inflater, container, savedInstanceState)
|
||||
// val window = dialog?.window
|
||||
//
|
||||
// // set "origin" to top left corner, so to speak
|
||||
// window!!.setGravity(Gravity.TOP or Gravity.LEFT)
|
||||
//
|
||||
// // after that, setting values for x and y works "naturally"
|
||||
// val params = window.attributes
|
||||
// params.x = 0
|
||||
// params.y = -200
|
||||
// window.attributes = params
|
||||
// }
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
// binding.statusDescription.setText(product.descriptionResId)
|
||||
// binding.statusProduct.text = product.name
|
||||
// binding.statusPrice.text = product.zatoshiValue.convertZatoshiToZecString(1) + " TAZ"
|
||||
// binding.statusImage.setImageResource(product.imageResId)
|
||||
|
||||
binding.buttonOk.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ARG_BALANCE_SYNCING = "arg_balance_syncing"
|
||||
const val ARG_BALANCE_AVAILABLE = "arg_balance_available"
|
||||
const val ARG_BALANCE_PENDING = "arg_balance_pending"
|
||||
const val ARG_SUMMARY = "arg_summary"
|
||||
}
|
||||
|
||||
}
|
|
@ -12,6 +12,11 @@ import cash.z.android.wallet.databinding.DialogSwagBinding
|
|||
import cash.z.android.wallet.ui.activity.MainActivity
|
||||
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import androidx.constraintlayout.widget.ConstraintAttribute.setAttributes
|
||||
import android.R.attr.y
|
||||
import android.R.attr.x
|
||||
|
||||
import android.view.*
|
||||
|
||||
|
||||
class Zcon1SwagDialog(argProductId: Int, argBuyerName: String) : DialogFragment() {
|
||||
|
@ -34,12 +39,31 @@ class Zcon1SwagDialog(argProductId: Int, argBuyerName: String) : DialogFragment(
|
|||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
binding = DataBindingUtil.inflate(activity!!.layoutInflater, R.layout.dialog_swag, null, false)
|
||||
return MaterialAlertDialogBuilder(context)
|
||||
val dialog = MaterialAlertDialogBuilder(context)
|
||||
.setView(binding.root)
|
||||
.create().also {
|
||||
// it.window.setBackgroundDrawableResource(android.R.color.transparent)
|
||||
}
|
||||
.create()
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
val lp = dialog.window.attributes
|
||||
lp.gravity = Gravity.TOP
|
||||
lp.y = 0
|
||||
|
||||
return dialog
|
||||
}
|
||||
//
|
||||
// override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
// return super.onCreateView(inflater, container, savedInstanceState)
|
||||
// val window = dialog?.window
|
||||
//
|
||||
// // set "origin" to top left corner, so to speak
|
||||
// window!!.setGravity(Gravity.TOP or Gravity.LEFT)
|
||||
//
|
||||
// // after that, setting values for x and y works "naturally"
|
||||
// val params = window.attributes
|
||||
// params.x = 0
|
||||
// params.y = -200
|
||||
// window.attributes = params
|
||||
// }
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
|
|
@ -20,6 +20,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import androidx.transition.Transition
|
||||
import androidx.transition.TransitionInflater
|
||||
import cash.z.android.wallet.ChipBucket
|
||||
import cash.z.android.wallet.R
|
||||
import cash.z.android.wallet.databinding.FragmentHomeBinding
|
||||
import cash.z.android.wallet.di.annotation.FragmentScope
|
||||
|
|
|
@ -37,6 +37,11 @@ class ScanFragment : BaseFragment() {
|
|||
|
||||
interface BarcodeCallback {
|
||||
fun onBarcodeScanned(value: String)
|
||||
|
||||
/**
|
||||
* Any barcode that returns false from this method will be ignored and not returned through [onBarcodeScanned]
|
||||
*/
|
||||
fun isTargetBarcode(value: String?): Boolean
|
||||
}
|
||||
|
||||
private val revealCamera = Runnable {
|
||||
|
@ -84,7 +89,7 @@ class ScanFragment : BaseFragment() {
|
|||
super.onCreate(savedInstanceState)
|
||||
// This callback will only be called when MyFragment is at least Started.
|
||||
val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
|
||||
mainActivity?.onBarcodeScanned("BACK PRESSED")
|
||||
mainActivity?.onBarcodeScanned("")
|
||||
}
|
||||
callback.isEnabled = true
|
||||
}
|
||||
|
@ -252,11 +257,13 @@ class ScanFragment : BaseFragment() {
|
|||
if (results.isNotEmpty()) {
|
||||
val barcode = results[0]
|
||||
val value = barcode.rawValue
|
||||
onScanSuccess(value!!)
|
||||
// TODO: highlight the barcode
|
||||
var bounds = barcode.boundingBox
|
||||
var corners = barcode.cornerPoints
|
||||
binding.cameraView.setBarcode(barcode)
|
||||
if (barcodeCallback == null || barcodeCallback?.isTargetBarcode(value) == true) {
|
||||
onScanSuccess(value!!)
|
||||
}
|
||||
// // TODO: highlight the barcode
|
||||
// var bounds = barcode.boundingBox
|
||||
// var corners = barcode.cornerPoints
|
||||
// binding.cameraView.setBarcode(barcode)
|
||||
}
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
|
|
|
@ -146,6 +146,11 @@ class SendFragment : BaseFragment(), SendPresenter.SendView, ScanFragment.Barcod
|
|||
sendPresenter.inputAddressUpdated(value)
|
||||
}
|
||||
|
||||
override fun isTargetBarcode(value: String?): Boolean {
|
||||
if(value == null) return false
|
||||
return value.toLowerCase().startsWith("ztest")
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Internal View Logic
|
||||
|
@ -179,11 +184,6 @@ class SendFragment : BaseFragment(), SendPresenter.SendView, ScanFragment.Barcod
|
|||
|
||||
/* Init - Taps */
|
||||
|
||||
binding.imageSwapCurrency.setOnClickListener {
|
||||
// validate the amount before we toggle (or else we lose their uncommitted change)
|
||||
sendPresenter.inputHeaderUpdated(binding.textValueHeader.text.toString())
|
||||
sendPresenter.inputToggleCurrency()
|
||||
}
|
||||
binding.buttonSendZec.setOnClickListener{
|
||||
exitScanMode()
|
||||
sendPresenter.inputSendPressed()
|
||||
|
@ -201,15 +201,6 @@ class SendFragment : BaseFragment(), SendPresenter.SendView, ScanFragment.Barcod
|
|||
TooltipCompat.setTooltipText(this, context.getString(R.string.send_tooltip_scan_qr))
|
||||
}
|
||||
|
||||
binding.imageAddressShortcut?.apply {
|
||||
if (BuildConfig.DEBUG) {
|
||||
visibility = View.VISIBLE
|
||||
TooltipCompat.setTooltipText(this, context.getString(R.string.send_tooltip_address_shortcut))
|
||||
setOnClickListener(::onPasteShortcutAddress)
|
||||
} else {
|
||||
visibility = View.GONE
|
||||
}
|
||||
}
|
||||
binding.dialogSendBackground.setOnClickListener { hideSendDialog() }
|
||||
binding.dialogSubmitButton.setOnClickListener { onSendZec() }
|
||||
binding.imageScanQr.setOnClickListener(::onScanQrCode)
|
||||
|
@ -329,12 +320,13 @@ class SendFragment : BaseFragment(), SendPresenter.SendView, ScanFragment.Barcod
|
|||
}
|
||||
|
||||
override fun setMemoError(message: String?) {
|
||||
val validColor = R.color.zcashBlack_12.toAppColor()
|
||||
val validColor = R.color.text_light.toAppColor()
|
||||
val accentColor = R.color.zcashPurple_accent.toAppColor()
|
||||
val errorColor = R.color.colorAccent.toAppColor()
|
||||
if (message == null) {
|
||||
binding.dividerMemo.setBackgroundColor(validColor)
|
||||
binding.textMemoCharCount.setTextColor(validColor)
|
||||
binding.textAreaMemo.setTextColor(R.color.text_dark.toAppColor())
|
||||
binding.dividerMemo.setBackgroundColor(accentColor)
|
||||
binding.textMemoCharCount.setTextColor(accentColor)
|
||||
binding.textAreaMemo.setTextColor(validColor)
|
||||
} else {
|
||||
binding.dividerMemo.setBackgroundColor(errorColor)
|
||||
binding.textMemoCharCount.setTextColor(errorColor)
|
||||
|
|
|
@ -28,6 +28,9 @@ class WelcomeFragment : ProgressFragment(R.id.progress_welcome) {
|
|||
|
||||
private lateinit var binding: FragmentZcon1WelcomeBinding
|
||||
|
||||
// Flag for development
|
||||
private val developmentShortcut: Boolean = false
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
//
|
||||
|
@ -54,13 +57,13 @@ class WelcomeFragment : ProgressFragment(R.id.progress_welcome) {
|
|||
override fun onResume() {
|
||||
super.onResume()
|
||||
mainActivity?.setToolbarShown(false)
|
||||
binding.lottieEccLogo.playToFrame(240/100)
|
||||
binding.lottieEccLogo.playToFrame(if(developmentShortcut) 240 else 240)
|
||||
binding.lottieEccLogo.speed = 1.4f
|
||||
}
|
||||
|
||||
private suspend fun onNext() = coroutineScope {
|
||||
if (mainActivity != null) {
|
||||
val hasName = prefs.getString(PREFS_PSEUDONYM, null) == null
|
||||
val hasName = if(developmentShortcut) true else prefs.getString(PREFS_PSEUDONYM, null) == null
|
||||
val destination =
|
||||
if (hasName) R.id.action_welcome_fragment_to_firstrun_fragment
|
||||
else R.id.action_welcome_fragment_to_home_fragment
|
||||
|
|
|
@ -60,6 +60,12 @@ class Zcon1CartFragment : BaseFragment(), BalancePresenter.BalanceView {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
mainActivity?.setToolbarShown(false)
|
||||
mainActivity?.setNavigationShown(true)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
mainActivity?.balancePresenter?.addBalanceView(this)
|
||||
|
@ -75,12 +81,6 @@ class Zcon1CartFragment : BaseFragment(), BalancePresenter.BalanceView {
|
|||
// Private API
|
||||
//
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
mainActivity?.setToolbarShown(false)
|
||||
mainActivity?.setNavigationShown(true)
|
||||
}
|
||||
|
||||
private fun showSwagDialog(item: Zcon1Store.CartItem) {
|
||||
Zcon1SwagDialog(item.id, buyerName).show(activity!!.supportFragmentManager, item.name)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package cash.z.android.wallet.ui.fragment
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import cash.z.android.wallet.R
|
||||
import cash.z.android.wallet.ZcashWalletApplication.Companion.PREFS_PSEUDONYM
|
||||
import cash.z.android.wallet.Zcon1Store
|
||||
import cash.z.android.wallet.databinding.FragmentZcon1FeedbackBinding
|
||||
import cash.z.android.wallet.di.annotation.FragmentScope
|
||||
import cash.z.android.wallet.ui.dialog.Zcon1SwagDialog
|
||||
import cash.z.android.wallet.ui.presenter.BalancePresenter
|
||||
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
|
||||
import cash.z.wallet.sdk.secure.Wallet
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
||||
/**
|
||||
* Fragment representing the home screen of the app. This is the screen most often seen by the user when launching the
|
||||
* application.
|
||||
*/
|
||||
class Zcon1FeedbackFragment : BaseFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var prefs: SharedPreferences
|
||||
private lateinit var binding: FragmentZcon1FeedbackBinding
|
||||
|
||||
|
||||
//
|
||||
// LifeCycle
|
||||
//
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return DataBindingUtil.inflate<FragmentZcon1FeedbackBinding>(
|
||||
inflater, R.layout.fragment_zcon1_feedback, container, false
|
||||
).let {
|
||||
binding = it
|
||||
it.root
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
mainActivity?.setToolbarShown(false)
|
||||
mainActivity?.setNavigationShown(true)
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Private API
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Module
|
||||
abstract class Zcon1FeedbackFragmentModule {
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributeZcon1FeedbackFragment(): Zcon1FeedbackFragment
|
||||
}
|
||||
|
|
@ -5,24 +5,48 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import cash.z.android.wallet.ChipBucket
|
||||
import cash.z.android.wallet.PokerChip
|
||||
import cash.z.android.wallet.R
|
||||
import cash.z.android.wallet.databinding.FragmentZcon1HomeBinding
|
||||
import cash.z.android.wallet.di.annotation.FragmentScope
|
||||
import cash.z.android.wallet.ui.adapter.TransactionAdapter
|
||||
import cash.z.android.wallet.ui.dialog.StatusDialog
|
||||
import cash.z.android.wallet.ui.presenter.BalancePresenter
|
||||
import cash.z.android.wallet.ui.presenter.HomePresenterModule
|
||||
import cash.z.android.wallet.ui.presenter.TransactionPresenter
|
||||
import cash.z.android.wallet.ui.presenter.TransactionPresenterModule
|
||||
import cash.z.wallet.sdk.dao.WalletTransaction
|
||||
import cash.z.wallet.sdk.ext.MINERS_FEE_ZATOSHI
|
||||
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
|
||||
import cash.z.wallet.sdk.secure.Wallet
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
/**
|
||||
* Fragment representing the home screen of the app. This is the screen most often seen by the user when launching the
|
||||
* application.
|
||||
*/
|
||||
class Zcon1HomeFragment : BaseFragment(), BalancePresenter.BalanceView {//, SwipeRefreshLayout.OnRefreshListener, HomePresenter.HomeView {
|
||||
class Zcon1HomeFragment : BaseFragment(), BalancePresenter.BalanceView, TransactionPresenter.TransactionView,
|
||||
ChipBucket.OnBucketChangedListener {
|
||||
|
||||
//, SwipeRefreshLayout.OnRefreshListener
|
||||
private lateinit var binding: FragmentZcon1HomeBinding
|
||||
|
||||
@Inject
|
||||
lateinit var transactionPresenter: TransactionPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var chipBucket: ChipBucket
|
||||
|
||||
lateinit var balanceInfo: Wallet.WalletBalance
|
||||
private var transactions: List<WalletTransaction> = emptyList()
|
||||
|
||||
|
||||
//
|
||||
// LifeCycle
|
||||
//
|
||||
|
@ -39,43 +63,119 @@ class Zcon1HomeFragment : BaseFragment(), BalancePresenter.BalanceView {//, Swip
|
|||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.backgroundBalanceImage.setOnClickListener {
|
||||
showStatus()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showStatus() {
|
||||
StatusDialog(balanceInfo.available).show(activity!!.supportFragmentManager, "dialog_status")
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
mainActivity?.setToolbarShown(false)
|
||||
mainActivity?.setNavigationShown(true)
|
||||
mainActivity?.onNavButtonSelected(0)
|
||||
|
||||
binding.recyclerTransactionHistory.apply {
|
||||
layoutManager = LinearLayoutManager(mainActivity, RecyclerView.VERTICAL, false)
|
||||
adapter = TransactionAdapter(R.layout.item_zcon1_transaction_history)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
chipBucket.setOnBucketChangedListener(this)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
mainActivity?.balancePresenter?.addBalanceView(this)
|
||||
chipBucket.setOnBucketChangedListener(this)
|
||||
launch {
|
||||
transactionPresenter.start()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
mainActivity?.balancePresenter?.removeBalanceView(this)
|
||||
chipBucket.removeOnBucketChangedListener(this)
|
||||
transactionPresenter.stop()
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// BalanceView Implementation
|
||||
//
|
||||
|
||||
override fun updateBalance(balanceInfo: Wallet.WalletBalance) {
|
||||
val balance = balanceInfo.available.convertZatoshiToZecString(6).split(".")
|
||||
fun refreshBalance() {
|
||||
val valuePending = chipBucket.valuePending()
|
||||
val balance = (balanceInfo.total + valuePending).convertZatoshiToZecString(6).split(".")
|
||||
binding.textIntegerDigits.text = balance[0]
|
||||
binding.textFractionalDigits.text = ""
|
||||
if (balance.size > 1) {
|
||||
binding.textFractionalDigits.text = ".${balance[1]}"
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Balance listeners
|
||||
//
|
||||
|
||||
override fun updateBalance(balanceInfo: Wallet.WalletBalance) {
|
||||
this.balanceInfo = balanceInfo
|
||||
refreshBalance()
|
||||
}
|
||||
|
||||
override fun onBucketChanged(bucket: ChipBucket) {
|
||||
refreshBalance()
|
||||
refreshTransactions()
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// TransactionView Implementation
|
||||
//
|
||||
|
||||
override fun setTransactions(transactions: List<WalletTransaction>) {
|
||||
this.transactions = transactions
|
||||
refreshTransactions()
|
||||
}
|
||||
|
||||
fun refreshTransactions() {
|
||||
with (binding.recyclerTransactionHistory) {
|
||||
(adapter as TransactionAdapter).submitList(addPokerChips(transactions))
|
||||
postDelayed({
|
||||
smoothScrollToPosition(0)
|
||||
}, 100L)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addPokerChips(transactions: List<WalletTransaction>): MutableList<WalletTransaction> {
|
||||
val mergedTransactions = mutableListOf<WalletTransaction>()
|
||||
chipBucket.forEach { mergedTransactions.add(it.toWalletTransaction()) }
|
||||
mergedTransactions.addAll(transactions)
|
||||
mergedTransactions.sortByDescending {
|
||||
if (!it.isMined && it.isSend) Long.MAX_VALUE else it.timeInSeconds
|
||||
}
|
||||
return mergedTransactions
|
||||
}
|
||||
}
|
||||
|
||||
private fun PokerChip.toWalletTransaction(): WalletTransaction {
|
||||
return WalletTransaction(
|
||||
value = zatoshiValue - MINERS_FEE_ZATOSHI,
|
||||
isSend = true,
|
||||
timeInSeconds = created/1000L,
|
||||
address = if (isRedeemed()) "Redeemed" else "Pending...",
|
||||
memo = "Poker Chip Scanned"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Module
|
||||
abstract class Zcon1HomeFragmentModule {
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector(modules = [HomePresenterModule::class])
|
||||
@ContributesAndroidInjector(modules = [TransactionPresenterModule::class])
|
||||
abstract fun contributeZcon1HomeFragment(): Zcon1HomeFragment
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package cash.z.android.wallet.ui.presenter
|
||||
|
||||
import cash.z.android.wallet.ui.fragment.Zcon1HomeFragment
|
||||
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
|
||||
import cash.z.wallet.sdk.dao.WalletTransaction
|
||||
import cash.z.wallet.sdk.data.*
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.channels.ReceiveChannel
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class TransactionPresenter @Inject constructor(
|
||||
private val view: Zcon1HomeFragment,
|
||||
private val synchronizer: Synchronizer
|
||||
) : Presenter {
|
||||
|
||||
interface TransactionView : PresenterView {
|
||||
fun setTransactions(transactions: List<WalletTransaction>)
|
||||
}
|
||||
|
||||
private var transactionJob: Job? = null
|
||||
|
||||
|
||||
//
|
||||
// LifeCycle
|
||||
//
|
||||
|
||||
override suspend fun start() {
|
||||
Twig.sprout("TransactionPresenter")
|
||||
twig("TransactionPresenter starting!")
|
||||
|
||||
transactionJob?.cancel()
|
||||
transactionJob = Job()
|
||||
// transactionJob = view.launchPurchaseBinder(synchronizer.activeTransactions())
|
||||
transactionJob = view.launchTransactionBinder(synchronizer.allTransactions())
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
twig("TransactionPresenter stopping!")
|
||||
Twig.clip("TransactionPresenter")
|
||||
transactionJob?.cancel()?.also { transactionJob = null }
|
||||
}
|
||||
|
||||
fun CoroutineScope.launchPurchaseBinder(channel: ReceiveChannel<Map<ActiveTransaction, TransactionState>>) = launch {
|
||||
twig("main purchase binder starting!")
|
||||
for (new in channel) {
|
||||
twig("main polled a purchase info")
|
||||
bind(new)
|
||||
}
|
||||
twig("main purchase binder exiting!")
|
||||
}
|
||||
|
||||
fun CoroutineScope.launchTransactionBinder(allTransactions: ReceiveChannel<List<WalletTransaction>>) = launch {
|
||||
twig("transaction binder starting!")
|
||||
for (walletTransactionList in allTransactions) {
|
||||
twig("received ${walletTransactionList.size} transactions for presenting")
|
||||
bind(walletTransactionList)
|
||||
}
|
||||
twig("transaction binder exiting!")
|
||||
}
|
||||
|
||||
//
|
||||
// Events
|
||||
//
|
||||
|
||||
private fun bind(activeTransactions: Map<ActiveTransaction, TransactionState>) {
|
||||
// val newestState = activeTransactions.entries.last().value
|
||||
// if (newestState is TransactionState.Failure) {
|
||||
// view.orderFailed(PurchaseResult.Failure(newestState.reason))
|
||||
// } else {
|
||||
// view.orderUpdated(PurchaseResult.Processing(newestState))
|
||||
// }
|
||||
}
|
||||
|
||||
private fun bind(transactions: List<WalletTransaction>) {
|
||||
twig("binding ${transactions.size} walletTransactions")
|
||||
view.setTransactions(transactions.sortedByDescending {
|
||||
if (!it.isMined && it.isSend) Long.MAX_VALUE else it.timeInSeconds
|
||||
})
|
||||
}
|
||||
|
||||
sealed class PurchaseResult {
|
||||
data class Processing(val state: TransactionState = TransactionState.Creating) : PurchaseResult()
|
||||
data class Failure(val reason: String = "") : PurchaseResult()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Module
|
||||
abstract class TransactionPresenterModule {
|
||||
@Binds
|
||||
abstract fun providePresenter(transactionPresenter: TransactionPresenter): Presenter
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package cash.z.android.wallet.ui.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import cash.z.android.wallet.PokerChip
|
||||
import cash.z.android.wallet.ZcashWalletApplication
|
||||
import cash.z.wallet.sdk.data.twig
|
||||
import com.mixpanel.android.mpmetrics.MixpanelAPI
|
||||
|
||||
object Analytics {
|
||||
// TODO: Move to manifest
|
||||
private const val MIXPANEL_TOKEN = "17e08c2ca8e6d1fe4f88335a2d1635cf"
|
||||
|
||||
@SuppressLint("StaticFieldLeak") // application context is fine to use here
|
||||
private val mixpanel: MixpanelAPI = MixpanelAPI.getInstance(ZcashWalletApplication.instance, MIXPANEL_TOKEN)
|
||||
|
||||
fun clear() {
|
||||
mixpanel.flush()
|
||||
}
|
||||
|
||||
fun trackFunnelStep(funnel: FunnelStep) {
|
||||
twig("@FunnelStep ${funnel.eventName()} : ${funnel.toProperties()}")
|
||||
mixpanel.trackMap(funnel.eventName(), funnel.toProperties())
|
||||
}
|
||||
|
||||
fun trackAction(action: Action) {
|
||||
twig("@Action ${action.eventName()}")
|
||||
mixpanel.trackMap(action.eventName(), mapOf("deviceTimestamp" to action.deviceTimestamp()))
|
||||
}
|
||||
|
||||
/**
|
||||
* An action is an event with no properties
|
||||
*/
|
||||
interface Action {
|
||||
fun eventName(): String {
|
||||
return this.toString()
|
||||
}
|
||||
fun deviceTimestamp(): Long = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
/**
|
||||
* A funnel is an event with properties, typically part of a larger sequence of events.
|
||||
*/
|
||||
interface FunnelStep : Action {
|
||||
fun toProperties(): MutableMap<String, Any>
|
||||
}
|
||||
|
||||
/**
|
||||
* Every tap in the app
|
||||
*/
|
||||
enum class Tap : Action {
|
||||
// Feedback Screen
|
||||
TAPPED_GIVE_FEEDBACK,
|
||||
TAPPED_CANCEL_FEEDBACK,
|
||||
TAPPED_SUBMIT_FEEDBACK,
|
||||
|
||||
// Received Screen
|
||||
TAPPED_COPY_ADDRESS,
|
||||
|
||||
// Scan
|
||||
TAPPED_SCAN_QR_HOME,
|
||||
TAPPED_SCAN_QR_SEND,
|
||||
TAPPED_SCAN_QR_ONBOARDING
|
||||
}
|
||||
|
||||
sealed class PokerChipFunnel(val pokerChip: PokerChip, val success: Boolean = true) : FunnelStep {
|
||||
class Collected(chip: PokerChip) : PokerChipFunnel(chip)
|
||||
class Aborted(chip: PokerChip) : PokerChipFunnel(chip)
|
||||
class StartSweep(chip: PokerChip) : PokerChipFunnel(chip)
|
||||
class Swept(chip: PokerChip, val error: String?) : PokerChipFunnel(chip, error == null) {
|
||||
override fun toProperties(): MutableMap<String, Any> {
|
||||
return super.toProperties().apply { this["errorMessage"] = "$error" }
|
||||
}
|
||||
}
|
||||
class Redeemed(chip: PokerChip, isSuccess: Boolean) : PokerChipFunnel(chip, isSuccess)
|
||||
|
||||
override fun toProperties(): MutableMap<String, Any> {
|
||||
return pokerChip.toProperties().apply {
|
||||
this["funnelStep"] = eventName()
|
||||
this["isSuccess"] = "$success"
|
||||
this["deviceTimestamp"] = deviceTimestamp()
|
||||
}
|
||||
}
|
||||
|
||||
override fun eventName(): String {
|
||||
return "${PokerChipFunnel::class.simpleName}.${javaClass.simpleName}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun PokerChip.toProperties(): MutableMap<String, Any> {
|
||||
return mutableMapOf(
|
||||
"PokerChip.id" to maskedId,
|
||||
"PokerChip.created" to created,
|
||||
"PokerChip.redeemed" to redeemed
|
||||
)
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package cash.z.android.wallet.ui.util
|
||||
|
||||
import cash.z.android.wallet.PokerChip
|
||||
import cash.z.android.wallet.ZcashWalletApplication
|
||||
import cash.z.android.wallet.data.StaticTransactionRepository
|
||||
import cash.z.android.wallet.extention.toDbPath
|
||||
import cash.z.android.wallet.extention.tryIgnore
|
||||
import cash.z.android.wallet.sample.SampleProperties
|
||||
|
@ -8,6 +10,7 @@ import cash.z.wallet.sdk.data.PollingTransactionRepository
|
|||
import cash.z.wallet.sdk.data.TransactionRepository
|
||||
import cash.z.wallet.sdk.data.Twig
|
||||
import cash.z.wallet.sdk.data.twig
|
||||
import cash.z.wallet.sdk.ext.MINERS_FEE_ZATOSHI
|
||||
import cash.z.wallet.sdk.jni.RustBackendWelding
|
||||
import cash.z.wallet.sdk.rpc.Service
|
||||
import cash.z.wallet.sdk.secure.Wallet
|
||||
|
@ -25,16 +28,12 @@ class Broom(
|
|||
private val appWallet: Wallet
|
||||
) {
|
||||
|
||||
val repository = PollingTransactionRepository(
|
||||
ZcashWalletApplication.instance,
|
||||
DATA_DB_NAME,
|
||||
SampleProperties.DEFAULT_TRANSACTION_POLL_FREQUENCY_MILLIS
|
||||
)
|
||||
private val repository = StaticTransactionRepository(DATA_DB_NAME, rustBackend)
|
||||
|
||||
/**
|
||||
* Gets the seed from the provider and sweeps the associated wallet
|
||||
*/
|
||||
suspend fun sweep(walletSeedProvider: ReadOnlyProperty<Any?, ByteArray>): Boolean = withContext(Dispatchers.IO){
|
||||
suspend fun sweep(walletSeedProvider: ReadOnlyProperty<Any?, ByteArray>, amount: Long = 100_000_000L - MINERS_FEE_ZATOSHI): String? = withContext(Dispatchers.IO){
|
||||
Twig.sprout("sweep")
|
||||
// copy cache db
|
||||
// cloneCachedBlocks() // optional?
|
||||
|
@ -49,7 +48,6 @@ class Broom(
|
|||
Twig.clip("broom-scan")
|
||||
if (scanResult) {
|
||||
twig("successfully scanned blocks! Ready to sweep!!!")
|
||||
val amount = 100_000_000L - 10_000L
|
||||
val memo = "swag shirt test"
|
||||
val address = "ztestsapling1yu2zy9aanf8pjf2qvm4qmn4k6q57y2d9fcs3vz0guthxx3m2aq57qm6hkx0580m9u9635xh6ttr"
|
||||
// val address = appWallet.getAddress()
|
||||
|
@ -59,10 +57,11 @@ class Broom(
|
|||
} else {
|
||||
twig("failed to scan!")
|
||||
}
|
||||
true
|
||||
null
|
||||
} catch (t: Throwable) {
|
||||
twig("Failed to sweep due to: ${t.message}")
|
||||
false
|
||||
val message = "Failed to sweep due to: ${t.message}"
|
||||
twig(message)
|
||||
message
|
||||
} finally {
|
||||
Twig.clip("sweep")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="@color/text_light_dimmed"
|
||||
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="M6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6L6,2zM13,9L13,3.5L18.5,9L13,9z"/>
|
||||
</vector>
|
|
@ -14,7 +14,7 @@
|
|||
<path
|
||||
android:name="_R_G_L_1_G_D_0_P_0"
|
||||
android:fillAlpha="1"
|
||||
android:fillColor="#009688"
|
||||
android:fillColor="@color/transaction_received_color"
|
||||
android:fillType="nonZero"
|
||||
android:pathData=" M66 0 C66,36.45 36.45,66 0,66 C-36.45,66 -66,36.45 -66,0 C-66,-36.45 -36.45,-66 0,-66 C36.45,-66 66,-36.45 66,0c " />
|
||||
</group>
|
||||
|
@ -29,7 +29,7 @@
|
|||
<path
|
||||
android:name="_R_G_L_0_G_D_0_P_0"
|
||||
android:fillAlpha="1"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillColor="@color/transaction_icon_fg_color"
|
||||
android:fillType="nonZero"
|
||||
android:pathData=" M-17.21 17.58 C-17.21,17.58 -25.85,9.26 -25.85,9.26 C-25.85,9.26 0,-17.58 0,-17.58 C0,-17.58 25.85,9.26 25.85,9.26 C25.85,9.26 17.21,17.58 17.21,17.58 C17.21,17.58 0,-0.28 0,-0.28 C0,-0.28 -17.21,17.58 -17.21,17.58c " />
|
||||
</group>
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
android:translateX="80"
|
||||
android:translateY="80">
|
||||
<path
|
||||
android:name="_R_G_L_3_G_D_0_P_0"
|
||||
android:name="_R_G_L_1_G_D_0_P_0"
|
||||
android:fillAlpha="1"
|
||||
android:fillColor="#f5a623"
|
||||
android:fillColor="@color/transaction_send_color"
|
||||
android:fillType="nonZero"
|
||||
android:pathData=" M55.59 0 C55.59,30.7 30.7,55.59 0,55.59 C-30.7,55.59 -55.58,30.7 -55.58,0 C-55.58,-30.7 -30.7,-55.58 0,-55.58 C30.7,-55.58 55.59,-30.7 55.59,0c " />
|
||||
android:pathData=" M66 0 C66,36.45 36.45,66 0,66 C-36.45,66 -66,36.45 -66,0 C-66,-36.45 -36.45,-66 0,-66 C36.45,-66 66,-36.45 66,0c " />
|
||||
</group>
|
||||
<group
|
||||
android:name="_R_G_L_2_G"
|
||||
|
@ -22,7 +22,7 @@
|
|||
<path
|
||||
android:name="_R_G_L_2_G_D_0_P_0"
|
||||
android:fillAlpha="1"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillColor="@color/transaction_icon_fg_color"
|
||||
android:fillType="nonZero"
|
||||
android:pathData=" M10.76 -10.09 C10.76,-10.09 -4.17,8.75 -4.17,8.75 C-4.17,8.75 5.67,25.78 5.67,25.78 C7.07,27.55 9.88,27.06 10.6,24.92 C10.6,24.92 26.8,-23.19 26.8,-23.19 C27.55,-25.42 25.42,-27.55 23.19,-26.8 C23.19,-26.8 -24.92,-10.6 -24.92,-10.6 C-27.06,-9.88 -27.55,-7.07 -25.77,-5.67 C-25.77,-5.67 -8.75,4.18 -8.75,4.18 C-8.75,4.18 10.09,-10.76 10.09,-10.76 C10.54,-11.12 11.12,-10.54 10.76,-10.09c " />
|
||||
</group>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
android:pathData="M0,20L3,20L3,7L0,7L0,20ZM19,13.191L16.482,17L8,17L8,7.943L10.61,3.008C11.068,3.068 11.422,3.458 11.422,3.929L11.422,9.071L19,9.071L19,13.191ZM19.423,6.071L14.422,6.071L14.422,3.929C14.422,1.763 12.657,0 10.487,0L8.808,0L5,7.199L5,20L16.726,20C17.584,20 18.383,19.566 18.862,18.841L21.558,14.763C21.847,14.326 22,13.815 22,13.287L22,8.705C22,7.253 20.844,6.071 19.423,6.071Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="@color/colorPrimary"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#00000000"/>
|
||||
</vector>
|
|
@ -0,0 +1,138 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/container_status_dialog"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="16dp">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/swag_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fillViewport="true"
|
||||
app:layout_constraintEnd_toEndOf="@+id/button_purchase"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:fontFamily="sans-serif-thin"
|
||||
android:text="Status"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_subtitle_1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_available"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Amount available:"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_body_2"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/status_header" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_syncing"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="Amount syncing:"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_body_2"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/text_available" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_pending"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="Pending chip amount:"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_body_2"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/text_syncing" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:background="@color/text_light_dimmed"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/text_pending" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_status_detail"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="Total Wallet Value:"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_body_2"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/divider" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/summary_header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="Summary"
|
||||
android:textAllCaps="true"
|
||||
android:fontFamily="sans-serif-thin"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_body_2"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/text_status_detail" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="We are waiting on funds to get enough network confirmations to allow for them to be spent."
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_body_2"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/summary_header" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_ok"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Zcash.Button.Alert"
|
||||
android:text="@android:string/ok"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/swag_container" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
|
@ -11,6 +11,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/fragment_receive_background"
|
||||
android:onClick="copyAddress"
|
||||
tools:context=".ui.fragment.ReceiveFragment">
|
||||
|
||||
<ImageView
|
||||
|
|
|
@ -137,16 +137,6 @@
|
|||
app:layout_constraintTop_toTopOf="@id/transition_active_transaction_bg" />
|
||||
|
||||
<!-- Currency swap symbol -->
|
||||
<ImageView
|
||||
android:id="@+id/image_swap_currency"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="18dp"
|
||||
android:tint="@color/text_light"
|
||||
app:layout_constraintBottom_toBottomOf="@id/transition_active_transaction_bg"
|
||||
app:layout_constraintEnd_toEndOf="@id/transition_active_transaction_bg"
|
||||
app:layout_constraintTop_toTopOf="@id/transition_active_transaction_bg"
|
||||
app:srcCompat="@drawable/ic_import_export_black" />
|
||||
|
||||
<!-- Input: Header -->
|
||||
<EditText
|
||||
|
@ -172,6 +162,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/text_light"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintEnd_toEndOf="@id/text_value_header"
|
||||
app:layout_constraintStart_toStartOf="@id/text_value_header"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_value_header"
|
||||
|
@ -223,6 +214,7 @@
|
|||
android:text="$"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="8dp"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintEnd_toStartOf="@id/text_value_subheader"
|
||||
app:layout_constraintTop_toTopOf="@id/image_zec_symbol_subheader" />
|
||||
|
||||
|
@ -274,20 +266,6 @@
|
|||
app:layout_constraintTop_toTopOf="@id/input_zcash_address"
|
||||
app:srcCompat="@drawable/ic_qrcode_24dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_address_shortcut"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingEnd="6dp"
|
||||
android:paddingBottom="24dp"
|
||||
android:tint="@color/zcashPurple_accent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/image_scan_qr"
|
||||
app:layout_constraintEnd_toStartOf="@id/image_scan_qr"
|
||||
app:layout_constraintTop_toTopOf="@id/image_scan_qr"
|
||||
app:srcCompat="@drawable/ic_content_paste_black_24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_to_label"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -309,6 +287,7 @@
|
|||
android:maxLength="@integer/memo_max_length"
|
||||
android:padding="16dp"
|
||||
android:textColor="@color/text_light"
|
||||
tools:text="heere is a heck of a lot of text"
|
||||
app:backgroundTint="#1E1331"
|
||||
app:layout_constraintBottom_toTopOf="@id/button_send_zec"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
|
||||
|
@ -355,7 +334,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="0 / 512"
|
||||
android:textColor="@color/zcashGray"
|
||||
android:textColor="@android:color/transparent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/text_area_memo"
|
||||
app:layout_constraintEnd_toEndOf="@id/divider_memo"
|
||||
app:layout_constraintTop_toBottomOf="@id/divider_memo" />
|
||||
|
@ -507,9 +486,6 @@
|
|||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="
|
||||
transition_active_transaction_bg,
|
||||
text_value_subheader,
|
||||
text_dollar_symbol_subheader,
|
||||
image_zec_symbol_subheader,
|
||||
image_zec_symbol_header,
|
||||
text_dollar_symbol_header,
|
||||
text_amount_background,
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout
|
||||
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">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_bg_parallax"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:alpha="0.5"
|
||||
android:scaleType="centerCrop"
|
||||
app:srcCompat="@drawable/bg_parrallax" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/layout_cart_fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline_content_start"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_begin="32dp" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline_content_end"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_end="32dp" />
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_header_icon"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginTop="64dp"
|
||||
android:background="@drawable/background_circle"
|
||||
android:padding="20dp"
|
||||
android:scaleType="fitXY"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_thumbup"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_feedback_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:includeFontPadding="false"
|
||||
android:text="Feedback!"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_h4"
|
||||
app:layout_constraintStart_toEndOf="@id/image_header_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/image_header_icon" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_feedback_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="Rank your experience"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_body_1"
|
||||
app:layout_constraintBottom_toBottomOf="@id/image_header_icon"
|
||||
app:layout_constraintStart_toEndOf="@id/image_header_icon" />
|
||||
|
||||
<View
|
||||
android:id="@+id/background_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@color/zcashBlack_54"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_feedback_subtitle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/feedback_exp_1"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:background="@drawable/background_circle"
|
||||
android:gravity="center"
|
||||
android:text="1"
|
||||
android:textColor="@color/colorPrimaryDark"
|
||||
android:textSize="30dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/background_buttons"
|
||||
app:layout_constraintEnd_toStartOf="@id/feedback_exp_2"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
|
||||
app:layout_constraintTop_toTopOf="@id/background_buttons" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/feedback_exp_2"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:background="@drawable/background_circle"
|
||||
android:gravity="center"
|
||||
android:text="2"
|
||||
android:textColor="@color/colorPrimaryDark"
|
||||
android:textSize="30dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/background_buttons"
|
||||
app:layout_constraintEnd_toStartOf="@id/feedback_exp_3"
|
||||
app:layout_constraintStart_toEndOf="@id/feedback_exp_1"
|
||||
app:layout_constraintTop_toTopOf="@id/background_buttons" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/feedback_exp_3"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:background="@drawable/background_circle"
|
||||
android:gravity="center"
|
||||
android:text="3"
|
||||
android:textColor="@color/colorPrimaryDark"
|
||||
android:textSize="30dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/background_buttons"
|
||||
app:layout_constraintEnd_toStartOf="@id/feedback_exp_4"
|
||||
app:layout_constraintStart_toEndOf="@id/feedback_exp_2"
|
||||
app:layout_constraintTop_toTopOf="@id/background_buttons" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/feedback_exp_4"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:background="@drawable/background_circle"
|
||||
android:gravity="center"
|
||||
android:text="4"
|
||||
android:textColor="@color/colorPrimaryDark"
|
||||
android:textSize="30dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/background_buttons"
|
||||
app:layout_constraintEnd_toStartOf="@id/feedback_exp_5"
|
||||
app:layout_constraintStart_toEndOf="@id/feedback_exp_3"
|
||||
app:layout_constraintTop_toTopOf="@id/background_buttons" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/feedback_exp_5"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:background="@drawable/background_circle"
|
||||
android:gravity="center"
|
||||
android:text="5"
|
||||
android:textColor="@color/colorPrimaryDark"
|
||||
android:textSize="30dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/background_buttons"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
|
||||
app:layout_constraintStart_toEndOf="@id/feedback_exp_4"
|
||||
app:layout_constraintTop_toTopOf="@id/background_buttons" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_question_1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/feedback_question_1"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_body_1"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
|
||||
app:layout_constraintTop_toBottomOf="@id/background_buttons" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/input_question_1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:hint="@string/feedback_hint_1"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_question_1">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="top"
|
||||
android:lines="3" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_question_2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/feedback_question_2"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_body_1"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
|
||||
app:layout_constraintTop_toBottomOf="@id/input_question_1" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/input_question_2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:hint="@string/feedback_hint_2"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_question_2">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="top"
|
||||
android:lines="3" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_question_3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/feedback_question_3"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_body_1"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
|
||||
app:layout_constraintTop_toBottomOf="@id/input_question_2" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/input_question_3"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:hint="@string/feedback_hint_3"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_question_3">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="top"
|
||||
android:singleLine="true" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_cancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:onClick="onFeedbackCancel"
|
||||
android:text="@string/cancel"
|
||||
android:textColor="@color/text_light"
|
||||
app:layout_constraintEnd_toStartOf="@id/button_submit"
|
||||
app:layout_constraintTop_toBottomOf="@id/input_question_3" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_submit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onFeedbackSubmit"
|
||||
android:text="Submit"
|
||||
android:textColor="@color/text_light"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
|
||||
app:layout_constraintTop_toTopOf="@id/button_cancel" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/extra_padding_for_scrolling"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/button_submit"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
</FrameLayout>
|
||||
</layout>
|
|
@ -46,11 +46,13 @@
|
|||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/view_pager_firstrun"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:overScrollMode="never"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/progress_firstrun"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_firstrun"
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<layout
|
||||
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">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/content_home"
|
||||
|
@ -36,28 +38,56 @@
|
|||
app:srcCompat="@drawable/ic_bg_path" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/background_balance_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:alpha="0.6"
|
||||
android:scaleType="fitXY"
|
||||
app:layout_constraintHeight_default="percent"
|
||||
app:layout_constraintHeight_percent="0.38"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:alpha="0.6"
|
||||
app:srcCompat="@drawable/bg_mountains" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_integer_digits"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="25"
|
||||
android:includeFontPadding="false"
|
||||
android:text="25"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_h1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/background_buttons"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/background_buttons"/>
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<!-- Status -->
|
||||
<TextView
|
||||
android:id="@+id/text_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="status: syncing"
|
||||
android:paddingBottom="3dp"
|
||||
android:includeFontPadding="false"
|
||||
android:fontFamily="sans-serif-thin"
|
||||
android:textSize="@dimen/text_size_subtitle_1"
|
||||
android:textColor="@color/text_light"
|
||||
app:layout_constraintBottom_toTopOf="@id/background_buttons"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_fractional_digits" />
|
||||
|
||||
<View
|
||||
android:id="@+id/indicator_status"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/background_circle"
|
||||
android:backgroundTint="@color/zcashRed"
|
||||
app:layout_constraintTop_toTopOf="@id/text_status"
|
||||
app:layout_constraintBottom_toBottomOf="@id/text_status"
|
||||
app:layout_constraintEnd_toStartOf="@id/text_status"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_fractional_digits"
|
||||
|
@ -73,16 +103,16 @@
|
|||
android:id="@+id/text_taz"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="TA"
|
||||
android:fontFamily="sans-serif-thin"
|
||||
android:textColor="@color/colorAccent"
|
||||
android:includeFontPadding="false"
|
||||
android:textSize="24dp"
|
||||
android:alpha="0.5"
|
||||
android:fontFamily="sans-serif-thin"
|
||||
android:includeFontPadding="false"
|
||||
android:text="TA"
|
||||
android:textColor="@color/colorAccent"
|
||||
android:textSize="24dp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintTop_toTopOf="@id/image_zec_symbol"
|
||||
app:layout_constraintBottom_toBottomOf="@id/image_zec_symbol"
|
||||
app:layout_constraintEnd_toStartOf="@id/image_zec_symbol"/>
|
||||
app:layout_constraintEnd_toStartOf="@id/image_zec_symbol"
|
||||
app:layout_constraintTop_toTopOf="@id/image_zec_symbol" />
|
||||
|
||||
<!-- Zec symbol -->
|
||||
<ImageView
|
||||
|
@ -91,9 +121,10 @@
|
|||
android:layout_height="24dp"
|
||||
android:layout_marginTop="18dp"
|
||||
android:tint="@color/colorAccent"
|
||||
app:layout_constraintTop_toTopOf="@id/text_integer_digits"
|
||||
app:layout_constraintEnd_toStartOf="@id/text_integer_digits"
|
||||
app:layout_constraintTop_toTopOf="@id/text_integer_digits"
|
||||
app:srcCompat="@drawable/ic_zec_symbol" />
|
||||
|
||||
<View
|
||||
android:id="@+id/background_buttons"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -129,5 +160,21 @@
|
|||
app:layout_constraintStart_toEndOf="@id/button_scan"
|
||||
app:layout_constraintTop_toTopOf="@id/background_buttons" />
|
||||
|
||||
<!-- Transactions -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_transaction_history"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="120dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/background_buttons"
|
||||
tools:itemCount="15"
|
||||
tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_zcon1_transaction_history"
|
||||
tools:orientation="vertical" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
|
@ -6,7 +6,8 @@
|
|||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/container_welcome"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/bg_parrallax">
|
||||
|
||||
<!-- Bottom Navigation (Custom) -->
|
||||
|
||||
|
@ -14,6 +15,7 @@
|
|||
android:id="@+id/lottie_ecc_logo"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:padding="72dp"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
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/container_transaction"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:background="#F0291441"
|
||||
android:elevation="1dp"
|
||||
android:padding="16dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_transaction_amount"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:textColor="@color/zcashPurple_accent"
|
||||
android:textSize="@dimen/text_size_h6"
|
||||
app:layout_constraintBottom_toTopOf="@id/text_transaction_timestamp"
|
||||
app:layout_constraintEnd_toEndOf="@id/text_transaction_timestamp"
|
||||
app:layout_constraintStart_toStartOf="@id/text_transaction_timestamp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="+ 4.244" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_transaction_timestamp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-thin"
|
||||
android:gravity="end"
|
||||
android:maxEms="6"
|
||||
android:minEms="6"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_body_2"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_transaction_amount"
|
||||
tools:text="8/23 3:24pm" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_transaction_type"
|
||||
android:layout_width="62dp"
|
||||
android:layout_height="62dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:scaleType="fitXY"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/text_transaction_timestamp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_received_transaction" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_transaction_address"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif-thin"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="3dp"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="@dimen/text_size_body_2"
|
||||
app:layout_constraintBottom_toTopOf="@id/text_transaction_memo"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/image_transaction_type"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="ztestsapling1yu2zy9aanf8pjf2qvm4qmn4k6q57y2d" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_transaction_memo"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableLeft="@drawable/ic_memo"
|
||||
android:drawablePadding="4dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif-thin"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/text_light_dimmed"
|
||||
android:textSize="@dimen/text_size_body_2"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/text_transaction_address"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_transaction_address"
|
||||
tools:text="ECC Swag Journal" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -109,8 +109,8 @@
|
|||
android:label="@string/destination_title_settings"
|
||||
tools:layout="@layout/fragment_placeholder" />
|
||||
<fragment
|
||||
android:id="@+id/nav_scan_fragment"
|
||||
android:name="cash.z.android.wallet.ui.fragment.Zcon1ScanFragment"
|
||||
tools:layout="@layout/fragment_zcon1_scan" />
|
||||
android:id="@+id/nav_feedback_fragment"
|
||||
android:name="cash.z.android.wallet.ui.fragment.Zcon1FeedbackFragment"
|
||||
tools:layout="@layout/fragment_zcon1_feedback" />
|
||||
|
||||
</navigation>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -57,7 +57,7 @@
|
|||
|
||||
<!-- association -->
|
||||
<color name="send_associated">@color/colorAccent</color>
|
||||
<color name="receive_associated">@color/colorPrimary</color> <!-- design decision: when you get money it should be associated with green -->
|
||||
<color name="receive_associated">@color/zcashPurple_accent</color> <!-- design decision: when you get money it should be associated with green -->
|
||||
<color name="request_associated">@color/zcashBlue</color>
|
||||
|
||||
<!-- icons -->
|
||||
|
@ -68,6 +68,7 @@
|
|||
<color name="fab_closed_color">@color/colorPrimary</color>
|
||||
<color name="fab_open_color">@color/colorPrimaryDark</color>
|
||||
|
||||
|
||||
<!-- text -->
|
||||
<color name="text_light">@color/zcashWhite</color>
|
||||
<color name="text_light_dimmed">@color/zcashWhite_50</color>
|
||||
|
@ -102,4 +103,9 @@
|
|||
<color name="receive_shield">@color/zcashPurple_dark</color>
|
||||
<color name="receive_segments">@color/colorAccent</color>
|
||||
|
||||
|
||||
<!-- Zcon1 -->
|
||||
<color name="transaction_icon_fg_color">@color/zcashBlack_54</color>
|
||||
<color name="transaction_send_color">#FFE082</color>
|
||||
<color name="transaction_received_color">#80CBC4</color>
|
||||
</resources>
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
<string name="ignore">ignore</string>
|
||||
<string name="details">details</string>
|
||||
<string name="cancel">cancel</string>
|
||||
<string name="wait">Wait</string>
|
||||
<string name="abort">Abort</string>
|
||||
<string name="cancelled">cancelled</string>
|
||||
<string name="ok_allcaps">OK</string>
|
||||
<string name="zec_abbreviation">ZECC</string>
|
||||
|
@ -108,6 +110,14 @@
|
|||
<string name="swag_tee_description">The Zcash Company has rebranded as The Electric Coin Company to help clear up confusion between the coin and the company. Now Zcash Co. is just ECC and you can be the first to sport the new logo :)\n\nLimited quantity of sizes.</string>
|
||||
<string name="swag_pad_description">Take notes like a pro with this ECC-branded moleskin notebook. There are only a few of these, so beg your friends for their extra TAZ and buy one quickly!\n\nVery limited supplies.</string>
|
||||
|
||||
<!-- Feedback -->
|
||||
<string name="feedback_question_1">Any details you\'d like to share?</string>
|
||||
<string name="feedback_question_2">How do you normally use Zcash?</string>
|
||||
<string name="feedback_question_3">Interested in being a beta tester?</string>
|
||||
<string name="feedback_hint_1">My experience was . . .</string>
|
||||
<string name="feedback_hint_2">I use Zcash . . .</string>
|
||||
<string name="feedback_hint_3">email (optional)</string>
|
||||
|
||||
|
||||
<string-array name="demo_user_names">
|
||||
<item>Alice</item>
|
||||
|
|
Loading…
Reference in New Issue