# Wallet Functionalities

## 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)
```

<details>

<summary>UI/bolt/BoltFragment.kt</summary>

{% code lineNumbers="true" %}

```kotlin
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()
    }
}
```

{% endcode %}

* 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 .&#x20;
* 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[ ](/guides/zus-gosdk/gosdk-for-mobile-builds-ios-and-android/mobile-sdk-reference.md)[gosdk methods.](/guides/zus-gosdk/gosdk-for-mobile-builds-ios-and-android/mobile-sdk-reference.md)
  * 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](#ui-bolt-transactionsadapter.kt) to make changes to live transaction balance and change it based on app actions and finally binds to UI. See[ ](https://docs.zus.network/guides/zus-gosdk/gosdk-for-mobile-builds-ios-and-android/android-gosdk-sample-app/testing-all-functionalities#bolt-wallet-functionalities-fragment)[gif](https://3006516934-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNrAZJ0KGiwEbKkbVRYPu%2Fuploads%2FSIxDpfq82GFnIpkavhck%2FBolt%20Fragment.gif?alt=media\&token=c825d302-db6f-4222-9397-b6d46bd20075) here.<br>
* Line 106 to 116 adds a mouse click Listener when a user clicks on faucet button .See[ ](https://docs.zus.network/guides/zus-gosdk/gosdk-for-mobile-builds-ios-and-android/android-gosdk-sample-app/testing-all-functionalities#bolt-wallet-functionalities-fragment)[ ](/guides/zus-gosdk/gosdk-for-mobile-builds-ios-and-android/android-gosdk-sample-app/testing-all-functionalities.md#getting-tokens-into-wallet)faucet [gif ](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNrAZJ0KGiwEbKkbVRYPu%2Fuploads%2FdmVJz5Wj7Uj6MKKCtCfy%2Ffaucet.gif?alt=media\&token=0e7eb3d9-1e1a-4b7c-ae9e-37ff09bf587b)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](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNrAZJ0KGiwEbKkbVRYPu%2Fuploads%2FdmVJz5Wj7Uj6MKKCtCfy%2Ffaucet.gif?alt=media\&token=0e7eb3d9-1e1a-4b7c-ae9e-37ff09bf587b) 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](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNrAZJ0KGiwEbKkbVRYPu%2Fuploads%2FdmVJz5Wj7Uj6MKKCtCfy%2Ffaucet.gif?alt=media\&token=0e7eb3d9-1e1a-4b7c-ae9e-37ff09bf587b) 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 ](#ui-bolt-sortenum.kt).
* 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. &#x20;

</details>

<details>

<summary>UI/bolt/BoltViewModel.kt</summary>

{% code lineNumbers="true" %}

```kotlin
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
            }
        }
    }
      
}
```

{% endcode %}

Describing Code

* Line 16 defines a `BoltViewModel`class that utilizes the [zcncore libraries](/guides/zus-gosdk/gosdk-for-mobile-builds-ios-and-android/mobile-sdk-reference.md) and provide the following functionalities for [BoltFragment](#ui-bolt-boltfragment.kt)&#x20;
* Line 30 to 40 `sendTransaction` function : Send Tokens to Wallet Transactions via gosdk [newTransaction](https://pkg.go.dev/github.com/0chain/gosdk@v1.8.15/zcncore#NewTransaction) function.&#x20;
* Line 42 to 53 `receiveFaucet` function : Receive Tokens to Wallet Transactions via gosdk [newTransaction ](https://pkg.go.dev/github.com/0chain/gosdk@v1.8.15/zcncore#NewTransaction)function.
* Line 75 to 99 getWalletBalance function ; Get Wallet Balance via gosdk [getbalance](https://pkg.go.dev/github.com/0chain/gosdk@v1.8.15/zcncore#GetBalance) function
* Line 101 to 127 `getTransactions` function : Get Transactions List via gosdk [getTransactions](https://pkg.go.dev/github.com/0chain/gosdk@v1.8.15/zcncore#GetTransactions) function.
* Line 129 to 134 `getBlobbers` function : Get list of storage providers/blobbers via gosdk [getBlobbers ](https://pkg.go.dev/github.com/0chain/gosdk@v1.8.15/zcncore#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](https://pkg.go.dev/github.com/0chain/gosdk@v1.8.15/zcncore#GetNonce) function.
* Line 150 to 158 `zcnToUsd` function : Convert ZCN value to USD. Utilized gosdk[ convertTokentoUSD](https://github.com/0chain/gosdk/blob/v1.8.15/zcncore/wallet_base.go#L836).
* Line 160 to 168 tokenToUsd function : Convert ZCN Token to USD.Utilized gosdk [convertTokentoUSD](https://github.com/0chain/gosdk/blob/v1.8.15/zcncore/wallet_base.go#L836) and [ConvertToToken](https://github.com/0chain/gosdk/blob/v1.8.15/zcncore/wallet_base.go#L832).

</details>

<details>

<summary>UI/bolt/SortEnum.kt</summary>

{% code lineNumbers="true" %}

```kotlin
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"
        }
    }
}
```

{% endcode %}

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.

</details>

<details>

<summary>UI/bolt/TransactionsAdapter.kt</summary>

{% code lineNumbers="true" %}

```kotlin
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"
            )
        }
    }
```

{% endcode %}

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](https://developer.android.com/develop/ui/views/layout/recyclerview)
* Line 19 defines a class `TransactionsAdapter`&#x20;
  * In the class following variables are defined( Line 20 to 23 )&#x20;
    * 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)

</details>

<details>

<summary>UI/bolt/TransactionBottomSheetFragment.kt</summary>

{% code lineNumbers="true" %}

```kotlin
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
    }
}
```

{% endcode %}

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](https://developer.android.com/reference/com/google/android/material/bottomsheet/BottomSheetDialog).
* Line 17 defines a class `TransactionBottomSheetFragment`&#x20;
* Line 18 to 20 defines the variables required for binding the view to transaction data via [Transaction](https://github.com/0chain/HelloWorld-Android/blob/master/app/src/main/java/org/zus/helloworld/models/bolt/TransactionModel.kt) Model.
* Line 23 to 66 overrides the method `onCreateView` and defines how different transaction details in the dialog box should be displayed .&#x20;
* Inside `onCreateView` Line 68 to 92 defines how transaction details should be displayed in the dialog box .&#x20;
* Line 105 to 122 inflates the view with the above defined bottomsheet dialog specs.

</details>

<details>

<summary>UI/CreateWalletFragment.kt</summary>

{% code lineNumbers="true" %}

```kotlin
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)
        }
    }
}
```

{% endcode %}

Describing Code :

* Line 3 to 25 defines function required for creating a CreateWallet UI. See [Homepage ](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNrAZJ0KGiwEbKkbVRYPu%2Fuploads%2FGr0MjiZzeYweOyraQtQu%2FScreenshot_2023-04-27-14-12-06-44_b3d101213ddb9daf00321b7b6d479e4a.jpg?alt=media\&token=af37c8ca-58ce-467f-aecc-3c422e4fbdb6)of the App as a reference.&#x20;
* `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 :<br>
  * 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.&#x20;
  * 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.<br>

</details>

###


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs-old.zus.network/guides/zus-gosdk/gosdk-for-mobile-builds-ios-and-android/android-gosdk-sample-app/wallet-functionalities.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
