Everything functional before changing startup sequence
This commit is contained in:
parent
ba51a19b0b
commit
ad708e69d5
Binary file not shown.
|
@ -1,24 +1,16 @@
|
|||
package cash.z.android.wallet.di.module
|
||||
|
||||
import android.util.Log
|
||||
import cash.z.android.wallet.BuildConfig
|
||||
import cash.z.android.wallet.ZcashWalletApplication
|
||||
import cash.z.android.wallet.di.module.Properties.CACHE_DB_NAME
|
||||
import cash.z.android.wallet.di.module.Properties.COMPACT_BLOCK_PORT
|
||||
import cash.z.android.wallet.di.module.Properties.COMPACT_BLOCK_SERVER
|
||||
import cash.z.android.wallet.di.module.Properties.DATA_DB_NAME
|
||||
import cash.z.android.wallet.di.module.Properties.SEED_PROVIDER
|
||||
import cash.z.android.wallet.di.module.Properties.SPENDING_KEY_PROVIDER
|
||||
import cash.z.android.wallet.sample.SampleProperties
|
||||
import cash.z.android.wallet.sample.SampleProperties.COMPACT_BLOCK_PORT
|
||||
import cash.z.android.wallet.sample.SampleProperties.COMPACT_BLOCK_SERVER
|
||||
import cash.z.wallet.sdk.data.*
|
||||
import cash.z.wallet.sdk.jni.JniConverter
|
||||
import cash.z.wallet.sdk.secure.Wallet
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import okio.ByteString
|
||||
import java.nio.charset.Charset
|
||||
import javax.inject.Singleton
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* Module that contributes all the objects necessary for the synchronizer, which is basically everything that has
|
||||
|
@ -43,21 +35,21 @@ internal object SynchronizerModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideProcessor(application: ZcashWalletApplication, converter: JniConverter, twigger: Twig): CompactBlockProcessor {
|
||||
return CompactBlockProcessor(application, converter, CACHE_DB_NAME, DATA_DB_NAME, logger = twigger)
|
||||
return CompactBlockProcessor(application, converter, SampleProperties.wallet.cacheDbName, SampleProperties.wallet.dataDbName, logger = twigger)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRepository(application: ZcashWalletApplication, converter: JniConverter, twigger: Twig): TransactionRepository {
|
||||
return PollingTransactionRepository(application, DATA_DB_NAME, 10_000L, converter, twigger)
|
||||
return PollingTransactionRepository(application, SampleProperties.wallet.dataDbName, 10_000L, converter, twigger)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWallet(application: ZcashWalletApplication, converter: JniConverter, twigger: Twig): Wallet {
|
||||
return Wallet(converter, application.getDatabasePath(DATA_DB_NAME).absolutePath, "${application.cacheDir.absolutePath}/params", seedProvider = SEED_PROVIDER, spendingKeyProvider = SPENDING_KEY_PROVIDER, logger = twigger)
|
||||
return Wallet(converter, application.getDatabasePath(SampleProperties.wallet.dataDbName).absolutePath, "${application.cacheDir.absolutePath}/params", seedProvider = SampleProperties.wallet.seedProvider, spendingKeyProvider = SampleProperties.wallet.spendingKeyProvider, logger = twigger)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
@ -91,55 +83,3 @@ internal object SynchronizerModule {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// TODO: load most of these properties in later, perhaps from settings
|
||||
object Properties {
|
||||
val COMPACT_BLOCK_SERVER = Servers.EMULATOR.host
|
||||
const val COMPACT_BLOCK_PORT = 9067
|
||||
const val CACHE_DB_NAME = "wallet_cache4821.db"
|
||||
const val DATA_DB_NAME = "wallet_data4821.db"
|
||||
val SEED_PROVIDER = SampleSeedProvider("dummyseed")
|
||||
val SPENDING_KEY_PROVIDER = SampleSpendingKeyProvider("dummyseed")
|
||||
}
|
||||
|
||||
enum class Servers(val host: String) {
|
||||
EMULATOR("10.0.2.2"),
|
||||
WLAN("10.0.0.26"),
|
||||
BOLT_TESTNET("ec2-34-228-10-162.compute-1.amazonaws.com"),
|
||||
ZCASH_TESTNET("lightwalletd.z.cash")
|
||||
}
|
||||
class SampleImportedSeedProvider(private val seedHex: String) : ReadOnlyProperty<Any?, ByteArray> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): ByteArray {
|
||||
val bytes = ByteString.decodeHex(seedHex).toByteArray()
|
||||
val stringBytes = String(bytes, Charset.forName("UTF-8"))
|
||||
Log.e("TWIG-x", "byteString: $stringBytes")
|
||||
return decodeHex(seedHex).also { Log.e("TWIG-x", "$it") }
|
||||
}
|
||||
|
||||
|
||||
fun decodeHex(hex: String): ByteArray {
|
||||
val result = ByteArray(hex.length / 2)
|
||||
for (i in result.indices) {
|
||||
val d1 = decodeHexDigit(hex[i * 2]) shl 4
|
||||
val d2 = decodeHexDigit(hex[i * 2 + 1])
|
||||
result[i] = (d1 + d2).toByte()
|
||||
}
|
||||
return result
|
||||
}
|
||||
private fun decodeHexDigit(c: Char): Int {
|
||||
if (c in '0'..'9') return c - '0'
|
||||
if (c in 'a'..'f') return c - 'a' + 10
|
||||
if (c in 'A'..'F') return c - 'A' + 10
|
||||
throw IllegalArgumentException("Unexpected hex digit: $c")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SampleSpendingKeyProvider2(private val seedValue: String) : ReadOnlyProperty<Any?, String> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): String {
|
||||
// dynamically generating keyes, based on seed is out of scope for this sample
|
||||
return "secret-extended-key-test1q0ks5jkcqqqqpqywf2mh5g2aw5smt252mqscphjr8svrqyvgtgss0av3jh37jc05pngstr6qcqu5x64zuk8entc97pfla68jd7g9fyhwv5l8pdey662qy3lr07w9yddpgwdlwt3tjgzhpszatyw90kpn4zs7feu5cudwnxcpts5k0za96xy0wt59nu7hg3ntalck7gwhn0nuyztmf8yceuhp0fn3wmrtr9mk9v6fhg8hwvsxp0thr4cn9r8pc0w3zh45czmnr7e3mrctlzaq7"
|
||||
// return "secret-extended-key-test1q0f0urnmqqqqpqxlree5urprcmg9pdgvr2c88qhm862etv65eu84r9zwannpz4g88299xyhv7wf9xkecag653jlwwwyxrymfraqsnz8qfgds70qjammscxxyl7s7p9xz9w906epdpy8ztsjd7ez7phcd5vj7syx68sjskqs8j9lef2uuacghsh8puuvsy9u25pfvcdznta33qe6xh5lrlnhdkgymnpdug4jm6tpf803cad6tqa9c0ewq9l03fqxatevm97jmuv8u0ccxjews5"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package cash.z.android.wallet.sample
|
||||
|
||||
import cash.z.wallet.sdk.data.SampleSeedProvider
|
||||
|
||||
object AliceWallet {
|
||||
const val name = "test.reference.alice"
|
||||
val seedProvider = SampleSeedProvider(name)
|
||||
val spendingKeyProvider = SampleSpendingKeySharedPref(name)
|
||||
const val cacheDbName = "testalice_cache.db"
|
||||
const val dataDbName = "testalice_data.db"
|
||||
}
|
||||
|
||||
object BobWallet {
|
||||
const val name = "test.reference.bob"
|
||||
val seedProvider =
|
||||
SampleSeedProvider(name)
|
||||
val spendingKeyProvider = SampleSpendingKeySharedPref(name)
|
||||
const val cacheDbName = "testalice_cache.db"
|
||||
const val dataDbName = "testalice_data.db"
|
||||
}
|
||||
|
||||
object MyWallet {
|
||||
const val name = "mine"
|
||||
val seedProvider =
|
||||
SampleImportedSeedProvider("295761fce7fdc89fa1095259f5be6375c4a36f7a214767d668f9ef6e17aa6314")
|
||||
val spendingKeyProvider = SampleSpendingKeySharedPref(name)
|
||||
const val cacheDbName = "wallet_cache1202.db"
|
||||
const val dataDbName = "wallet_data1202.db"
|
||||
}
|
||||
|
||||
enum class Servers(val host: String) {
|
||||
EMULATOR("10.0.2.2"),
|
||||
WLAN("10.0.0.26"),
|
||||
BOLT_TESTNET("ec2-34-228-10-162.compute-1.amazonaws.com"),
|
||||
ZCASH_TESTNET("lightwalletd.z.cash")
|
||||
}
|
||||
|
||||
|
||||
// TODO: load most of these properties in later, perhaps from settings
|
||||
object SampleProperties {
|
||||
val COMPACT_BLOCK_SERVER = Servers.EMULATOR.host
|
||||
const val COMPACT_BLOCK_PORT = 9067
|
||||
val wallet = AliceWallet
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package cash.z.android.wallet.sample
|
||||
|
||||
import android.preference.PreferenceManager
|
||||
import android.util.Log
|
||||
import cash.z.android.wallet.ZcashWalletApplication
|
||||
import okio.ByteString
|
||||
import java.nio.charset.Charset
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
import android.R.id.edit
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
@Deprecated(message = InsecureWarning.message)
|
||||
class SampleImportedSeedProvider(private val seedHex: String) : ReadOnlyProperty<Any?, ByteArray> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): ByteArray {
|
||||
val bytes = ByteString.decodeHex(seedHex).toByteArray()
|
||||
val stringBytes = String(bytes, Charset.forName("UTF-8"))
|
||||
Log.e("TWIG-x", "byteString: $stringBytes")
|
||||
return decodeHex(seedHex).also { Log.e("TWIG-x", "$it") }
|
||||
}
|
||||
|
||||
fun decodeHex(hex: String): ByteArray {
|
||||
val result = ByteArray(hex.length / 2)
|
||||
for (i in result.indices) {
|
||||
val d1 = decodeHexDigit(hex[i * 2]) shl 4
|
||||
val d2 = decodeHexDigit(hex[i * 2 + 1])
|
||||
result[i] = (d1 + d2).toByte()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun decodeHexDigit(c: Char): Int {
|
||||
if (c in '0'..'9') return c - '0'
|
||||
if (c in 'a'..'f') return c - 'a' + 10
|
||||
if (c in 'A'..'F') return c - 'A' + 10
|
||||
throw IllegalArgumentException("Unexpected hex digit: $c")
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated(message = InsecureWarning.message)
|
||||
class SampleSpendingKeySharedPref(private val fileName: String) : ReadWriteProperty<Any?, String> {
|
||||
|
||||
private fun getPrefs() = ZcashWalletApplication.instance
|
||||
.getSharedPreferences(fileName, Context.MODE_PRIVATE)
|
||||
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): String {
|
||||
val preferences = getPrefs()
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(ZcashWalletApplication.instance)
|
||||
return preferences.getString("spending", null)
|
||||
?: throw IllegalStateException(
|
||||
"Spending key was not there when we needed it! Make sure it was saved " +
|
||||
"during the first run of the app, when accounts were created!"
|
||||
)
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
|
||||
Log.e("TWIG", "Spending key is being stored")
|
||||
val preferences = getPrefs()
|
||||
val editor = preferences.edit()
|
||||
editor.putString("spending", value)
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal object InsecureWarning {
|
||||
const val message = "Do not use this because it is insecure and only intended for test code and samples. " +
|
||||
"Instead, use the Android Keystore system or a 3rd party library that leverages it."
|
||||
}
|
|
@ -3,21 +3,23 @@ package cash.z.android.wallet.ui.fragment
|
|||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import cash.z.android.wallet.R
|
||||
import android.view.ViewGroup
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import cash.z.android.wallet.R
|
||||
import cash.z.android.wallet.databinding.FragmentHistoryBinding
|
||||
import cash.z.android.wallet.ui.adapter.TransactionAdapter
|
||||
import cash.z.android.wallet.ui.presenter.HistoryPresenter
|
||||
import cash.z.android.wallet.ui.util.AlternatingRowColorDecoration
|
||||
import cash.z.wallet.sdk.dao.WalletTransaction
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import kotlinx.coroutines.launch
|
||||
import cash.z.android.wallet.databinding.FragmentHistoryBinding
|
||||
import cash.z.wallet.sdk.dao.WalletTransaction
|
||||
|
||||
|
||||
class HistoryFragment : BaseFragment(), HistoryPresenter.HistoryView {
|
||||
|
||||
|
||||
override val titleResId: Int get() = R.string.destination_title_history
|
||||
lateinit var historyPresenter: HistoryPresenter
|
||||
lateinit var binding: FragmentHistoryBinding
|
||||
|
||||
|
@ -28,6 +30,25 @@ class HistoryFragment : BaseFragment(), HistoryPresenter.HistoryView {
|
|||
.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mainActivity.let { mainActivity ->
|
||||
mainActivity.setSupportActionBar(view.findViewById(R.id.toolbar))
|
||||
mainActivity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
mainActivity.supportActionBar?.setTitle(R.string.destination_title_history)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
historyPresenter = HistoryPresenter(this, mainActivity.synchronizer)
|
||||
binding.recyclerTransactionsHistory.apply {
|
||||
layoutManager = LinearLayoutManager(activity, RecyclerView.VERTICAL, false)
|
||||
adapter = TransactionAdapter()
|
||||
addItemDecoration(AlternatingRowColorDecoration())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
launch {
|
||||
|
@ -41,7 +62,8 @@ class HistoryFragment : BaseFragment(), HistoryPresenter.HistoryView {
|
|||
}
|
||||
|
||||
override fun setTransactions(transactions: List<WalletTransaction>) {
|
||||
}
|
||||
(binding.recyclerTransactionsHistory.adapter as TransactionAdapter).submitList(transactions)
|
||||
}
|
||||
}
|
||||
|
||||
@Module
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.text.SpannableString
|
|||
import android.text.Spanned
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.AccelerateInterpolator
|
||||
|
@ -44,6 +45,7 @@ import kotlinx.android.synthetic.main.include_home_header.*
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextLong
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
|
||||
/**
|
||||
|
@ -117,7 +119,11 @@ class HomeFragment : BaseFragment(), HomePresenter.HomeView {
|
|||
}
|
||||
|
||||
launch {
|
||||
setFirstRunShown(mainActivity.synchronizer.isFirstRun())
|
||||
Log.e("TWIG", "deciding whether to show first run")
|
||||
val extraDelay = measureTimeMillis {
|
||||
setFirstRunShown(mainActivity.synchronizer.isFirstRun() || mainActivity.synchronizer.isOutOfSync())
|
||||
}
|
||||
Log.e("TWIG", "done deciding whether to show first run in $extraDelay ms. Was that worth it? Or should we toggle a boolean in the application class?")
|
||||
}
|
||||
|
||||
header_active_transaction.visibility = View.GONE
|
||||
|
@ -161,7 +167,11 @@ class HomeFragment : BaseFragment(), HomePresenter.HomeView {
|
|||
layoutManager = LinearLayoutManager(activity, RecyclerView.VERTICAL, false)
|
||||
adapter = TransactionAdapter().also { transactionAdapter = it }
|
||||
addItemDecoration(AlternatingRowColorDecoration())
|
||||
|
||||
}
|
||||
// recycler_transactions.setOnClickListener {
|
||||
// mainActivity.navController.navigate(R.id.nav_history_fragment)
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
@ -198,7 +208,7 @@ class HomeFragment : BaseFragment(), HomePresenter.HomeView {
|
|||
// var hasEmptyViews = group_empty_view_items.visibility == View.VISIBLE
|
||||
// if(!viewsInitialized) toggleViews(true)
|
||||
//
|
||||
val message = if(progress >= 100) "Download complete! Processing blocks..." else "Downloading blocks ($progress%)"
|
||||
val message = if(progress >= 100) "Download complete! Processing blocks..." else "Downloading remaining blocks ($progress%)"
|
||||
// text_wallet_message.text = message
|
||||
|
||||
if (snackbar == null && progress <= 50) {
|
||||
|
@ -209,7 +219,7 @@ class HomeFragment : BaseFragment(), HomePresenter.HomeView {
|
|||
snackbar?.show()
|
||||
} else {
|
||||
snackbar?.setText(message)
|
||||
if(progress == 100 && snackbar?.isShownOrQueued != true) snackbar?.show()
|
||||
if(snackbar?.isShownOrQueued != true) snackbar?.show()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ class ReceiveFragment : BaseFragment() {
|
|||
}
|
||||
|
||||
private fun onAddressLoaded(address: String) {
|
||||
Log.e("TWIG", "onAddressLoaded: $address")
|
||||
qrecycler.load(address)
|
||||
.withQuietZoneSize(3)
|
||||
.withCorrectionLevel(QRecycler.CorrectionLevel.MEDIUM)
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
package cash.z.android.wallet.ui.presenter
|
||||
|
||||
import android.util.Log
|
||||
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
|
||||
import cash.z.wallet.sdk.dao.WalletTransaction
|
||||
import cash.z.wallet.sdk.data.ActiveSendTransaction
|
||||
import cash.z.wallet.sdk.data.ActiveTransaction
|
||||
import cash.z.wallet.sdk.data.Synchronizer
|
||||
import cash.z.wallet.sdk.data.TransactionState
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.channels.ReceiveChannel
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class HistoryPresenter(
|
||||
private val view: HistoryView,
|
||||
private val synchronizer: Synchronizer
|
||||
) : Presenter, CoroutineScope {
|
||||
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext get() = Dispatchers.Main + job
|
||||
|
||||
interface HistoryView : PresenterView {
|
||||
fun setTransactions(transactions: List<WalletTransaction>)
|
||||
}
|
||||
|
||||
override suspend fun start() {
|
||||
Log.e("@TWIG", "historyPresenter starting!")
|
||||
launchTransactionBinder(synchronizer.repository.allTransactions())
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
Log.e("@TWIG", "historyPresenter stopping!")
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
private fun CoroutineScope.launchTransactionBinder(channel: ReceiveChannel<List<WalletTransaction>>) = launch {
|
||||
Log.e("@TWIG", "transaction binder starting!")
|
||||
for (walletTransactionList in channel) {
|
||||
Log.e("@TWIG", "received ${walletTransactionList.size} transactions for presenting")
|
||||
bind(walletTransactionList)
|
||||
}
|
||||
Log.e("@TWIG", "transaction binder exiting!")
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// View Callbacks on Main Thread
|
||||
//
|
||||
|
||||
private fun bind(transactions: List<WalletTransaction>) {
|
||||
Log.e("@TWIG", "binding ${transactions.size} walletTransactions")
|
||||
view.setTransactions(transactions)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -82,22 +82,23 @@ class HomePresenter(
|
|||
//
|
||||
|
||||
private fun bind(old: Long?, new: Long) = onMain {
|
||||
Log.e("@TWIG-t", "binding balance of $new")
|
||||
Log.e("@TWIG-b", "binding balance of $new")
|
||||
view.updateBalance(old ?: 0L, new)
|
||||
}
|
||||
|
||||
|
||||
private fun bind(transactions: List<WalletTransaction>) = onMain {
|
||||
Log.e("@TWIG-t", "binding ${transactions.size} walletTransactions")
|
||||
Log.e("@TWIG-b", "binding ${transactions.size} walletTransactions")
|
||||
view.setTransactions(transactions)
|
||||
}
|
||||
|
||||
private fun bind(progress: Int) = onMain {
|
||||
Log.e("@TWIG-b", "binding progress of $progress")
|
||||
view.showProgress(progress)
|
||||
}
|
||||
|
||||
private fun bind(activeTransactionMap: Map<ActiveTransaction, TransactionState>) = onMain {
|
||||
Log.e("@TWIG-v", "binding a.t. map of size ${activeTransactionMap.size}")
|
||||
Log.e("@TWIG-b", "binding a.t. map of size ${activeTransactionMap.size}")
|
||||
if (activeTransactionMap.isNotEmpty()) view.setActiveTransactions(activeTransactionMap)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<?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">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<!-- Transactions -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_transactions_history"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="72dp"
|
||||
android:layout_margin="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/appbar"
|
||||
tools:itemCount="15"
|
||||
tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_transaction_history"
|
||||
tools:orientation="vertical" />
|
||||
|
||||
<include
|
||||
layout="@layout/include_app_bar"
|
||||
tools:ignore="MissingConstraints" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</layout>
|
|
@ -0,0 +1,51 @@
|
|||
<?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:background="@color/home_transaction_item_background"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:elevation="1dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<View
|
||||
android:id="@+id/view_transaction_status"
|
||||
android:layout_width="6dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="1dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:background="@color/colorPrimary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/text_transaction_timestamp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/text_transaction_timestamp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_transaction_timestamp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingTop="8dp"
|
||||
tools:text="8/23 3:24pm"
|
||||
android:textSize="@dimen/text_size_body_2"
|
||||
android:textColor="@color/text_dark_dimmed"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/view_transaction_status"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_transaction_amount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="+ 4.244"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="@dimen/text_size_body_2"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue