Merge pull request #62 from zcash/feature/profile-screen
Feature/profile screen
This commit is contained in:
commit
cb9e6cc4b4
|
@ -9,7 +9,7 @@ apply plugin: 'com.google.gms.google-services'
|
|||
|
||||
archivesBaseName = 'zcash-android-wallet'
|
||||
group = 'cash.z.ecc.android'
|
||||
version = '1.0.0-alpha04'
|
||||
version = '1.0.0-alpha05'
|
||||
|
||||
android {
|
||||
compileSdkVersion Deps.compileSdkVersion
|
||||
|
@ -19,7 +19,7 @@ android {
|
|||
applicationId 'cash.z.ecc.android'
|
||||
minSdkVersion Deps.minSdkVersion
|
||||
targetSdkVersion Deps.targetSdkVersion
|
||||
versionCode = 1_00_00_004
|
||||
versionCode = 1_00_00_005
|
||||
// last digits are alpha(0XX) beta(2XX) rc(4XX) release(8XX). Ex: 1_08_04_401 is an release candidate build of version 1.8.4 and 1_08_04_800 would be the final release.
|
||||
versionName = "$version"
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
|
|
@ -7,13 +7,21 @@ import androidx.camera.camera2.Camera2Config
|
|||
import androidx.camera.core.CameraXConfig
|
||||
import cash.z.ecc.android.di.component.AppComponent
|
||||
import cash.z.ecc.android.di.component.DaggerAppComponent
|
||||
import cash.z.ecc.android.feedback.Feedback
|
||||
import cash.z.ecc.android.feedback.FeedbackCoordinator
|
||||
import cash.z.wallet.sdk.ext.TroubleshootingTwig
|
||||
import cash.z.wallet.sdk.ext.Twig
|
||||
import cash.z.wallet.sdk.ext.twig
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
|
||||
class ZcashWalletApp : Application(), CameraXConfig.Provider {
|
||||
|
||||
@Inject
|
||||
lateinit var feedbackCoordinator: FeedbackCoordinator
|
||||
|
||||
var creationTime: Long = 0
|
||||
private set
|
||||
|
||||
|
@ -26,7 +34,8 @@ class ZcashWalletApp : Application(), CameraXConfig.Provider {
|
|||
super.onCreate()
|
||||
|
||||
component = DaggerAppComponent.factory().create(this)
|
||||
Thread.setDefaultUncaughtExceptionHandler(ExceptionReporter(Thread.getDefaultUncaughtExceptionHandler()))
|
||||
component.inject(this)
|
||||
Thread.setDefaultUncaughtExceptionHandler(ExceptionReporter(feedbackCoordinator, Thread.getDefaultUncaughtExceptionHandler()))
|
||||
Twig.plant(TroubleshootingTwig())
|
||||
}
|
||||
|
||||
|
@ -44,11 +53,21 @@ class ZcashWalletApp : Application(), CameraXConfig.Provider {
|
|||
lateinit var component: AppComponent
|
||||
}
|
||||
|
||||
class ExceptionReporter(val ogHandler: Thread.UncaughtExceptionHandler) : Thread.UncaughtExceptionHandler {
|
||||
/**
|
||||
* @param feedbackCoordinator inject a provider so that if a crash happens before configuration
|
||||
* is complete, we can lazily initialize all the feedback objects at this moment so that we
|
||||
* don't have to add any time to startup.
|
||||
*/
|
||||
class ExceptionReporter(private val coordinator: FeedbackCoordinator, private val ogHandler: Thread.UncaughtExceptionHandler) : Thread.UncaughtExceptionHandler {
|
||||
override fun uncaughtException(t: Thread?, e: Throwable?) {
|
||||
// trackCrash(e, "Top-level exception wasn't caught by anything else!")
|
||||
// Analytics.clear()
|
||||
twig("Uncaught Exception: $e")
|
||||
coordinator.feedback.report(e)
|
||||
coordinator.flush()
|
||||
// can do this if necessary but first verify that we need it
|
||||
runBlocking {
|
||||
coordinator.await()
|
||||
coordinator.feedback.stop()
|
||||
}
|
||||
ogHandler.uncaughtException(t, e)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import javax.inject.Singleton
|
|||
@Singleton
|
||||
@Component(modules = [AppModule::class])
|
||||
interface AppComponent {
|
||||
fun inject(zcashWalletApp: ZcashWalletApp)
|
||||
|
||||
// Subcomponents
|
||||
fun mainActivitySubcomponent(): MainActivitySubcomponent.Factory
|
||||
fun synchronizerSubcomponent(): SynchronizerSubcomponent.Factory
|
||||
|
|
|
@ -3,13 +3,11 @@ package cash.z.ecc.android.di.module
|
|||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import cash.z.ecc.android.ZcashWalletApp
|
||||
import cash.z.ecc.android.di.component.InitializerSubcomponent
|
||||
import cash.z.ecc.android.di.component.MainActivitySubcomponent
|
||||
import cash.z.ecc.android.di.component.SynchronizerSubcomponent
|
||||
import cash.z.wallet.sdk.Initializer
|
||||
import cash.z.ecc.android.feedback.*
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.Reusable
|
||||
import dagger.multibindings.IntoSet
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module(subcomponents = [MainActivitySubcomponent::class])
|
||||
|
@ -23,4 +21,40 @@ class AppModule {
|
|||
@Singleton
|
||||
fun provideClipboard(context: Context) =
|
||||
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
|
||||
|
||||
//
|
||||
// Feedback
|
||||
//
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideFeedback(): Feedback = Feedback()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideFeedbackCoordinator(
|
||||
feedback: Feedback,
|
||||
defaultObservers: Set<@JvmSuppressWildcards FeedbackCoordinator.FeedbackObserver>
|
||||
): FeedbackCoordinator = FeedbackCoordinator(feedback, defaultObservers)
|
||||
|
||||
|
||||
//
|
||||
// Default Feedback Observer Set
|
||||
//
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@IntoSet
|
||||
fun provideFeedbackFile(): FeedbackCoordinator.FeedbackObserver = FeedbackFile()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@IntoSet
|
||||
fun provideFeedbackConsole(): FeedbackCoordinator.FeedbackObserver = FeedbackConsole()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@IntoSet
|
||||
fun provideFeedbackMixpanel(): FeedbackCoordinator.FeedbackObserver = FeedbackMixpanel()
|
||||
}
|
||||
|
|
|
@ -1,44 +1,10 @@
|
|||
package cash.z.ecc.android.di.module
|
||||
|
||||
import cash.z.ecc.android.di.annotation.ActivityScope
|
||||
import cash.z.ecc.android.di.component.InitializerSubcomponent
|
||||
import cash.z.ecc.android.di.component.SynchronizerSubcomponent
|
||||
import cash.z.ecc.android.feedback.*
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.multibindings.IntoSet
|
||||
|
||||
@Module(includes = [ViewModelsActivityModule::class], subcomponents = [SynchronizerSubcomponent::class, InitializerSubcomponent::class])
|
||||
class MainActivityModule {
|
||||
|
||||
@Provides
|
||||
@ActivityScope
|
||||
fun provideFeedback(): Feedback = Feedback()
|
||||
|
||||
@Provides
|
||||
@ActivityScope
|
||||
fun provideFeedbackCoordinator(
|
||||
feedback: Feedback,
|
||||
defaultObservers: Set<@JvmSuppressWildcards FeedbackCoordinator.FeedbackObserver>
|
||||
): FeedbackCoordinator = FeedbackCoordinator(feedback, defaultObservers)
|
||||
|
||||
|
||||
//
|
||||
// Default Feedback Observer Set
|
||||
//
|
||||
|
||||
@Provides
|
||||
@ActivityScope
|
||||
@IntoSet
|
||||
fun provideFeedbackFile(): FeedbackCoordinator.FeedbackObserver = FeedbackFile()
|
||||
|
||||
@Provides
|
||||
@ActivityScope
|
||||
@IntoSet
|
||||
fun provideFeedbackConsole(): FeedbackCoordinator.FeedbackObserver = FeedbackConsole()
|
||||
|
||||
@Provides
|
||||
@ActivityScope
|
||||
@IntoSet
|
||||
fun provideFeedbackMixpanel(): FeedbackCoordinator.FeedbackObserver = FeedbackMixpanel()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package cash.z.ecc.android.ext
|
||||
|
||||
import android.text.Spannable
|
||||
import android.text.Spanned
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import androidx.core.text.toSpannable
|
||||
|
||||
fun String.toColoredSpan(colorResId: Int, coloredPortion: String): Spannable {
|
||||
return toSpannable().apply {
|
||||
val start = this@toColoredSpan.indexOf(coloredPortion)
|
||||
setSpan(ForegroundColorSpan(colorResId.toAppColor()), start, start + coloredPortion.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
}
|
|
@ -13,8 +13,10 @@ import android.view.ViewGroup
|
|||
import android.view.WindowManager
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.findNavController
|
||||
|
@ -149,7 +151,7 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
fun copyAddress(view: View) {
|
||||
fun copyAddress(view: View? = null) {
|
||||
lifecycleScope.launch {
|
||||
clipboard.setPrimaryClip(
|
||||
ClipData.newPlainText(
|
||||
|
@ -161,6 +163,18 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
fun preventBackPress(fragment: Fragment) {
|
||||
onFragmentBackPressed(fragment){}
|
||||
}
|
||||
|
||||
fun onFragmentBackPressed(fragment: Fragment, block: () -> Unit) {
|
||||
onBackPressedDispatcher.addCallback(fragment, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
block()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun showMessage(message: String, action: String) {
|
||||
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
|
|
@ -9,10 +9,7 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import cash.z.ecc.android.ui.MainActivity
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
abstract class BaseFragment<T : ViewBinding> : Fragment() {
|
||||
val mainActivity: MainActivity? get() = activity as MainActivity?
|
||||
|
@ -33,7 +30,7 @@ abstract class BaseFragment<T : ViewBinding> : Fragment() {
|
|||
override fun onResume() {
|
||||
super.onResume()
|
||||
resumedScope = lifecycleScope.coroutineContext.let {
|
||||
CoroutineScope(it + SupervisorJob(it[Job]))
|
||||
CoroutineScope(Dispatchers.Main + SupervisorJob(it[Job]))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,4 +42,10 @@ abstract class BaseFragment<T : ViewBinding> : Fragment() {
|
|||
// inflate is static in the ViewBinding class so we can't handle this ourselves
|
||||
// each fragment must call FragmentMyLayoutBinding.inflate(inflater)
|
||||
abstract fun inflate(@NonNull inflater: LayoutInflater): T
|
||||
|
||||
fun onBackPressNavTo(navResId: Int) {
|
||||
mainActivity?.onFragmentBackPressed(this) {
|
||||
mainActivity?.navController?.navigate(navResId)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.ext.toAppColor
|
||||
import cash.z.wallet.sdk.entity.ConfirmedTransaction
|
||||
import cash.z.wallet.sdk.ext.abbreviatedAddress
|
||||
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
|
||||
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
@ -23,7 +23,7 @@ class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : Recycler
|
|||
var lineTwo: String = ""
|
||||
var amount: String = ""
|
||||
var amountColor: Int = 0
|
||||
var indicatorColor: Int = 0
|
||||
var indicatorBackground: Int = 0
|
||||
|
||||
transaction?.apply {
|
||||
amount = value.convertZatoshiToZecString()
|
||||
|
@ -32,18 +32,18 @@ class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : Recycler
|
|||
val isMined = blockTimeInSeconds != 0L
|
||||
when {
|
||||
!toAddress.isNullOrEmpty() -> {
|
||||
lineOne = "You paid ${toAddress?.abbreviatedAddress()}"
|
||||
lineOne = "You paid ${toAddress?.toAbbreviatedAddress()}"
|
||||
lineTwo = if (isMined) "Sent $timestamp" else "Pending confirmation"
|
||||
amount = "- $amount"
|
||||
amountColor = R.color.zcashRed
|
||||
indicatorColor = R.color.colorPrimary
|
||||
indicatorBackground = R.drawable.background_indicator_outbound
|
||||
}
|
||||
raw == null || raw?.isEmpty() == true -> {
|
||||
lineOne = "Unknown paid you"
|
||||
lineTwo = "Received $timestamp"
|
||||
amount = "+ $amount"
|
||||
amountColor = R.color.zcashGreen
|
||||
indicatorColor = R.color.zcashGreen
|
||||
indicatorBackground = R.drawable.background_indicator_inbound
|
||||
}
|
||||
else -> {
|
||||
lineOne = "Unknown"
|
||||
|
@ -56,6 +56,7 @@ class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : Recycler
|
|||
bottomText.text = lineTwo
|
||||
amountText.text = amount
|
||||
amountText.setTextColor(amountColor.toAppColor())
|
||||
indicator.setBackgroundColor(indicatorColor.toAppColor())
|
||||
val context = itemView.context
|
||||
indicator.background = context.resources.getDrawable(indicatorBackground)
|
||||
}
|
||||
}
|
|
@ -1,22 +1,25 @@
|
|||
package cash.z.ecc.android.ui.detail
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.paging.PagedList
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.databinding.FragmentDetailBinding
|
||||
import cash.z.ecc.android.di.viewmodel.viewModel
|
||||
import cash.z.ecc.android.ext.onClick
|
||||
import cash.z.ecc.android.ext.goneIf
|
||||
import cash.z.ecc.android.ext.onClickNavUp
|
||||
import cash.z.ecc.android.feedback.FeedbackFile
|
||||
import cash.z.ecc.android.ext.toColoredSpan
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import cash.z.wallet.sdk.block.CompactBlockProcessor.WalletBalance
|
||||
import cash.z.wallet.sdk.entity.ConfirmedTransaction
|
||||
import cash.z.wallet.sdk.ext.collectWith
|
||||
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
|
||||
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
|
||||
import cash.z.wallet.sdk.ext.twig
|
||||
import okio.Okio
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class WalletDetailFragment : BaseFragment<FragmentDetailBinding>() {
|
||||
|
@ -31,21 +34,26 @@ class WalletDetailFragment : BaseFragment<FragmentDetailBinding>() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.backButtonHitArea.onClickNavUp()
|
||||
|
||||
onClick(binding.buttonFeedback) {
|
||||
onSendFeedback()
|
||||
}
|
||||
onClick(binding.buttonLogs) {
|
||||
onViewLogs()
|
||||
}
|
||||
onClick(binding.buttonBackup, 1L) {
|
||||
onBackupWallet()
|
||||
lifecycleScope.launch {
|
||||
binding.textAddress.text = viewModel.getAddress().toAbbreviatedAddress()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
initTransactionUI()
|
||||
viewModel.balance.collectWith(resumedScope) {
|
||||
onBalanceUpdated(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onBalanceUpdated(balance: WalletBalance) {
|
||||
binding.textBalanceAvailable.text = balance.availableZatoshi.convertZatoshiToZecString()
|
||||
val change = balance.totalZatoshi - balance.availableZatoshi
|
||||
binding.textBalanceDescription.apply {
|
||||
goneIf(change <= 0)
|
||||
text = "(expecting +$change ZEC in change)".toColoredSpan(R.color.text_light, "+$change")
|
||||
}
|
||||
}
|
||||
|
||||
private fun initTransactionUI() {
|
||||
|
@ -56,41 +64,8 @@ class WalletDetailFragment : BaseFragment<FragmentDetailBinding>() {
|
|||
binding.recyclerTransactions.adapter = adapter
|
||||
}
|
||||
|
||||
private fun onSendFeedback() {
|
||||
mainActivity?.showSnackbar("Feedback not yet implemented.")
|
||||
}
|
||||
|
||||
private fun onViewLogs() {
|
||||
loadLogFileAsText().let { logText ->
|
||||
if (logText == null) {
|
||||
mainActivity?.showSnackbar("Log file not found!")
|
||||
} else {
|
||||
val sendIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
putExtra(Intent.EXTRA_TEXT, logText)
|
||||
type = "text/plain"
|
||||
}
|
||||
|
||||
val shareIntent = Intent.createChooser(sendIntent, "Share Log File")
|
||||
startActivity(shareIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onBackupWallet() {
|
||||
mainActivity?.navController?.navigate(R.id.action_nav_detail_to_backup_wallet)
|
||||
}
|
||||
|
||||
private fun onTransactionsUpdated(transactions: PagedList<ConfirmedTransaction>) {
|
||||
twig("got a new paged list of transactions")
|
||||
adapter.submitList(transactions)
|
||||
}
|
||||
|
||||
private fun loadLogFileAsText(): String? {
|
||||
val feedbackFile: FeedbackFile =
|
||||
mainActivity?.feedbackCoordinator?.findObserver() ?: return null
|
||||
Okio.buffer(Okio.source(feedbackFile.file)).use {
|
||||
return it.readUtf8()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,6 +11,9 @@ class WalletDetailViewModel @Inject constructor() : ViewModel() {
|
|||
lateinit var synchronizer: Synchronizer
|
||||
|
||||
val transactions get() = synchronizer.clearedTransactions
|
||||
val balance get() = synchronizer.balances
|
||||
|
||||
suspend fun getAddress() = synchronizer.getAddress()
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
|
|
|
@ -3,17 +3,19 @@ package cash.z.ecc.android.ui.home
|
|||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.text.Spanned
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.core.text.toSpannable
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.databinding.FragmentHomeBinding
|
||||
import cash.z.ecc.android.di.viewmodel.activityViewModel
|
||||
import cash.z.ecc.android.di.viewmodel.viewModel
|
||||
import cash.z.ecc.android.ext.disabledIf
|
||||
import cash.z.ecc.android.ext.goneIf
|
||||
import cash.z.ecc.android.ext.onClickNavTo
|
||||
import cash.z.ecc.android.ext.*
|
||||
import cash.z.ecc.android.ext.toAppColor
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import cash.z.ecc.android.ui.home.HomeFragment.BannerAction.*
|
||||
import cash.z.ecc.android.ui.send.SendViewModel
|
||||
|
@ -87,7 +89,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
buttonNumberPadDecimal.asKey(),
|
||||
buttonNumberPadBack.asKey()
|
||||
)
|
||||
hitAreaReceive.onClickNavTo(R.id.action_nav_home_to_nav_receive)
|
||||
hitAreaReceive.onClickNavTo(R.id.action_nav_home_to_nav_profile)
|
||||
iconDetail.onClickNavTo(R.id.action_nav_home_to_nav_detail)
|
||||
textDetail.onClickNavTo(R.id.action_nav_home_to_nav_detail)
|
||||
hitAreaScan.onClickNavTo(R.id.action_nav_home_to_nav_scan)
|
||||
|
@ -98,17 +100,33 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
buttonSend.setOnClickListener {
|
||||
onSend()
|
||||
}
|
||||
setSendAmount("0")
|
||||
}
|
||||
|
||||
binding.buttonNumberPadBack.setOnLongClickListener {
|
||||
onClearAmount()
|
||||
true
|
||||
}
|
||||
|
||||
// if (::uiModel.isInitialized) {
|
||||
// twig("uiModel exists!")
|
||||
// onModelUpdated(HomeViewModel.UiModel(), uiModel)
|
||||
// }
|
||||
}
|
||||
|
||||
private fun onClearAmount() {
|
||||
repeat(binding.textSendAmount.text.length) {
|
||||
resumedScope.launch {
|
||||
_typedChars.send('<')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.initialize(typedChars)
|
||||
twig("HomeFragment.onResume resumeScope.isActive: ${resumedScope.isActive} $resumedScope")
|
||||
onClearAmount()
|
||||
viewModel.uiModels.scanReduce { old, new ->
|
||||
onModelUpdated(old, new)
|
||||
new
|
||||
|
@ -163,8 +181,11 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param amount the amount to send represented as ZEC, without the dollar sign.
|
||||
*/
|
||||
fun setSendAmount(amount: String) {
|
||||
binding.textSendAmount.text = "\$$amount"
|
||||
binding.textSendAmount.text = "\$$amount".toColoredSpan(R.color.text_light_dimmed, "$")
|
||||
sendViewModel.zatoshiAmount = amount.safelyConvertToBigDecimal().convertZecToZatoshi()
|
||||
binding.buttonSend.disabledIf(amount == "0")
|
||||
}
|
||||
|
@ -175,7 +196,8 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
binding.textBalanceDescription.apply {
|
||||
goneIf(availableBalance < 0)
|
||||
text = if (availableBalance != -1L && (availableBalance < totalBalance)) {
|
||||
"(expecting +${(totalBalance - availableBalance).convertZatoshiToZecString()} ZEC in change)"
|
||||
val change = (totalBalance - availableBalance).convertZatoshiToZecString()
|
||||
"(expecting +$change ZEC in change)".toColoredSpan(R.color.text_light, "+$change")
|
||||
} else {
|
||||
"(enter an amount to send)"
|
||||
}
|
||||
|
@ -221,7 +243,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
}
|
||||
|
||||
private fun onSynced(uiModel: HomeViewModel.UiModel) {
|
||||
if (!uiModel.hasFunds) {
|
||||
if (!uiModel.hasBalance) {
|
||||
onNoFunds()
|
||||
} else {
|
||||
setBanner("")
|
||||
|
|
|
@ -57,7 +57,7 @@ class HomeViewModel @Inject constructor() : ViewModel() {
|
|||
}
|
||||
uiModels = synchronizer.run {
|
||||
combine(status, progress, balances, zec) { s, p, b, z->
|
||||
UiModel(s, p, b.available, b.total, z)
|
||||
UiModel(s, p, b.availableZatoshi, b.totalZatoshi, z)
|
||||
}
|
||||
}.conflate()
|
||||
}
|
||||
|
@ -81,6 +81,7 @@ class HomeViewModel @Inject constructor() : ViewModel() {
|
|||
): Parcelable {
|
||||
// Note: the wallet is effectively empty if it cannot cover the miner's fee
|
||||
val hasFunds: Boolean get() = availableBalance > (MINERS_FEE_ZATOSHI.toDouble() / ZATOSHI_PER_ZEC) // 0.0001
|
||||
val hasBalance: Boolean get() = totalBalance > (MINERS_FEE_ZATOSHI.toDouble() / ZATOSHI_PER_ZEC) // 0.0001
|
||||
val isSynced: Boolean get() = status == SYNCED
|
||||
val isSendEnabled: Boolean get() = isSynced && hasFunds
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package cash.z.ecc.android.ui.profile
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import cash.z.ecc.android.BuildConfig
|
||||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.databinding.FragmentProfileBinding
|
||||
import cash.z.ecc.android.ext.onClick
|
||||
import cash.z.ecc.android.ext.onClickNavBack
|
||||
import cash.z.ecc.android.ext.onClickNavTo
|
||||
import cash.z.ecc.android.feedback.FeedbackFile
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import okio.Okio
|
||||
|
||||
class ProfileFragment : BaseFragment<FragmentProfileBinding>() {
|
||||
override fun inflate(inflater: LayoutInflater): FragmentProfileBinding =
|
||||
FragmentProfileBinding.inflate(inflater)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.hitAreaClose.onClickNavBack()
|
||||
binding.buttonBackup.onClickNavTo(R.id.action_nav_profile_to_nav_backup)
|
||||
binding.textVersion.text = BuildConfig.VERSION_NAME
|
||||
onClick(binding.buttonLogs) {
|
||||
onViewLogs()
|
||||
}
|
||||
onClick(binding.buttonFeedback) {
|
||||
onSendFeedback()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onViewLogs() {
|
||||
loadLogFileAsText().let { logText ->
|
||||
if (logText == null) {
|
||||
mainActivity?.showSnackbar("Log file not found!")
|
||||
} else {
|
||||
val sendIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
putExtra(Intent.EXTRA_TEXT, logText)
|
||||
type = "text/plain"
|
||||
}
|
||||
|
||||
val shareIntent = Intent.createChooser(sendIntent, "Share Log File")
|
||||
startActivity(shareIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSendFeedback() {
|
||||
mainActivity?.showSnackbar("Feedback feature coming soon!")
|
||||
}
|
||||
|
||||
private fun loadLogFileAsText(): String? {
|
||||
val feedbackFile: FeedbackFile =
|
||||
mainActivity?.feedbackCoordinator?.findObserver() ?: return null
|
||||
Okio.buffer(Okio.source(feedbackFile.file)).use {
|
||||
return it.readUtf8()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ import android.content.Context
|
|||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import cash.z.android.qrecycler.QRecycler
|
||||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.databinding.FragmentReceiveNewBinding
|
||||
|
@ -12,7 +11,7 @@ import cash.z.ecc.android.di.viewmodel.viewModel
|
|||
import cash.z.ecc.android.ext.onClickNavBack
|
||||
import cash.z.ecc.android.ext.onClickNavTo
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import cash.z.wallet.sdk.ext.abbreviatedAddress
|
||||
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
|
||||
import cash.z.wallet.sdk.ext.twig
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.roundToInt
|
||||
|
@ -63,7 +62,7 @@ class ReceiveFragment : BaseFragment<FragmentReceiveNewBinding>() {
|
|||
.withCorrectionLevel(QRecycler.CorrectionLevel.MEDIUM)
|
||||
.into(binding.receiveQrCode)
|
||||
|
||||
binding.receiveAddress.text = address.abbreviatedAddress(12, 12)
|
||||
binding.receiveAddress.text = address.toAbbreviatedAddress(12, 12)
|
||||
|
||||
// address.distribute(8) { i, part ->
|
||||
// setAddressPart(i, part)
|
||||
|
|
|
@ -13,9 +13,6 @@ import cash.z.ecc.android.ext.*
|
|||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import cash.z.wallet.sdk.ext.*
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.onErrorResumeNext
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SendAddressFragment : BaseFragment<FragmentSendAddressBinding>(),
|
||||
|
@ -31,7 +28,7 @@ class SendAddressFragment : BaseFragment<FragmentSendAddressBinding>(),
|
|||
binding.buttonNext.setOnClickListener {
|
||||
onSubmit()
|
||||
}
|
||||
binding.backButtonHitArea.onClickNavBack()
|
||||
binding.backButtonHitArea.onClickNavTo(R.id.action_nav_send_address_to_nav_home)
|
||||
binding.textBannerAction.setOnClickListener {
|
||||
onPaste()
|
||||
}
|
||||
|
@ -49,7 +46,7 @@ class SendAddressFragment : BaseFragment<FragmentSendAddressBinding>(),
|
|||
binding.inputZcashAmount.setText(null)
|
||||
}
|
||||
if (!sendViewModel.toAddress.isNullOrEmpty()){
|
||||
binding.textAmount.text = "Send to ${sendViewModel.toAddress.abbreviatedAddress()}"
|
||||
binding.textAmount.text = "Send to ${sendViewModel.toAddress.toAbbreviatedAddress()}"
|
||||
binding.inputZcashAddress.setText(sendViewModel.toAddress)
|
||||
} else {
|
||||
binding.inputZcashAddress.setText(null)
|
||||
|
|
|
@ -7,11 +7,11 @@ import androidx.lifecycle.lifecycleScope
|
|||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.databinding.FragmentSendConfirmBinding
|
||||
import cash.z.ecc.android.di.viewmodel.activityViewModel
|
||||
import cash.z.ecc.android.di.viewmodel.viewModel
|
||||
import cash.z.ecc.android.ext.goneIf
|
||||
import cash.z.ecc.android.ext.onClickNavBack
|
||||
import cash.z.ecc.android.ext.onClickNavTo
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import cash.z.wallet.sdk.ext.abbreviatedAddress
|
||||
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
|
||||
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
@ -27,10 +27,13 @@ class SendConfirmFragment : BaseFragment<FragmentSendConfirmBinding>() {
|
|||
binding.buttonNext.setOnClickListener {
|
||||
onSend()
|
||||
}
|
||||
binding.backButtonHitArea.onClickNavBack()
|
||||
R.id.action_nav_send_confirm_to_nav_send_memo.let {
|
||||
binding.backButtonHitArea.onClickNavTo(it)
|
||||
onBackPressNavTo(it)
|
||||
}
|
||||
mainActivity?.lifecycleScope?.launch {
|
||||
binding.textConfirmation.text =
|
||||
"Send ${sendViewModel.zatoshiAmount.convertZatoshiToZecString(8)} ZEC to ${sendViewModel?.toAddress.abbreviatedAddress()}?"
|
||||
"Send ${sendViewModel.zatoshiAmount.convertZatoshiToZecString(8)} ZEC to ${sendViewModel?.toAddress.toAbbreviatedAddress()}?"
|
||||
}
|
||||
sendViewModel.memo.trim().isNotEmpty().let { hasMemo ->
|
||||
binding.radioIncludeAddress.isChecked = hasMemo
|
||||
|
|
|
@ -8,11 +8,10 @@ import androidx.lifecycle.lifecycleScope
|
|||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.databinding.FragmentSendFinalBinding
|
||||
import cash.z.ecc.android.di.viewmodel.activityViewModel
|
||||
import cash.z.ecc.android.di.viewmodel.viewModel
|
||||
import cash.z.ecc.android.ext.goneIf
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import cash.z.wallet.sdk.entity.*
|
||||
import cash.z.wallet.sdk.ext.abbreviatedAddress
|
||||
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
|
||||
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
|
||||
import cash.z.wallet.sdk.ext.twig
|
||||
import kotlinx.coroutines.delay
|
||||
|
@ -37,11 +36,12 @@ class SendFinalFragment : BaseFragment<FragmentSendFinalBinding>() {
|
|||
onExit()
|
||||
}
|
||||
binding.textConfirmation.text =
|
||||
"Sending ${sendViewModel.zatoshiAmount.convertZatoshiToZecString(8)} ZEC to ${sendViewModel.toAddress.abbreviatedAddress()}"
|
||||
"Sending ${sendViewModel.zatoshiAmount.convertZatoshiToZecString(8)} ZEC to ${sendViewModel.toAddress.toAbbreviatedAddress()}"
|
||||
sendViewModel.memo?.trim()?.isNotEmpty()?.let { hasMemo ->
|
||||
binding.radioIncludeAddress.isChecked = hasMemo
|
||||
binding.radioIncludeAddress.goneIf(!hasMemo)
|
||||
}
|
||||
mainActivity?.preventBackPress(this)
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
|
@ -91,6 +91,6 @@ class SendFinalFragment : BaseFragment<FragmentSendFinalBinding>() {
|
|||
}
|
||||
|
||||
private fun onExit() {
|
||||
mainActivity?.navController?.popBackStack(R.id.send_navigation, true)
|
||||
mainActivity?.navController?.popBackStack(R.id.nav_send_address, true)
|
||||
}
|
||||
}
|
|
@ -7,8 +7,7 @@ import android.view.inputmethod.EditorInfo
|
|||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.databinding.FragmentSendMemoBinding
|
||||
import cash.z.ecc.android.di.viewmodel.activityViewModel
|
||||
import cash.z.ecc.android.di.viewmodel.viewModel
|
||||
import cash.z.ecc.android.ext.onClickNavBack
|
||||
import cash.z.ecc.android.ext.onClickNavTo
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
|
||||
class SendMemoFragment : BaseFragment<FragmentSendMemoBinding>() {
|
||||
|
@ -28,7 +27,10 @@ class SendMemoFragment : BaseFragment<FragmentSendMemoBinding>() {
|
|||
sendViewModel.memo = ""
|
||||
mainActivity?.navController?.navigate(R.id.action_nav_send_memo_to_send_confirm)
|
||||
}
|
||||
binding.backButtonHitArea.onClickNavBack()
|
||||
R.id.action_nav_send_memo_to_nav_send_address.let {
|
||||
binding.backButtonHitArea.onClickNavTo(it)
|
||||
onBackPressNavTo(it)
|
||||
}
|
||||
binding.radioIncludeAddress.setOnClickListener {
|
||||
if (binding.radioIncludeAddress.isActivated) {
|
||||
binding.radioIncludeAddress.isChecked = false
|
||||
|
|
|
@ -47,6 +47,9 @@ class SendViewModel @Inject constructor() : ViewModel() {
|
|||
zatoshiAmount < ZcashSdk.MINERS_FEE_ZATOSHI -> {
|
||||
emit("Please enter a larger amount")
|
||||
}
|
||||
synchronizer.getAddress() == toAddress -> {
|
||||
emit("That appears to be your address!")
|
||||
}
|
||||
else -> emit(null)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import kotlinx.coroutines.withContext
|
|||
class BackupFragment : BaseFragment<FragmentBackupBinding>() {
|
||||
val walletSetup: WalletSetupViewModel by activityViewModel(false)
|
||||
|
||||
private var hasBackUp: Boolean? = null
|
||||
private var hasBackUp: Boolean = true //TODO: implement backup and then check for it here-ish
|
||||
|
||||
override fun inflate(inflater: LayoutInflater): FragmentBackupBinding =
|
||||
FragmentBackupBinding.inflate(inflater)
|
||||
|
@ -80,7 +80,7 @@ class BackupFragment : BaseFragment<FragmentBackupBinding>() {
|
|||
if (showMessage) {
|
||||
Toast.makeText(activity, "Backup verification coming soon!", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
mainActivity?.navController?.popBackStack(R.id.wallet_setup_navigation, true)
|
||||
mainActivity?.navController?.popBackStack()
|
||||
}
|
||||
|
||||
private fun applySpan(vararg textViews: TextView) = lifecycleScope.launch {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:fillAfter="true">
|
||||
<translate
|
||||
android:fromXDelta="0%" android:toXDelta="0%"
|
||||
android:fromYDelta="100%" android:toYDelta="0%"
|
||||
android:duration="300" />
|
||||
</set>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:fillAfter="true">
|
||||
<translate
|
||||
android:fromXDelta="-100%" android:toXDelta="0%"
|
||||
android:fromYDelta="0%" android:toYDelta="0%"
|
||||
android:duration="300" />
|
||||
</set>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:fillAfter="true">
|
||||
<translate
|
||||
android:fromXDelta="100%" android:toXDelta="0%"
|
||||
android:fromYDelta="0%" android:toYDelta="0%"
|
||||
android:duration="300" />
|
||||
</set>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:fillAfter="true">
|
||||
<translate
|
||||
android:interpolator="@android:interpolator/decelerate_cubic"
|
||||
android:fromXDelta="0%" android:toXDelta="-100%"
|
||||
android:fromYDelta="0%" android:toYDelta="0%"
|
||||
android:duration="300"/>
|
||||
</set>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:fillAfter="true">
|
||||
<translate
|
||||
android:interpolator="@android:interpolator/decelerate_cubic"
|
||||
android:fromXDelta="0%" android:toXDelta="100%"
|
||||
android:fromYDelta="0%" android:toYDelta="0%"
|
||||
android:duration="300"/>
|
||||
</set>
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="0dp"
|
||||
android:topLeftRadius="10dp"
|
||||
android:topRightRadius="10dp" />
|
||||
<solid android:color="@color/text_light_dimmed" />
|
||||
</shape>
|
||||
</item>
|
||||
<item
|
||||
android:end="1dp"
|
||||
android:start="1dp"
|
||||
android:top="1dp">
|
||||
<shape>
|
||||
<corners
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="0dp"
|
||||
android:topLeftRadius="10dp"
|
||||
android:topRightRadius="10dp" />
|
||||
<stroke
|
||||
android:color="#282828"
|
||||
android:width="1dp" />
|
||||
<solid android:color="@color/background_banner" />
|
||||
</shape>
|
||||
</item></layer-list>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="4dp" />
|
||||
<gradient android:startColor="@color/colorPrimary" android:endColor="@color/colorPrimaryDark" />
|
||||
</shape>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="4dp" />
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:centerColor="@color/zcashGreen"
|
||||
android:endColor="@color/zcashBlue"
|
||||
android:startColor="@color/zcashGreen" />
|
||||
</shape>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="4dp" />
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:endColor="@color/colorPrimaryMedium"
|
||||
android:startColor="@color/colorPrimary" />
|
||||
</shape>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
|
||||
</vector>
|
|
@ -0,0 +1,14 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="17dp"
|
||||
android:height="21dp"
|
||||
android:viewportWidth="17"
|
||||
android:viewportHeight="21">
|
||||
<path
|
||||
android:pathData="M17,3.3876L17,11.6833C17,14.6427 15.3708,17.348 12.75,18.7407L8.5,21L4.25,18.7407C1.6292,17.348 0,14.6427 0,11.6833L0,3.3876L8.5,0L17,3.3876ZM8.0121,10.8519L11.9036,7L14,9.0741L8.0121,15L4,11.0298L6.0964,8.9558L8.0121,10.8519Z"
|
||||
android:strokeAlpha="0.4079706"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#00000000"
|
||||
android:fillAlpha="0.4079706"/>
|
||||
</vector>
|
|
@ -2,9 +2,9 @@
|
|||
<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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:background="@drawable/background_home">
|
||||
|
||||
<!-- -->
|
||||
|
@ -68,65 +68,111 @@
|
|||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.045" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/button_backup"
|
||||
android:id="@+id/text_balance_available"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Backup\nWallet"
|
||||
android:text="Updating"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:textColor="@color/text_light"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_keyline_end"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toTopOf="@id/text_balance_description"
|
||||
app:layout_constraintEnd_toStartOf="@id/label_balance"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/back_button" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label_balance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:text="Available"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:textColor="@color/colorPrimary"
|
||||
app:layout_constraintBaseline_toBaselineOf="@id/text_balance_available"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/text_balance_available" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_balance_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="(enter an amount to send)"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
|
||||
app:layout_constraintBottom_toBottomOf="@id/back_button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_balance_available" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/header"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="24dp"
|
||||
android:paddingTop="24dp"
|
||||
android:onClick="copyAddress"
|
||||
android:background="@drawable/background_header"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_keyline_end"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_keyline_start"
|
||||
app:layout_constraintTop_toBottomOf="@id/back_button_hit_area">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_header_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="Your Wallet History"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="22dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label_address"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="Shielded address: "
|
||||
android:textColor="@color/text_light_dimmed"
|
||||
app:layout_constraintStart_toStartOf="@id/text_header_title"
|
||||
app:layout_constraintTop_toBottomOf="@+id/text_header_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_address"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
tools:text="zs1g7cqw...9qmvyzgm"
|
||||
android:textColor="@color/colorPrimaryMedium"
|
||||
app:layout_constraintStart_toEndOf="@id/label_address"
|
||||
app:layout_constraintTop_toBottomOf="@+id/text_header_title" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_copy"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:src="@drawable/ic_content_copy"
|
||||
app:layout_constraintTop_toTopOf="@id/label_address"
|
||||
app:layout_constraintBottom_toBottomOf="@id/label_address"
|
||||
app:layout_constraintStart_toEndOf="@id/text_address"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.15"
|
||||
app:layout_constraintDimensionRatio="W,1:1"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_transactions"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="12dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/guideline_bottom_buttons"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_keyline_start"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_keyline_end"
|
||||
app:layout_constraintTop_toBottomOf="@id/button_backup"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_keyline_start"
|
||||
app:layout_constraintTop_toBottomOf="@id/header"
|
||||
tools:itemCount="15"
|
||||
tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_transaction"
|
||||
tools:orientation="vertical" />
|
||||
|
||||
<View
|
||||
android:id="@+id/text_banner_message"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@drawable/background_banner_large"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_keyline_end"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_keyline_start"
|
||||
app:layout_constraintTop_toBottomOf="@id/guideline_bottom_buttons"
|
||||
app:layout_constraintBottom_toBottomOf="@id/guideline_keyline_bottom"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_feedback"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Zcash.Button.White"
|
||||
android:text="Send Feedback"
|
||||
android:padding="12dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_marginStart="24dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_keyline_start"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_keyline_end"
|
||||
app:layout_constraintVertical_bias="0.8"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_logs"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:text="View Logs"
|
||||
android:textColor="@color/text_light"
|
||||
app:layout_constraintTop_toBottomOf="@id/button_feedback"
|
||||
app:layout_constraintBottom_toBottomOf="@id/guideline_keyline_bottom"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintVertical_bias="0.2"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -74,7 +74,9 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:text="(enter an amount to send)"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
|
||||
android:textColor="@color/text_light_dimmed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_balance_available" />
|
||||
|
@ -291,14 +293,14 @@
|
|||
android:layout_height="0dp"
|
||||
android:elevation="6dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_percent="0.03825136612"
|
||||
app:layout_constraintHorizontal_bias="0.8883"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.8883"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.06420765027"
|
||||
app:layout_constraintWidth_percent="0.05582524272"
|
||||
app:srcCompat="@drawable/ic_receive_funds" />
|
||||
app:layout_constraintVertical_bias="0.064"
|
||||
app:layout_constraintWidth_percent="0.08"
|
||||
app:srcCompat="@drawable/ic_account_circle" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon_detail"
|
||||
|
@ -359,7 +361,7 @@
|
|||
android:layout_height="0dp"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:text="$0"
|
||||
tools:text="$0"
|
||||
android:textAppearance="@style/Zcash.TextAppearance.Zec"
|
||||
android:textSize="72dp"
|
||||
android:maxLines="1"
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:background="@drawable/background_home">
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline_hit_area_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.04" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline_send_amount_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.13" />
|
||||
|
||||
<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_percent="0.15" />
|
||||
|
||||
<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_percent="0.85" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon_close"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:elevation="6dp"
|
||||
android:tint="@color/text_light_dimmed"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.8883"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.064"
|
||||
app:layout_constraintWidth_percent="0.08"
|
||||
app:srcCompat="@drawable/ic_cancel" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon_profile"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:elevation="6dp"
|
||||
android:tint="@color/text_light_dimmed"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.212"
|
||||
app:layout_constraintWidth_percent="0.4"
|
||||
app:srcCompat="@drawable/ic_account_circle" />
|
||||
|
||||
<View
|
||||
android:id="@+id/hit_area_close"
|
||||
android:layout_width="68dp"
|
||||
android:layout_height="68dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:elevation="6dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/guideline_hit_area_top" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_username"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.AppCompat.Body1"
|
||||
android:textSize="20dp"
|
||||
android:text="Shielded User"
|
||||
app:layout_constraintTop_toBottomOf="@id/icon_profile"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/text_address"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Zcash.TextAppearance.AddressPart"
|
||||
android:textSize="16dp"
|
||||
android:text="zs1g7cqw...9qmvyzgm"
|
||||
android:textColor="@color/text_light_dimmed"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_username"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_copy"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:src="@drawable/ic_content_copy"
|
||||
app:layout_constraintTop_toTopOf="@id/text_address"
|
||||
app:layout_constraintBottom_toBottomOf="@id/text_address"
|
||||
app:layout_constraintStart_toEndOf="@id/text_address"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.15"
|
||||
app:layout_constraintDimensionRatio="W,1:1"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/hit_area_address"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:onClick="copyAddress"
|
||||
app:layout_constraintStart_toStartOf="@id/text_address"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
|
||||
app:layout_constraintTop_toTopOf="@id/text_username"
|
||||
app:layout_constraintBottom_toTopOf="@id/button_feedback"
|
||||
tools:background="@color/spacer" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_feedback"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Zcash.Button"
|
||||
android:text="Send Feedback"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:textColor="#000000"
|
||||
android:gravity="center"
|
||||
android:padding="12dp"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_address"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintVertical_bias="0.1" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_backup"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Zcash.Button.OutlinedButton"
|
||||
android:text="Backup Wallet"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:textColor="@color/text_light"
|
||||
android:gravity="center"
|
||||
android:padding="12dp"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
|
||||
app:layout_constraintTop_toBottomOf="@id/button_feedback" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/button_logs"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
style="@style/TextAppearance.AppCompat.Body1"
|
||||
android:textSize="16sp"
|
||||
android:text="See Application Log"
|
||||
app:layout_constraintTop_toBottomOf="@id/button_backup"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<!-- -->
|
||||
<!-- Banner -->
|
||||
<!-- -->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_banner_message"
|
||||
android:elevation="6dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/background_banner"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:text="zECC App"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:textColor="@color/text_light"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_content_end"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
|
||||
app:layout_constraintTop_toBottomOf="@id/button_logs"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_version"
|
||||
android:elevation="6dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="v1.0.0-alpha05"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:textColor="@color/text_light_dimmed"
|
||||
app:layout_constraintBaseline_toBaselineOf="@id/text_banner_message"
|
||||
app:layout_constraintEnd_toEndOf="@id/text_banner_message" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -88,7 +88,6 @@
|
|||
android:id="@+id/icon_qr_logo"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:onClick="copyAddress"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_zcash_primary"
|
||||
|
@ -135,6 +134,27 @@
|
|||
app:layout_constraintStart_toEndOf="@id/receive_address"
|
||||
app:layout_constraintTop_toTopOf="@id/receive_address" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/space_address_median"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="1dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/receive_address"
|
||||
app:layout_constraintBottom_toTopOf="@id/button_scan"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintVertical_bias="0.4"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/hit_area_address"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:onClick="copyAddress"
|
||||
app:layout_constraintStart_toStartOf="@id/receive_address"
|
||||
app:layout_constraintEnd_toEndOf="@id/background_qr"
|
||||
app:layout_constraintTop_toTopOf="@id/receive_title"
|
||||
app:layout_constraintBottom_toTopOf="@id/space_address_median"
|
||||
tools:background="@color/spacer" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_scan"
|
||||
android:layout_width="0dp"
|
||||
|
|
|
@ -91,17 +91,19 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/text_address_error"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/inconsolata"
|
||||
android:includeFontPadding="false"
|
||||
android:textColor="@android:color/holo_red_light"
|
||||
android:maxLines="1"
|
||||
android:autoSizeTextType="uniform"
|
||||
android:textSize="14dp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/button_next"
|
||||
app:layout_constraintStart_toStartOf="@+id/input_zcash_amount"
|
||||
app:layout_constraintTop_toTopOf="@+id/button_next"
|
||||
tools:text="Please enter a larger amount of money" />
|
||||
app:layout_constraintEnd_toStartOf="@id/button_next"
|
||||
tools:text="Please enter a larger amount of money also please enter a shorter sentence" />
|
||||
|
||||
<!-- Scan QR code -->
|
||||
<ImageView
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<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"
|
||||
|
@ -7,58 +8,125 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:background="@color/background_banner"
|
||||
android:elevation="1dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingStart="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<!-- -->
|
||||
<!-- Borders -->
|
||||
<!-- -->
|
||||
|
||||
<View
|
||||
android:id="@+id/divider_top"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:background="@color/text_light_dimmed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider_left"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/text_light_dimmed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/divider_right"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/text_light_dimmed"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
|
||||
<View
|
||||
android:id="@+id/indicator"
|
||||
android:layout_width="4dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:layout_width="3dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/background_indicator_inbound"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:background="@color/zcashGreen"/>
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_transaction_top"
|
||||
<ImageView
|
||||
android:id="@+id/image_shield"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginTop="16dp"
|
||||
android:src="@drawable/ic_check_shield"
|
||||
app:layout_constraintBottom_toTopOf="@id/text_transaction_bottom"
|
||||
app:layout_constraintStart_toEndOf="@id/indicator"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_shield_invisible"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:src="@drawable/ic_check_shield"
|
||||
android:tint="@color/zcashRed"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toTopOf="@id/text_transaction_bottom"
|
||||
app:layout_constraintStart_toEndOf="@id/indicator"
|
||||
app:layout_constraintTop_toTopOf="@id/indicator"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_transaction_top"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="14sp"
|
||||
android:maxLines="1"
|
||||
android:autoSizeTextType="uniform"
|
||||
app:layout_constraintBottom_toBottomOf="@id/image_shield_invisible"
|
||||
app:layout_constraintStart_toEndOf="@id/image_shield"
|
||||
app:layout_constraintEnd_toStartOf="@id/text_transaction_amount"
|
||||
app:layout_constraintTop_toTopOf="@id/image_shield_invisible"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="8/23 3:24pm" />
|
||||
app:layout_goneMarginStart="16dp"
|
||||
tools:text="This is a very long sentence and it better not messup the UI" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_transaction_bottom"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:paddingEnd="16dp"
|
||||
android:maxLines="1"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:paddingEnd="16dp"
|
||||
android:autoSizeTextType="uniform"
|
||||
android:text="This is a very long sentence and it better not messup the UI"
|
||||
android:textColor="@color/text_light_dimmed"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/indicator"
|
||||
app:layout_constraintEnd_toStartOf="@id/text_transaction_amount"
|
||||
app:layout_constraintStart_toStartOf="@id/text_transaction_top"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_transaction_top"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:text="funds received" />
|
||||
app:layout_constraintStart_toEndOf="@id/indicator"
|
||||
app:layout_constraintTop_toBottomOf="@id/image_shield_invisible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_transaction_amount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="+ 4.244"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:autoSizeTextType="uniform"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/indicator"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
app:layout_constraintTop_toTopOf="@id/indicator"
|
||||
app:layout_constraintWidth_percent="0.25"
|
||||
tools:text="+ 4345.2444" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -15,16 +15,22 @@
|
|||
app:destination="@id/nav_receive" />
|
||||
<action
|
||||
android:id="@+id/action_nav_home_to_nav_detail"
|
||||
app:destination="@id/nav_detail" />
|
||||
app:destination="@id/nav_detail"
|
||||
app:enterAnim="@anim/anim_enter_from_bottom" />
|
||||
<action
|
||||
android:id="@+id/action_nav_home_to_create_wallet"
|
||||
app:destination="@id/nav_landing" />
|
||||
<action
|
||||
android:id="@+id/action_nav_home_to_send"
|
||||
app:destination="@id/nav_send_address" />
|
||||
app:destination="@id/nav_send_address"
|
||||
app:exitAnim="@anim/anim_exit_to_left"
|
||||
app:enterAnim="@anim/anim_enter_from_right"/>
|
||||
<action
|
||||
android:id="@+id/action_nav_home_to_nav_scan"
|
||||
app:destination="@id/nav_scan" />
|
||||
<action
|
||||
android:id="@+id/action_nav_home_to_nav_profile"
|
||||
app:destination="@id/nav_profile" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
@ -57,13 +63,17 @@
|
|||
<fragment
|
||||
android:id="@+id/nav_detail"
|
||||
android:name="cash.z.ecc.android.ui.detail.WalletDetailFragment"
|
||||
tools:layout="@layout/fragment_detail" >
|
||||
tools:layout="@layout/fragment_detail" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/nav_profile"
|
||||
android:name="cash.z.ecc.android.ui.profile.ProfileFragment"
|
||||
tools:layout="@layout/fragment_profile" >
|
||||
<action
|
||||
android:id="@+id/action_nav_detail_to_backup_wallet"
|
||||
android:id="@+id/action_nav_profile_to_nav_backup"
|
||||
app:destination="@id/nav_backup" />
|
||||
</fragment>
|
||||
|
||||
|
||||
<!-- -->
|
||||
<!-- Send Navigation -->
|
||||
<!-- -->
|
||||
|
@ -74,10 +84,17 @@
|
|||
tools:layout="@layout/fragment_send_address" >
|
||||
<action
|
||||
android:id="@+id/action_nav_send_address_to_send_memo"
|
||||
app:destination="@id/nav_send_memo"/>
|
||||
app:destination="@id/nav_send_memo"
|
||||
app:exitAnim="@anim/anim_exit_to_left"
|
||||
app:enterAnim="@anim/anim_enter_from_right"/>
|
||||
<action
|
||||
android:id="@+id/action_nav_send_address_to_nav_scan"
|
||||
app:destination="@id/nav_scan" />
|
||||
<action
|
||||
android:id="@+id/action_nav_send_address_to_nav_home"
|
||||
app:destination="@id/nav_home"
|
||||
app:enterAnim="@anim/anim_enter_from_left"
|
||||
app:exitAnim="@anim/anim_exit_to_right"/>
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
@ -86,7 +103,14 @@
|
|||
tools:layout="@layout/fragment_send_memo" >
|
||||
<action
|
||||
android:id="@+id/action_nav_send_memo_to_send_confirm"
|
||||
app:destination="@id/nav_send_confirm"/>
|
||||
app:destination="@id/nav_send_confirm"
|
||||
app:exitAnim="@anim/anim_exit_to_left"
|
||||
app:enterAnim="@anim/anim_enter_from_right"/>
|
||||
<action
|
||||
android:id="@+id/action_nav_send_memo_to_nav_send_address"
|
||||
app:destination="@id/nav_send_address"
|
||||
app:enterAnim="@anim/anim_enter_from_left"
|
||||
app:exitAnim="@anim/anim_exit_to_right" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
@ -95,7 +119,15 @@
|
|||
tools:layout="@layout/fragment_send_confirm" >
|
||||
<action
|
||||
android:id="@+id/action_nav_send_confirm_to_send_final"
|
||||
app:destination="@id/nav_send_final"/>
|
||||
app:destination="@id/nav_send_final"
|
||||
app:popUpTo="@id/nav_send_confirm"
|
||||
app:popUpToInclusive="true"
|
||||
app:enterAnim="@anim/anim_fade_in"/>
|
||||
<action
|
||||
android:id="@+id/action_nav_send_confirm_to_nav_send_memo"
|
||||
app:destination="@id/nav_send_memo"
|
||||
app:enterAnim="@anim/anim_enter_from_left"
|
||||
app:exitAnim="@anim/anim_exit_to_right" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<!-- colors here should map to a material design value -->
|
||||
|
||||
<color name="colorPrimary">@color/zcashYellow</color>
|
||||
<color name="colorPrimaryMedium">#805E08</color>
|
||||
<color name="colorPrimaryDark">#4D3805</color>
|
||||
<color name="colorAccent">#A1A1A1</color>
|
||||
<color name="colorSurface">@color/text_light</color>
|
||||
|
@ -40,6 +41,7 @@
|
|||
|
||||
<color name="zcashGreen">#66BB6A</color>
|
||||
<color name="zcashRed">#BB666A</color>
|
||||
<color name="zcashBlue">#26DAB6</color>
|
||||
|
||||
<!-- yellows -->
|
||||
<color name="zcashYellow_light">#FFD649</color>
|
||||
|
@ -56,6 +58,7 @@
|
|||
|
||||
<color name="background_banner">@color/zcashBlack_dark</color>
|
||||
<color name="scan_overlay_background">@color/zcashBlack_87</color>
|
||||
<color name="spacer">#1FBB666A</color>
|
||||
|
||||
<!-- text -->
|
||||
<color name="text_light">#FFFFFF</color>
|
||||
|
|
|
@ -5,6 +5,8 @@ import kotlinx.coroutines.*
|
|||
import kotlinx.coroutines.channels.BroadcastChannel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
class Feedback(capacity: Int = 256) {
|
||||
|
@ -106,10 +108,11 @@ class Feedback(capacity: Int = 256) {
|
|||
*
|
||||
* @param metric the metric to add.
|
||||
*/
|
||||
fun report(metric: Metric) {
|
||||
fun report(metric: Metric): Feedback {
|
||||
jobs += scope.launch {
|
||||
_metrics.send(metric)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,10 +120,21 @@ class Feedback(capacity: Int = 256) {
|
|||
*
|
||||
* @param action the action to add.
|
||||
*/
|
||||
fun report(action: Action) {
|
||||
fun report(action: Action): Feedback {
|
||||
jobs += scope.launch {
|
||||
_actions.send(action)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the given error to everything that is tracking feedback. Converts it to a Crash object
|
||||
* which is intended for use in property-based analytics.
|
||||
*
|
||||
* @param error the uncaught exception that occurred.
|
||||
*/
|
||||
fun report(error: Throwable?): Feedback {
|
||||
return report(Crash(error))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -147,6 +161,7 @@ class Feedback(capacity: Int = 256) {
|
|||
throw IllegalStateException("Feedback is still active because ${errors.joinToString(", ")}.")
|
||||
}
|
||||
|
||||
|
||||
interface Metric : Mappable<String, Any> {
|
||||
val key: String
|
||||
val startTime: Long?
|
||||
|
@ -193,4 +208,30 @@ class Feedback(capacity: Int = 256) {
|
|||
return "$description in ${elapsedTime}ms"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Crash(val error: Throwable?) : Action {
|
||||
override val key: String = "crash"
|
||||
override fun toMap(): Map<String, Any> {
|
||||
return mutableMapOf<String, Any>(
|
||||
"message" to (error?.message ?: "None"),
|
||||
"cause" to (error?.cause?.toString() ?: "None"),
|
||||
"cause.cause" to (error?.cause?.cause?.toString() ?: "None"),
|
||||
"cause.cause.cause" to (error?.cause?.cause?.cause?.toString() ?: "None")
|
||||
).apply { putAll(super.toMap()); putAll(error.stacktraceToMap()) }
|
||||
}
|
||||
override fun toString() = "App crashed due to: $error"
|
||||
}
|
||||
}
|
||||
|
||||
private fun Throwable?.stacktraceToMap(chunkSize: Int = 250): Map<out String, String> {
|
||||
val properties = mutableMapOf("stacktrace0" to "None")
|
||||
if (this == null) return properties
|
||||
val stringWriter = StringWriter()
|
||||
|
||||
printStackTrace(PrintWriter(stringWriter))
|
||||
|
||||
stringWriter.toString().chunked(chunkSize).forEachIndexed { index, chunk ->
|
||||
properties["stacktrace$index"] = chunk
|
||||
}
|
||||
return properties
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue