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 BoltViewModelclass that utilizes the zcncore libraries and provide the following functionalities for BoltFragment

  • Line 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 function

  • Line 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 onCreateViewHoldercreates 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 gosdk setWalletinfo 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