Wallet Functionalities
The page provides instructions on how to add wallet/Bolt functionalities to the sample app.
Wallet Directories
The following directories lie inside the android studio project:
├── ui
│ ├── bolt
│ │ ├── BoltFragment.kt(Handles UI logic,click and views.)
│ │ ├── BoltViewModel.kt(Handles data sources and core api methods.)
│ │ ├── SortEnum.kt(Wrapper to efficiently use sort while getting transactions)
│ │ └── TransactionsAdapter.kt(Adapter for recycler view in BoltFragment)
│ │ └── TransactionBottomSheetFragment.kt(Bottom Sheet for displaying the transaction value)
│ ├── CreateWalletFragment.kt (Creates wallet for a user at first time)
UI/bolt/BoltFragment.kt
package org.zus.bolt.helloworld.ui.bolt
import android.app.AlertDialog
import android.content.ClipboardManager
import android.content.Context.CLIPBOARD_SERVICE
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.viewbinding.ViewBindings
import com.google.android.material.textfield.TextInputLayout
import com.google.android.material.textview.MaterialTextView
import kotlinx.coroutines.*
import org.zus.bolt.helloworld.R
import org.zus.bolt.helloworld.databinding.BoltFragmentBinding
import org.zus.bolt.helloworld.ui.mainactivity.MainViewModel
import zcncore.Zcncore
public const val TAG_BOLT: String = "BoltFragment"
class BoltFragment : Fragment() {
private lateinit var binding: BoltFragmentBinding
private lateinit var mainViewModel: MainViewModel
private lateinit var boltViewModel: BoltViewModel
private lateinit var onBackPressedCallback: OnBackPressedCallback
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
binding = BoltFragmentBinding.inflate(inflater, container, false)
mainViewModel = ViewModelProvider(requireActivity())[MainViewModel::class.java]
boltViewModel = ViewModelProvider(this)[BoltViewModel::class.java]
binding.zcnBalance.text = getString(R.string.zcn_balance, "0")
binding.zcnDollar.text = getString(R.string.zcn_dollar, 0.0f)
boltViewModel.isRefreshLiveData.observe(viewLifecycleOwner) { isRefresh ->
binding.swipeRefresh.isRefreshing = isRefresh
}
CoroutineScope(Dispatchers.Main).launch {
val usd = boltViewModel.zcnToUsd(1.0)
binding.zcnDollarValue.text = getString(R.string._1_zcn_0_0001_usd, 1.0, usd)
}
onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (boltViewModel.isRefreshLiveData.value != true) {
findNavController().popBackStack()
}
}
}
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
onBackPressedCallback
)
boltViewModel.isRefreshLiveData.observe(viewLifecycleOwner) { isRefresh ->
if (!isRefresh) {
requireActivity().runOnUiThread {
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
}
} else {
requireActivity().runOnUiThread {
requireActivity().window.setFlags(
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
)
}
}
binding.swipeRefresh.isRefreshing = isRefresh
}
/* Setting the adapters. */
val transactionsAdapter = TransactionsAdapter(requireContext(), childFragmentManager, listOf())
binding.rvTransactions.layoutManager = LinearLayoutManager(requireContext())
binding.rvTransactions.adapter = transactionsAdapter
boltViewModel.transactionsLiveData.observe(viewLifecycleOwner) { transactions ->
transactionsAdapter.transactions = transactions
transactionsAdapter.notifyDataSetChanged()
}
boltViewModel.balanceLiveData.observe(viewLifecycleOwner) { balance ->
binding.zcnBalance.text = getString(R.string.zcn_balance, balance)
CoroutineScope(Dispatchers.Main).launch {
val dollar = boltViewModel.zcnToUsd(balance.toDouble())
try {
binding.zcnDollar.text = getString(R.string.zcn_dollar, dollar)
} catch (e: Exception) {
Log.e(TAG_BOLT, "onCreateView: ${e.message}")
}
}
}
/* Receive token faucet transaction. */
binding.mFaucet.setOnClickListener {
/* updating the balance after 3 seconds.*/
CoroutineScope(Dispatchers.IO).launch {
boltViewModel.receiveFaucet()
val calls = async {
updateBalance()
updateTransactions()
}
awaitAll(calls)
}
}
/* Send token to an address. */
binding.msendToken.setOnClickListener {
val builder = AlertDialog.Builder(requireContext())
val dialogView = LayoutInflater.from(requireContext())
.inflate(R.layout.send_transaction_dialog, null)
builder.setTitle("Send Token")
.setView(dialogView)
.setPositiveButton("Send") { _, _ ->
val address =
dialogView.findViewById<TextView>(R.id.et_to_client_id).text.toString()
val amount = dialogView.findViewById<TextView>(R.id.amount).text.toString()
if (amount.isBlank() || amount.toDouble() <= 0.0f) {
val amountTIL = ViewBindings.findChildViewById<TextInputLayout>(
dialogView,
R.id.amountTextInputLayout
)
amountTIL?.error = "Amount should be greater than 0"
} else {
CoroutineScope(Dispatchers.IO).launch {
boltViewModel.sendTransaction(address, amount)
}
}
}
.setNegativeButton("Cancel") { dialog, _ ->
dialog.cancel()
}
builder.create().show()
}
binding.mreceiveToken.setOnClickListener {
val builder = AlertDialog.Builder(requireContext())
val dialogView = LayoutInflater.from(requireContext())
.inflate(R.layout.receive_transaction_dialog, null)
dialogView.findViewById<MaterialTextView>(R.id.tv_receiver_client_id).text =
buildString {
append(
mainViewModel.wallet?.mClientId?.substring(
0,
16
)
)
append("...")
append(mainViewModel.wallet?.mClientId?.substring(mainViewModel.wallet?.mClientId?.length!! - 16))
}
builder.setTitle("Receive Token")
.setView(dialogView)
.setCancelable(true)
.setNeutralButton("Copy Client ID") { _, _ ->
//copy to clipboard
var clipboard =
requireActivity().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setPrimaryClip(
android.content.ClipData.newPlainText(
"text",
mainViewModel.wallet?.mClientId
)
)
}
builder.create().show()
}
binding.swipeRefresh.setOnRefreshListener {
CoroutineScope(Dispatchers.IO).launch {
val calls = async {
updateTransactions()
updateBalance()
}
awaitAll(calls)
}
}
return binding.root
}
/* Get transactions. */
private suspend fun updateTransactions() {
boltViewModel.getTransactions(
fromClientId = mainViewModel.wallet?.mClientId ?: "",
toClientId = "",
sortOrder = Sort.getSort(SortEnum.DESC),
limit = 20,
offset = 0
)
}
private suspend fun updateBalance() {
boltViewModel.getWalletBalance()
}
}
Line 3 to 23 import all the libraries (zcncore(gosdk) is imported at line 23 ) required to built WalletUI fragment for the App.
Line 26 defines
BoltFragment
class to define structure for UI.Line 31 to 34 defines variable required to hold values for binding application data to view ,main home page UI of the app wallet UI and .
Line 35 defines method for creating a view
a)
override fun
. This is just us overriding the function called onCreateView that is inherited from the Fragment class.b)
onCreateView()
method is called and the view returned from this method will be the one shown to the user.c) ViewModelProvider instance is used with Fragments to hold activity data,The ViewModelProvider exists when you first request a View until the Activity is finished and destroyed.
d) In line 41 and 42, all the ViewModelProvider activities are assigned to mainViewModel and BoltViewModel variables to hold and manage UI-related data .
Line 45 and 46 binds zcn balance in tokens and usd via gosdk methods.
Line 52 to 67 make use of coroutunes to handle execution for back button in the sample app .A coroutine can suspend its execution at some point and resume later on.
Line 85 to line 103 makes use of the TransactionAdapter to make changes to live transaction balance and change it based on app actions and finally binds to UI. See gif here.
Line 106 to 116 adds a mouse click Listener when a user clicks on faucet button .See faucet gif here.
Line 119 to 145 adds a mouse click Listener when a user click on send button, provides a new dialog to provide sender details and update wallet balance after sending .See send button in the gif for reference.
Line 147 to 189 adds a mouse click Listener when a user click on recieve button, provides a new dialog to provide reciever details and update wallet balance after receiving .See recieve button in gif for reference
Line 192 to 200 defines function to update transactions in real time .It is updated by all the on mouse click listeners defined above and details are sorted via sortenum .
Line 202 to 205 defines function to update wallet balance .It is utilized and called by onmouseclick listeners defined above after their desired functionality is reached.
UI/bolt/BoltViewModel.kt
package org.zus.bolt.helloworld.ui.bolt
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.zus.bolt.helloworld.models.bolt.BalanceModel
import org.zus.bolt.helloworld.models.bolt.TransactionModel
import zcncore.GetInfoCallback
import zcncore.Transaction
import zcncore.TransactionCallback
import zcn.Zcn
class BoltViewModel : ViewModel() {
val transactionsLiveData: MutableLiveData<List<TransactionModel>> = MutableLiveData()
var balanceLiveData = MutableLiveData<String>()
val isRefreshLiveData = MutableLiveData<Boolean>()
private val getInfoCallback = GetInfoCallback { p0, p1, p2, p3 ->
isRefreshLiveData.postValue(false)
Log.i(TAG_BOLT, "onInfoAvailable: ")
Log.i(TAG_BOLT, "onInfoAvailable: p0 $p0")
Log.i(TAG_BOLT, "onInfoAvailable: p1 $p1")
Log.i(TAG_BOLT, "onInfoAvailable: p2 $p2")
Log.i(TAG_BOLT, "onInfoAvailable: p3 $p3")
}
suspend fun sendTransaction(to: String, amount: String) {
withContext(Dispatchers.IO) {
isRefreshLiveData.postValue(true)
Zcncore.newTransaction(transactionCallback, /* gas = */ "0", /* nonce = */ getNonce())
.send(
/* receiver address = */ to,
/* amount = */ Zcncore.convertToValue(amount.toDouble()).toString(),
/* notes = */ "Hello world! sending tokens."
)
}
}
suspend fun receiveFaucet() {
withContext(Dispatchers.IO) {
isRefreshLiveData.postValue(true)
Zcncore.newTransaction(transactionCallback, /* gas = */ "0",/* nonce = */getNonce())
.executeSmartContract(
/* faucet address = */ "6dba10422e368813802877a85039d3985d96760ed844092319743fb3a76712d3",
/* method name = */ "pour",
/* inputs = */ "{}",
/* amount = */ Zcncore.convertToValue(1.0)
)
}
}
/* Use this callback while making a transaction. */
private val transactionCallback = object : TransactionCallback {
override fun onAuthComplete(p0: Transaction?, p1: Long) {
// confirmation of successful authentication of the transaction.
}
override fun onTransactionComplete(transaction: Transaction?, status: Long) {
// confirmation of successful transaction.
isRefreshLiveData.postValue(false)
if (status == 0L) {
// Successful status of the transaction.
}
}
override fun onVerifyComplete(p0: Transaction?, p1: Long) {
// confirmation of successful verification of the transaction.
isRefreshLiveData.postValue(false)
}
}
suspend fun getWalletBalance() {
return withContext(Dispatchers.IO) {
try {
isRefreshLiveData.postValue(true)
Zcncore.getBalance { status, value, info ->
isRefreshLiveData.postValue(false)
if (status == 0L) {
Gson().fromJson(info, BalanceModel::class.java).let { balanceModel ->
balanceLiveData.postValue(
Zcncore.convertToToken(balanceModel.balance).toString()
)
}
} else {
print("Error: $info")
balanceLiveData.postValue("")
}
}
} catch (e: Exception) {
isRefreshLiveData.postValue(false)
print("Error: $e")
// Zcncore.newTransaction(transactionCallback, /* gas = */ "0", /* nonce = */ getNonce())
balanceLiveData.postValue("")
}
}
}
suspend fun getTransactions(
toClientId: String,
fromClientId: String,
sortOrder: String,
limit: Long,
offset: Long,
) {
withContext(Dispatchers.IO) {
isRefreshLiveData.postValue(true)
Zcncore.getTransactions(
toClientId,
fromClientId,
/*block hash optional =*/"",
sortOrder,
limit,
offset
) { _, _, json, error ->
isRefreshLiveData.postValue(false)
if (error.isEmpty() && !json.isNullOrBlank() && json.isNotEmpty()) {
val transactions = Gson().fromJson(json, Array<TransactionModel>::class.java)
this@BoltViewModel.transactionsLiveData.postValue(transactions.toList())
} else {
Log.e(TAG_BOLT, "getTransactions: $error")
}
}
}
}
suspend fun getBlobbers() {
withContext(Dispatchers.IO) {
isRefreshLiveData.postValue(true)
Zcncore.getBlobbers(getInfoCallback, /* limit */ 20, /* offset */ 0, true)
}
}
private suspend fun getNonce(): Long {
return withContext(Dispatchers.IO) {
var nonceGlobal: Long = 0L
Zcncore.getNonce { status, nonce, error ->
if (status == 0L && error == null) {
// nonce is a string
// nonce = "0"
nonceGlobal = nonce
}
}
return@withContext nonceGlobal
}
}
suspend fun zcnToUsd(zcn: Double): Double {
return withContext(Dispatchers.IO) {
return@withContext try {
Zcncore.convertTokenToUSD(zcn)
} catch (e: Exception) {
0.0
}
}
}
suspend fun tokenToUsd(token: Long): Double {
return withContext(Dispatchers.IO) {
return@withContext try {
Zcncore.convertTokenToUSD(Zcncore.convertToToken(token))
} catch (e: Exception) {
0.0
}
}
}
}
Describing Code
Line 16 defines a
BoltViewModel
class that utilizes the zcncore libraries and provide the following functionalities for BoltFragmentLine 30 to 40
sendTransaction
function : Send Tokens to Wallet Transactions via gosdk newTransaction function.Line 42 to 53
receiveFaucet
function : Receive Tokens to Wallet Transactions via gosdk newTransaction function.Line 75 to 99 getWalletBalance function ; Get Wallet Balance via gosdk getbalance function
Line 101 to 127
getTransactions
function : Get Transactions List via gosdk getTransactions function.Line 129 to 134
getBlobbers
function : Get list of storage providers/blobbers via gosdk getBlobbers functionLine 136 to 146
getNonce
function: Retrieves a 32-bit number that miners use as a base for transaction hash calculations.Retrieved via gosdk getNonce function.Line 150 to 158
zcnToUsd
function : Convert ZCN value to USD. Utilized gosdk convertTokentoUSD.Line 160 to 168 tokenToUsd function : Convert ZCN Token to USD.Utilized gosdk convertTokentoUSD and ConvertToToken.
UI/bolt/SortEnum.kt
package org.zus.bolt.helloworld.ui.bolt
enum class SortEnum {
ASC,
DESC
}
object Sort {
fun getSort(sortEnum: SortEnum): String {
return when (sortEnum) {
SortEnum.ASC -> "asc"
SortEnum.DESC -> "desc"
}
}
}
Desccribing Code
Line 3 to 6 defines enum class
SortEnum
which has two value Ascending and Descending.Line 8 to 9 defines object of Sort EnumClass which can be used later to filter any details in ascending or descending order.
UI/bolt/TransactionsAdapter.kt
package org.zus.bolt.helloworld.ui.bolt
import android.app.Activity
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
import org.zus.bolt.helloworld.R
import org.zus.bolt.helloworld.models.bolt.TransactionModel
import org.zus.bolt.helloworld.utils.Utils.Companion.getConvertedTime
import org.zus.bolt.helloworld.utils.Utils.Companion.getShortFormattedString
class TransactionsAdapter(
var context: Context,
var childFragmentManager: FragmentManager,
var transactions: List<TransactionModel>,
) : RecyclerView.Adapter<TransactionsAdapter.ViewHolder>() {
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val tvHash: TextView
val tvDateTime: TextView
val btnCopyHash: ImageButton
init {
tvHash = view.findViewById(R.id.tv_hash)
tvDateTime = view.findViewById(R.id.tv_date_time)
btnCopyHash = view.findViewById(R.id.btn_copy_hash)
}
}
holder.tvDateTime.text =
(transactions[position].creation_date / 1000000000).getConvertedTime()
holder.btnCopyHash.setOnClickListener {
val clipboard =
context.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
val clip = android.content.ClipData.newPlainText(
"Transaction Hash",
transactions[position].hash
)
clipboard.setPrimaryClip(clip)
Toast.makeText(
context,
"Transaction Hash Copied ${transactions[position].hash.getShortFormattedString()}",
Toast.LENGTH_SHORT
).show()
}
holder.itemView.setOnClickListener {
val transactionBottomSheetFragment =
TransactionBottomSheetFragment(transactions[position])
transactionBottomSheetFragment.show(
childFragmentManager,
"TransactionBottomSheetFragment"
)
}
}
Describing Code:
Line 3 to 17 import packages required to segment sample app into multiple, independent screens that are hosted within an activity and to create a Recycler View in app. To understand more about RecyclerView read here
Line 19 defines a class
TransactionsAdapter
In the class following variables are defined( Line 20 to 23 )
context: to hold context for TransactionsView.
childFragmentManager : An array to hold transaction object details
Another class 'ViewHolder' is defined which holds a recyclable view of transaction details.(Line 25 to 35)
Line 37 overrides the method
onCreateViewHolder
creates a new ViewHolder and initializes some private fields to be used by RecyclerView.Line 43 overrides onBindViewHolder method which is Called by RecyclerView to display the transaction data at the specified position.Time stamps are deermined for the transaction and its details are listed in a dialog box as per TransactionBottomSheetFragment.kt
UI/bolt/TransactionBottomSheetFragment.kt
package org.zus.bolt.helloworld.ui.bolt
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.zus.bolt.helloworld.R
import org.zus.bolt.helloworld.databinding.GenericBottomSheetDetailsFragmentBinding
import org.zus.bolt.helloworld.databinding.RowDetailsListItemBinding
import org.zus.bolt.helloworld.models.bolt.TransactionModel
import org.zus.bolt.helloworld.models.selectapp.DetailsListModel
import org.zus.bolt.helloworld.models.selectapp.DetailsModel
import org.zus.bolt.helloworld.ui.selectapp.DetailsListAdapter
import org.zus.bolt.helloworld.utils.Utils.Companion.getConvertedDateTime
class TransactionBottomSheetFragment(
private val transactionModel: TransactionModel,
) : BottomSheetDialogFragment() {
private lateinit var binding: GenericBottomSheetDetailsFragmentBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = GenericBottomSheetDetailsFragmentBinding.inflate(inflater, container, false)
binding.tvPageTitle.text = getString(R.string.transaction_details)
val signatureAndHashes = DetailsListModel(
title = "Signature and Hashes",
detailsList = listOf(
DetailsModel(
title = "Transaction Hash: ${transactionModel.hash}",
value = transactionModel.hash,
showArrowButton = false
),
DetailsModel(
title = "Block Hash: ${transactionModel.block_hash}",
value = transactionModel.block_hash,
showArrowButton = false
),
DetailsModel(
title = "Output Hash: ${transactionModel.output_hash}",
value = transactionModel.output_hash,
showArrowButton = false
),
DetailsModel(
title = "Client Id: ${transactionModel.client_id}",
value = transactionModel.client_id,
showArrowButton = false
),
DetailsModel(
title = "To Client Id: ${transactionModel.to_client_id}",
value = transactionModel.to_client_id,
showArrowButton = false
),
DetailsModel(
title = "Signature: ${transactionModel.signature}",
value = transactionModel.signature,
showArrowButton = false
),
)
)
val amountDetails = DetailsListModel(
title = "Amount Details",
detailsList = listOf(
DetailsModel(
title = "Status: ${transactionModel.status}",
value = transactionModel.status.toString(),
showArrowButton = false
),
DetailsModel(
title = "Value: ${transactionModel.value}",
value = transactionModel.value.toString(),
showArrowButton = false
),
DetailsModel(
title = "Fee: ${transactionModel.fee}",
value = transactionModel.fee.toString(),
showArrowButton = false
),
DetailsModel(
title = "Date: ${(transactionModel.creation_date / 1000000000).getConvertedDateTime()}",
value = transactionModel.creation_date.toString(),
showArrowButton = false
)
)
)
val explorer = DetailsListModel(
title = "Explorer",
detailsList = listOf(
DetailsModel(
title = "Explorer: https://demo.atlus.cloud/transaction-details/${transactionModel.hash}",
value = "https://demo.atlus.cloud/transaction-details/${transactionModel.hash}",
showArrowButton = true
)
)
)
val detailsList = listOf(signatureAndHashes, amountDetails, explorer)
binding.detailsListView.removeAllViews()
for (detailsModel in detailsList) {
val rowDetailsListItemBindings = RowDetailsListItemBinding.inflate(
LayoutInflater.from(requireActivity()),
binding.detailsListView,
false
)
rowDetailsListItemBindings.tvDetails.text = detailsModel.title
rowDetailsListItemBindings.detailsListView.adapter = DetailsListAdapter(
requireActivity(),
detailsModel.detailsList
)
binding.detailsListView.addView(rowDetailsListItemBindings.root)
}
return binding.root
}
}
Describing Code :
Line 1 to 15 import packages required to create a bottom sheet dialog box for the sample app trasnsaction view.To learn more about bottomsheets check here.
Line 17 defines a class
TransactionBottomSheetFragment
Line 18 to 20 defines the variables required for binding the view to transaction data via Transaction Model.
Line 23 to 66 overrides the method
onCreateView
and defines how different transaction details in the dialog box should be displayed .Inside
onCreateView
Line 68 to 92 defines how transaction details should be displayed in the dialog box .Line 105 to 122 inflates the view with the above defined bottomsheet dialog specs.
UI/CreateWalletFragment.kt
package org.zus.bolt.helloworld.ui
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.zus.bolt.helloworld.R
import org.zus.bolt.helloworld.databinding.CreateWalletFragmentBinding
import org.zus.bolt.helloworld.models.bolt.WalletModel
import org.zus.bolt.helloworld.ui.mainactivity.MainViewModel
import org.zus.bolt.helloworld.ui.vult.VultViewModel
import org.zus.bolt.helloworld.utils.Utils
import org.zus.bolt.helloworld.utils.ZcnSDK
import zcncore.Zcncore
import java.io.FileNotFoundException
import java.util.*
public const val TAG_CREATE_WALLET: String = "CreateWalletFragment"
class CreateWalletFragment : Fragment() {
private lateinit var binding: CreateWalletFragmentBinding
private lateinit var mainViewModel: MainViewModel
private lateinit var vultViewModel: VultViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
binding = CreateWalletFragmentBinding.inflate(inflater, container, false)
mainViewModel = ViewModelProvider(requireActivity())[MainViewModel::class.java]
vultViewModel = ViewModelProvider(requireActivity())[VultViewModel::class.java]
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.progressView.visibility = View.VISIBLE
try {
val walletJsonStringFromFile = Utils(requireContext()).readWalletFromFileJSON()
Log.i(
TAG_CREATE_WALLET,
"walletJsonStringFromFile: ${walletJsonStringFromFile.isNullOrBlank()}"
)
if (walletJsonStringFromFile.isBlank() || walletJsonStringFromFile.isEmpty()) {
Zcncore.createWallet { status, walletJson, error ->
if (status == 0L) {
Log.i(TAG_CREATE_WALLET, "New Wallet created successfully")
Utils(requireContext()).saveWalletAsFile(walletJson)
processWallet(walletJson)
} else {
Log.e(TAG_CREATE_WALLET, "Error: $error")
}
}
} else {
Log.i(TAG_CREATE_WALLET, "Wallet already exists")
processWallet(walletJsonStringFromFile)
}
} catch (e: FileNotFoundException) {
Log.d(TAG_CREATE_WALLET, "File not found")
Zcncore.createWallet { status, walletJson, error ->
if (status == 0L) {
Log.i(TAG_CREATE_WALLET, "New Wallet created successfully")
Utils(requireContext()).saveWalletAsFile(walletJson)
processWallet(walletJson)
} else {
Log.e(TAG_CREATE_WALLET, "Error: $error")
}
}
} catch (e: Exception) {
Log.e(TAG_CREATE_WALLET, "Error: ${e.message}", e)
}
}
private fun processWallet(walletJson: String) {
try {
val walletModel = Gson().fromJson(walletJson, WalletModel::class.java)
mainViewModel.wallet = walletModel
val wallet: WalletModel = mainViewModel.wallet!!
Log.e(TAG_CREATE_WALLET, walletModel.mMnemonics)
wallet.walletJson = walletJson
Zcncore.setWalletInfo(walletJson, false)
// ZcnSDK().readPoolLock(1.0,0.0)
runBlocking {
CoroutineScope(Dispatchers.IO).launch {
requireActivity().runOnUiThread {
binding.btCreateWallet.text = getString(R.string.creating_allocation)
}
vultViewModel.storageSDK =
VultViewModel.initZboxStorageSDK(
Utils(requireContext()).config,
Utils(requireContext()).readWalletFromFileJSON()
)
if (vultViewModel.getAllocation() == null) {
ZcnSDK().faucet("pour", "{Pay day}", 10.0)
vultViewModel.createAllocation(
allocationName = "test allocation",
dataShards = 2,
parityShards = 2,
allocationSize = 2147483648,
expirationSeconds = Date().time / 1000 + 30000,
lockTokens = Zcncore.convertToValue(1.0),
)
}
requireActivity().runOnUiThread {
binding.progressView.visibility = View.GONE
findNavController().navigate(R.id.action_createWalletFragment_to_selectAppFragment)
}
}
}
} catch (e: Exception) {
Log.e(TAG_CREATE_WALLET, "Error: ${e.message}", e)
}
}
}
Describing Code :
Line 3 to 25 defines function required for creating a CreateWallet UI. See Homepage of the App as a reference.
Line 29 Class CreateWalletFragment
defines the CreateWallet UI of the application with the folllowing properties and fields.
Line 31-33 defines the following private fields for holding UI related data :
Private writable property called binding to bind data to the UI
Private writable variable for holding WalletView
Private writable variable for holding MainApp View
Line 36 defines method for creating a create wallet view
override fun
. This is just us overriding the function called onCreateView that is inherited from the Fragment class.onCreateView()
method is called to create/inflate a layout and we have provided our MainViewModel with a non-null view(via view binding)In line 42 and 43 ViewModelProvider instance is used with the CreateWallet Fragment to hold activity data, The ViewModelProvider exists from when you first request a ViewModel until the UI Activity is finished and destroyed.
binding.root returns the view hierarchy(line 44)
Line 48 defines method
onViewCreated()
for managing the created view.onViewCreated()
runs after the onCreateView(). Any view setup should occur here. E.g., view lookups and attaching view listeners.Line 50 to 90 defines a create Wallet button in UI and calls gosdk createWallet function .Also checks functionality for if wallet exists already and proceed directly to Home Page of app.
Line 93 to 130 defines
processWallet
function and sets wallet info via gosdksetWalletinfo
function.Line 114 to 124 adds tokens to set wallet .
Wallet is set and saved as wallet.json file in assets folder if state change is detected by runOnUiThread() . In case of wallet not saved due to any other issues,stack errors are thrown.
Last updated