Select App Fragments
The page provides instructions on how to add dialog box functionalities to the sample app.
Select App Directory
The following directories lie inside the android studio project:
├── ui
│ ├── selectapp
│ ├── AllocationDetailsBottomScreenFragment.kt (Bottom sheet fragment for displaying allocation details)
│ ├── DetailsListAdapter.kt (Adapter for generalised list view)
│ ├── NetworkDetailsBottomScreenFragment.kt (Bottom Sheet fragment for displaying network details)
│ ├── SelectAppFragment.kt (Navigates to bolt or vult app.)
│ └── WalletDetailsBottomScreenFragment.kt (wallet details bottom sheet fragment in SelectAppFragment.)
└── DetailsBottomSheetFragment.kt(Bottom sheet fragment for displaying all details )
ui/selectapp/AllocationDetailsBottomScreenFragment.kt
package org.zus.bolt.helloworld.ui.selectapp
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.zus.bolt.helloworld.R
import org.zus.bolt.helloworld.databinding.GenericBottomSheetDetailsFragmentBinding
import org.zus.bolt.helloworld.models.vult.AllocationModel
import org.zus.bolt.helloworld.utils.Utils
import org.zus.bolt.helloworld.databinding.RowDetailsListItemBinding
import org.zus.bolt.helloworld.models.selectapp.DetailsListModel
import org.zus.bolt.helloworld.models.selectapp.DetailsModel
import org.zus.bolt.helloworld.ui.vult.VultViewModel
import org.zus.bolt.helloworld.utils.Utils.Companion.getConvertedDateTime
import org.zus.bolt.helloworld.utils.Utils.Companion.getConvertedSize
class AllocationDetailsBottomScreenFragment(
private val allocationModel: Allocation,
) : BottomSheetDialogFragment() {
private lateinit var binding: GenericBottomSheetDetailsFragmentBinding
private lateinit var vultViewModel: VultViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = GenericBottomSheetDetailsFragmentBinding.inflate(inflater, container, false)
vultViewModel = ViewModelProvider(requireActivity())[VultViewModel::class.java]
binding.tvPageTitle.text = getString(R.string.allocation_details_title)
CoroutineScope(Dispatchers.Main).launch {
val statsModel = vultViewModel.getAllocation()?.let { vultViewModel.getStats(it.stats) }
val allocationDetailsModel = DetailsListModel(
title = getString(R.string.details),
detailsList = listOf(
DetailsModel(
title = "Allocation ID: ${allocationModel.id}",
value = allocationModel.id,
showArrowButton = false
),
DetailsModel(
title = "Name: ${allocationModel.name}",
value = allocationModel.name,
showArrowButton = false
),
DetailsModel(
title = "Expiration: ${allocationModel.expiration.getConvertedDateTime()}",
value = allocationModel.expiration.getConvertedDateTime(),
showArrowButton = false
),
DetailsModel(
title = "Size: ${allocationModel.size.getConvertedSize()}",
value = allocationModel.size.getConvertedSize(),
showArrowButton = false
),
DetailsModel(
title = "Used Size: ${statsModel?.used_size?.getConvertedSize() ?: "0"}",
value = statsModel?.used_size?.getConvertedSize() ?: "0",
showArrowButton = false
)
)
)
val shardsAndChallengesDetails = DetailsListModel(
title = getString(R.string.shards_and_challenges_details_title),
detailsList = listOf(
DetailsModel(
title = "Data Shards: ${allocationModel.dataShards}",
value = allocationModel.dataShards.toString(),
showArrowButton = false
),
DetailsModel(
title = "Parity Shards: ${allocationModel.parityShards}",
value = allocationModel.parityShards.toString(),
showArrowButton = false
),
DetailsModel(
title = "Number of Writes: ${statsModel?.num_of_writes ?: 0}",
value = statsModel?.num_of_writes.toString(),
showArrowButton = false
),
DetailsModel(
title = "Number of Reads: ${statsModel?.num_of_reads ?: 0}",
value = statsModel?.num_of_reads.toString(),
showArrowButton = false
),
DetailsModel(
title = "Number of Challenges: ${statsModel?.num_success_challenges ?: 0}",
value = statsModel?.num_success_challenges.toString(),
showArrowButton = false
),
DetailsModel(
title = "Latest Closed Challenge: ${statsModel?.latest_closed_challenge ?: "No value provided"}",
value = statsModel?.latest_closed_challenge ?: "No value provided",
showArrowButton = false
)
)
)
val allocationDetails = listOf(
allocationDetailsModel,
shardsAndChallengesDetails
)
binding.detailsListView.removeAllViews()
for (detailsModel in allocationDetails) {
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 3 to 20 import packages to create view/UI for Allocation Details in the sample app.
Line 22 defines a class AllocationDetailsBottomScreenFragment which has following fields:
Line 25 defines a private late initialized binding variable that holds Bottom Sheet Dialog object instance
Line 28 overrides the onCreateView function which creates and returns the view hierarchy associated with the AllocationDetailsBottomScreen.
Line 38 to 71 defines a coroutine
allocationDetailsModel
that awaits execution of AllocationDetails and sharders/challenges details in the defined format by Details Model instance.Line 73 to 107 defines how sharders and challenges information should be displayed. The display format is similar to Details Model instance.
Line 109 to 132 takes all the allocation and sharder information and binds it to the entire view to outermost container in layout.
ui/selectapp/DetailsListAdapter.kt
package org.zus.bolt.helloworld.ui.selectapp
import android.annotation.SuppressLint
import android.content.ClipboardManager
import android.content.Context.CLIPBOARD_SERVICE
import android.text.SpannableString
import android.text.SpannedString
import android.text.style.ForegroundColorSpan
import android.text.style.StyleSpan
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import androidx.navigation.findNavController
import org.zus.bolt.helloworld.R
import org.zus.bolt.helloworld.databinding.DialogItemViewBinding
import org.zus.bolt.helloworld.databinding.RowDetailsBinding
import org.zus.bolt.helloworld.models.selectapp.DetailsModel
import org.zus.bolt.helloworld.ui.TAG_CREATE_WALLET
import org.zus.bolt.helloworld.utils.Utils.Companion.isValidJson
import org.zus.bolt.helloworld.utils.Utils.Companion.isValidUrl
import org.zus.bolt.helloworld.utils.Utils.Companion.prettyJsonFormat
class DetailsListAdapter(
private val fragmentActivity: FragmentActivity,
private val detailsList: List<DetailsModel>,
) : BaseAdapter() {
override fun getCount(): Int = detailsList.size
override fun getItem(position: Int): Any {
return detailsList[position]
}
override fun getItemId(position: Int): Long {
return 0
}
@SuppressLint("RestrictedApi")
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val rowDetailsBinding = RowDetailsBinding.inflate(LayoutInflater.from(parent?.context))
rowDetailsBinding.tvDetailTitle.text = detailsList[position].title
rowDetailsBinding.rowDetailsRoot.setOnLongClickListener {
val clipboard =
fragmentActivity.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setPrimaryClip(
android.content.ClipData.newPlainText(
detailsList[position].title,
detailsList[position].value
)
)
Toast.makeText(
fragmentActivity,
"${detailsList[position].title} Copied to clipboard",
Toast.LENGTH_SHORT
).show()
true
}
if (detailsList[position].value.isValidUrl()) {
val text = SpannableString(detailsList[position].title).apply {
if (detailsList[position].title.isValidUrl()) {
rowDetailsBinding.tvDetailTitle.setTextColor(
ContextCompat.getColor(
fragmentActivity,
R.color.color_url
)
)
} else {
val colonIndex = detailsList[position].title.indexOf(":")
setSpan(
ForegroundColorSpan(
ContextCompat.getColor(
fragmentActivity,
R.color.color_url
)
),
colonIndex,
detailsList[position].title.length,
SpannedString.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
rowDetailsBinding.tvDetailTitle.text = text
rowDetailsBinding.rowDetailsRoot.setOnClickListener {
val openUrl = android.content.Intent(android.content.Intent.ACTION_VIEW)
openUrl.data = android.net.Uri.parse(detailsList[position].value)
fragmentActivity.startActivity(openUrl)
}
}
if (detailsList[position].showArrowButton) {
rowDetailsBinding.ibnArrowDetails.visibility = View.VISIBLE
rowDetailsBinding.rowDetailsRoot.setOnClickListener {
val detailsBottomSheetDialogFragment =
DetailsBottomSheetFragment(detailsList[position])
detailsBottomSheetDialogFragment.show(
fragmentActivity.supportFragmentManager,
"detailsBottomSheetDialogFragment"
)
}
} else {
rowDetailsBinding.ibnArrowDetails.visibility = View.GONE
}
return rowDetailsBinding.root
}
}
Describing Code
DetailsListAdapter class defines fields and functions that handles functionality of addition and removal of data in a list without the need to redraw the entire view.
ui/selectapp/NetworkDetailsBottomScreenFragment.kt
package org.zus.bolt.helloworld.ui.selectapp
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.gson.Gson
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.NetworkModel
import org.zus.bolt.helloworld.models.selectapp.DetailsListModel
import org.zus.bolt.helloworld.models.selectapp.DetailsModel
import org.zus.bolt.helloworld.utils.Utils
class NetworkDetailsBottomScreenFragment : BottomSheetDialogFragment() {
lateinit var binding: GenericBottomSheetDetailsFragmentBinding
lateinit var networkModel: NetworkModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
binding =
GenericBottomSheetDetailsFragmentBinding.inflate(inflater, container, false)
binding.tvPageTitle.text = getString(R.string.network_details_title)
networkModel = Gson().fromJson(
Utils(requireContext()).getConfigFromAssets("config.json"),
NetworkModel::class.java
)
val networkDetailsModel = DetailsListModel(
title = getString(R.string.details),
detailsList = listOf(
DetailsModel(
title = "Name: ${networkModel.domainUrl}",
value = networkModel.domainUrl,
showArrowButton = false
),
DetailsModel(
title = "Url: ${networkModel.config.blockWorker}",
value = networkModel.config.blockWorker,
showArrowButton = false
),
DetailsModel(
title = "0box Url: ${networkModel.zboxUrl}",
value = networkModel.zboxUrl,
showArrowButton = false
)
)
)
val url = DetailsListModel(
title = "",
detailsList = listOf(
DetailsModel(
title = networkModel.config.blockWorker,
value = networkModel.config.blockWorker,
showArrowButton = false
)
)
)
val networkDetails = listOf(
networkDetailsModel,
url
)
binding.detailsListView.removeAllViews()
for (detailsModel in networkDetails) {
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 3 to 16 import packages required to create UI for Network Details in App.See gif here
Line 18 defines a class
NetworkDetailsBottomScreenFragment
which has following fields:
(Line 20) A private variable that holds access to NetworkModel instance.
Line 21 overrides the onCreateView function which creates and returns the view hierarchy associated with the NetworkDetailsBottomScreenFragment.
(Line 27) A private late initialized binding variable that holds Bottom Sheet Dialog object instance.
Line 31 to 33 retrieves network configuration from config.json
Line 36 to 66 defines how network details should be displayed in UI of the app. The format is defined by DetailsModel
Line 69 to 90 defines view binding that help interact code with views defined in line 36 to 66.
Line 91
binding.root
ties the entire view to outermost container in layout of the app.
ui/selectapp/SelectAppFragment.kt
package org.zus.bolt.helloworld.ui.selectapp
import android.os.Bundle
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.android.material.snackbar.Snackbar
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.zus.bolt.helloworld.R
import org.zus.bolt.helloworld.databinding.SelectAppFragmentBinding
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
class SelectAppFragment : Fragment() {
lateinit var binding: SelectAppFragmentBinding
lateinit var mainViewModel: MainViewModel
lateinit var vultViewModel: VultViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
// Inflate the layout for this fragment
binding = SelectAppFragmentBinding.inflate(inflater, container, false)
mainViewModel = ViewModelProvider(requireActivity())[MainViewModel::class.java]
vultViewModel = ViewModelProvider(requireActivity())[VultViewModel::class.java]
binding.cvWalletDetails.setOnClickListener {
val walletDetailsBottomScreenFragment =
WalletDetailsBottomScreenFragment(mainViewModel.wallet!!)
walletDetailsBottomScreenFragment.show(
parentFragmentManager,
"WalletDetailsBottomScreenFragment"
)
}
binding.cvAllocationDetails.setOnClickListener {
CoroutineScope(Dispatchers.IO).launch {
try {
val allocationModel = vultViewModel.getAllocation()
requireActivity().runOnUiThread {
if (allocationModel != null)
AllocationDetailsBottomScreenFragment(allocationModel).show(
parentFragmentManager,
"AllocationDetailsBottomScreenFragment")
else
Snackbar.make(
binding.root,
"Error: Allocation not found",
Snackbar.LENGTH_LONG
).show()
}
} catch (e: Exception) {
requireActivity().runOnUiThread {
Snackbar.make(
binding.root,
"Error: ${e.message}",
Snackbar.LENGTH_LONG
).show()
}
}
}
}
binding.cvNetworkDetails.setOnClickListener {
val networkDetailsBottomScreenFragment =
NetworkDetailsBottomScreenFragment()
networkDetailsBottomScreenFragment.show(
parentFragmentManager,
"NetworkDetailsBottomScreenFragment"
)
}
binding.cvBolt.setOnClickListener {
if (!Utils(requireContext()).isWalletExist()) {
findNavController().navigate(R.id.action_selectAppFragment_to_boltFragment)
} else {
mainViewModel.wallet =
Gson().fromJson(
Utils(requireContext()).readWalletFromFileJSON(),
WalletModel::class.java
)
mainViewModel.setWalletJson(Utils(requireContext()).readWalletFromFileJSON())
findNavController().navigate(R.id.action_selectAppFragment_to_boltFragment)
}
}
binding.cvVult.setOnClickListener {
if (!Utils(requireContext()).isWalletExist()) {
findNavController().navigate(R.id.action_selectAppFragment_to_vultFragment)
} else {
mainViewModel.wallet =
Gson().fromJson(
Utils(requireContext()).readWalletFromFileJSON(),
WalletModel::class.java
)
mainViewModel.setWalletJson(Utils(requireContext()).readWalletFromFileJSON())
findNavController().navigate(R.id.action_selectAppFragment_to_vultFragment)
}
}
return binding.root
}
}
Describing Code:
Line 3 to 16 import packages that are required to create views for the selected app parts in the app.
Line 22 defines a class SelectAppFragment which has following fields
A late intialized variable
binding
that holds SelectAppFragment Binding instance.A late initialized variable
mainViewModel
that holds MainViewModel instance.A late initialized variable
vultViewModel
that holds VultViewModel instance
Line 27 overrides the onCreateView function which creates and returns the view hierarchy associated with the SelectAppFragment.
Line 32 selected app fragment binding is called via inflate which will uncompress all the binding data and provide the container with the uncompressed data
Line 33 mainViewModel variable get assigned with a ViewModelProvider instance which provides Models to produce Views for MainViewModel.
Line 34 VultViewModel variable gets assigned with a ViewModelProvider instance which provides Models to produce Views for VultViewModel.
Line 36 to 42 defines view for for data related to Wallet Details. On button click present the recyclable view in WalletDetailsBottomScreenFragment
Line 44 to 71 defines view for the data related to Allocation Details. On button click present the recyclable view in AllocationDetailsBottomScreenFragment
Line 72 to 79 defines view for the data related to Network Details.On button click present the view in NetworkDetailsBottomScreenFragment
Line 81 to 93 defines view for Bolt selected part. It sets the wallet via WalletModel on and navigates the user to Bolt UI on button click.
Line 94 to 105 defines view for Vult selected part. It sets the wallet via WalletModel and navigates the user to VultUI on button click.
Line 107
binding.root
binds the entire view to outermost container in layout.
ui/selectapp/WalletDetailsBottomScreenFragment.kt
package org.zus.bolt.helloworld.ui.selectapp
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.WalletModel
import org.zus.bolt.helloworld.models.selectapp.DetailsListModel
import org.zus.bolt.helloworld.models.selectapp.DetailsModel
import org.zus.bolt.helloworld.utils.Utils.Companion.prettyJsonFormat
class WalletDetailsBottomScreenFragment(
private val walletModel: WalletModel,
) : 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.wallet_details_title)
val walletDetailsListModel = DetailsListModel(
title = getString(R.string.details),
detailsList = listOf(
DetailsModel(
title = "ClientID",
value = walletModel.mClientId,
showArrowButton = true
),
DetailsModel(
title = "Private Encryption Key",
value = walletModel.mKeys[0].mPrivateKey,
showArrowButton = true
),
DetailsModel(
title = "Public Encryption Key",
value = walletModel.mKeys[0].mPublicKey,
showArrowButton = true
),
DetailsModel(
title = "Mnemonics",
value = walletModel.mMnemonics,
showArrowButton = true
)
)
)
val walletJsonModel = DetailsListModel(
title = getString(R.string.wallet_json_title),
detailsList = listOf(
DetailsModel(
title = walletModel.walletJson.prettyJsonFormat(),
value = walletModel.walletJson,
showArrowButton = false
)
)
)
val detailsListModel = listOf(walletDetailsListModel, walletJsonModel)
//linear layout adapter
binding.detailsListView.removeAllViews()
for (detailsModel in detailsListModel) {
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
}
}
Line 3 to 12 import packages required to create UI for Network Details in App.
Line 15 defines a class NetworkDetailsBottomScreenFragment which has following fields:
(Line 16) A private variable that holds access to NetworkModel instance.
(Line 15) A private late initialized binding variable that holds Bottom Sheet Dialog object instance
Line 17 overrides the onCreateView function which creates and returns the view hierarchy associated with the NetworkDetailsBottomScreenFragment.
The view hierarchy as follows:(Line 25 to 35) in a BottomSheetDialog Network Name ,URL,0box URL and Domain are list as list pairs. See gif for reference
The Network Details are fetched via [Utils.kt] method and from config.json stored in assets.
Line 41 and 43 defines a variable
linearArrayAdapter
that holds Network details in DetailListAdaptertype. DetailsListView handles addition and removal without the need to redraw the entire view
Line 43
binding.root
binds the entire view to outermost container in layout.
ui/selectapp/DetailsBottomSheetFragment.kt
package org.zus.bolt.helloworld.ui.selectapp
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context.CLIPBOARD_SERVICE
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.navigation.fragment.findNavController
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.zus.bolt.helloworld.databinding.DetailsFragmentBinding
import org.zus.bolt.helloworld.models.selectapp.DetailsModel
class DetailsBottomSheetFragment(
private val detailsModel: DetailsModel,
) : BottomSheetDialogFragment() {
private lateinit var binding: DetailsFragmentBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DetailsFragmentBinding.inflate(inflater, container, false)
/* Header title. */
binding.tvHeaderTitle.text = detailsModel.title
binding.btnBack.setOnClickListener {
findNavController().popBackStack()
}
binding.tvDetails.text = detailsModel.value
binding.tvDetails.setOnLongClickListener {
val clipboard =
requireActivity().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("label", detailsModel.value)
clipboard.setPrimaryClip(clip)
Toast.makeText(requireContext(), "Copied to clipboard", Toast.LENGTH_SHORT).show()
true
}
return binding.root
}
}
Describing Code :
Line 3 to 14 import packages to create fragment for viewing details in the sample app.
Line 16 defines a class DetailsBottomScreenFragment which has following fields
Line 19 defines a private late initialized binding variable that holds DetailsFragmentBinding object.
Line 21 to 42 overrides the onCreateView function which creates and returns the view for the details reusable portion of the sample app.
Line 44 binds the entire view to outermost container in layout.
Last updated