From bd67d6d4bde57a38920efbd48abbdd17f73a347e Mon Sep 17 00:00:00 2001 From: pokkst Date: Sat, 9 Dec 2023 02:30:55 -0600 Subject: [PATCH] Refactoring settings/onboarding screens --- .../fragment/onboarding/OnboardingFragment.kt | 164 ++++--- .../onboarding/OnboardingViewModel.kt | 66 ++- .../fragment/settings/SettingsFragment.kt | 191 +++++--- .../fragment/settings/SettingsViewModel.kt | 35 ++ .../wallet/livedata/CombinedLiveDatas.kt | 123 ++++++ .../net/mynero/wallet/livedata/LiveData.kt | 414 ------------------ .../net/mynero/wallet/service/ProxyService.kt | 2 - .../main/res/layout/fragment_onboarding.xml | 28 +- app/src/main/res/layout/fragment_settings.xml | 22 +- 9 files changed, 468 insertions(+), 577 deletions(-) create mode 100644 app/src/main/java/net/mynero/wallet/livedata/CombinedLiveDatas.kt delete mode 100644 app/src/main/java/net/mynero/wallet/livedata/LiveData.kt diff --git a/app/src/main/java/net/mynero/wallet/fragment/onboarding/OnboardingFragment.kt b/app/src/main/java/net/mynero/wallet/fragment/onboarding/OnboardingFragment.kt index 48e46b7..438a652 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/onboarding/OnboardingFragment.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/onboarding/OnboardingFragment.kt @@ -1,10 +1,8 @@ package net.mynero.wallet.fragment.onboarding -import android.app.Activity import android.os.Bundle import android.text.Editable import android.text.TextWatcher -import android.util.Patterns import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -18,13 +16,12 @@ import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.appcompat.widget.SwitchCompat import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.widget.addTextChangedListener import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope +import com.google.android.material.progressindicator.CircularProgressIndicator import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import net.mynero.wallet.MoneroApplication import net.mynero.wallet.R import net.mynero.wallet.data.Node import net.mynero.wallet.fragment.dialog.AddNodeBottomSheetDialog @@ -32,9 +29,9 @@ import net.mynero.wallet.fragment.dialog.AddNodeBottomSheetDialog.AddNodeListene import net.mynero.wallet.fragment.dialog.NodeSelectionBottomSheetDialog import net.mynero.wallet.fragment.dialog.NodeSelectionBottomSheetDialog.NodeSelectionDialogListener import net.mynero.wallet.fragment.onboarding.OnboardingViewModel.SeedType +import net.mynero.wallet.model.EnumTorState import net.mynero.wallet.service.PrefService import net.mynero.wallet.service.ProxyService -import net.mynero.wallet.util.Constants class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListener { private var useOffset = true @@ -56,6 +53,7 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe private var xmrchanOnboardingImage: ImageView? = null private var seedTypeButton: Button? = null private var seedTypeDescTextView: TextView? = null + private var useBundledTor: CheckBox? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -83,11 +81,38 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe xmrchanOnboardingImage = view.findViewById(R.id.xmrchan_onboarding_imageview) seedTypeButton = view.findViewById(R.id.seed_type_button) seedTypeDescTextView = view.findViewById(R.id.seed_type_desc_textview) + useBundledTor = view.findViewById(R.id.bundled_tor_checkbox) + + seedOffsetCheckbox?.isChecked = useOffset + showXmrchanSwitch?.isChecked = true + val usingProxy = ProxyService.instance?.usingProxy == true + val usingBundledTor = ProxyService.instance?.useBundledTor == true + + torSwitch?.isChecked = usingProxy + useBundledTor?.isChecked = usingBundledTor + useBundledTor?.isEnabled = usingProxy + walletProxyAddressEditText?.isEnabled = usingProxy && !usingBundledTor + walletProxyPortEditText?.isEnabled = usingProxy && !usingBundledTor + walletProxyPortEditText?.visibility = if (usingBundledTor) View.GONE else View.VISIBLE + walletProxyAddressEditText?.visibility = if (usingBundledTor) View.GONE else View.VISIBLE + + val node = PrefService.instance?.node // should be using default here + selectNodeButton?.text = getString(R.string.node_button_text, node?.address) + bindListeners() bindObservers() } private fun bindObservers() { + mViewModel?.passphrase?.observe(viewLifecycleOwner) { text -> + if (text.isEmpty()) { + walletPasswordConfirmEditText?.text = null + walletPasswordConfirmEditText?.visibility = View.GONE + } else { + walletPasswordConfirmEditText?.visibility = View.VISIBLE + } + } + mViewModel?.showMoreOptions?.observe(viewLifecycleOwner) { show: Boolean -> if (show) { moreOptionsChevronImageView?.setImageResource(R.drawable.ic_keyboard_arrow_up) @@ -97,9 +122,11 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe advancedOptionsLayout?.visibility = View.GONE } } + mViewModel?.enableButton?.observe(viewLifecycleOwner) { enable: Boolean -> createWalletButton?.isEnabled = enable } + mViewModel?.seedType?.observe(viewLifecycleOwner) { seedType: SeedType -> seedTypeButton?.text = seedType.toString() seedTypeDescTextView?.text = getText(seedType.descResId) @@ -116,59 +143,92 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe } } + mViewModel?.showMonerochan?.observe(viewLifecycleOwner) { + if (it) { + xmrchanOnboardingImage?.visibility = View.VISIBLE + } else { + xmrchanOnboardingImage?.visibility = View.GONE + } + } + + mViewModel?.useBundledTor?.observe(viewLifecycleOwner) { isChecked -> + walletProxyPortEditText?.visibility = if (isChecked) View.GONE else View.VISIBLE + walletProxyAddressEditText?.visibility = if (isChecked) View.GONE else View.VISIBLE + } + + mViewModel?.useProxy?.observe(viewLifecycleOwner) { useProxy -> + useBundledTor?.isEnabled = useProxy + walletProxyAddressEditText?.isEnabled = useProxy + walletProxyPortEditText?.isEnabled = useProxy + } + val samouraiTorManager = ProxyService.instance?.samouraiTorManager - samouraiTorManager?.getTorStateLiveData()?.observeForever { - println("STATE CHANGE:: ${it.state.name}") + val indicatorCircle = view?.findViewById(R.id.onboarding_tor_loading_progressindicator) + val torIcon = view?.findViewById(R.id.onboarding_tor_icon) + + samouraiTorManager?.getTorStateLiveData()?.observe(viewLifecycleOwner) { state -> samouraiTorManager.getProxy()?.address()?.let { socketAddress -> if(socketAddress.toString().isEmpty()) return@let - println("PROXY INIT") val proxyString = socketAddress.toString().substring(1) val address = proxyString.split(":")[0] val port = proxyString.split(":")[1] if(mViewModel?.useProxy?.value == true && mViewModel?.useBundledTor?.value == true) { mViewModel?.setProxyAddress(address) mViewModel?.setProxyPort(port) + torIcon?.visibility = View.VISIBLE + indicatorCircle?.visibility = View.INVISIBLE } } + + indicatorCircle?.isIndeterminate = state.progressIndicator == 0 + indicatorCircle?.progress = state.progressIndicator + + when (state.state) { + EnumTorState.OFF -> { + torIcon?.visibility = View.INVISIBLE + indicatorCircle?.visibility = View.INVISIBLE + } + EnumTorState.STARTING -> { + torIcon?.visibility = View.INVISIBLE + indicatorCircle?.visibility = View.VISIBLE + } + EnumTorState.STOPPING -> { + torIcon?.visibility = View.INVISIBLE + indicatorCircle?.visibility = View.VISIBLE + } + else -> {} + } } } private fun bindListeners() { - val useBundledTor = view?.findViewById(R.id.bundled_tor_checkbox) - - seedOffsetCheckbox?.isChecked = useOffset // Disable onBack click val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() {} } - val activity = activity activity?.onBackPressedDispatcher?.addCallback(viewLifecycleOwner, onBackPressedCallback) + moreOptionsDropdownTextView?.setOnClickListener { mViewModel?.onMoreOptionsClicked() } + moreOptionsChevronImageView?.setOnClickListener { mViewModel?.onMoreOptionsClicked() } + seedOffsetCheckbox?.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean -> useOffset = b } + createWalletButton?.setOnClickListener { onBackPressedCallback.isEnabled = false - (getActivity()?.application as MoneroApplication).executor?.execute { - createOrImportWallet( - walletSeedEditText?.text.toString().trim { it <= ' ' }, - walletRestoreHeightEditText?.text.toString().trim { it <= ' ' } - ) - } + createOrImportWallet( + walletSeedEditText?.text.toString().trim { it <= ' ' }, + walletRestoreHeightEditText?.text.toString().trim { it <= ' ' } + ) } + walletPasswordEditText?.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun afterTextChanged(editable: Editable) { - val text = editable.toString() - mViewModel?.setPassphrase(text) - if (text.isEmpty()) { - walletPasswordConfirmEditText?.text = null - walletPasswordConfirmEditText?.visibility = View.GONE - } else { - walletPasswordConfirmEditText?.visibility = View.VISIBLE - } + mViewModel?.setPassphrase(editable.toString()) } }) @@ -176,10 +236,10 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun afterTextChanged(editable: Editable) { - val text = editable.toString() - mViewModel?.setConfirmedPassphrase(text) + mViewModel?.setConfirmedPassphrase(editable.toString()) } }) + walletSeedEditText?.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} @@ -192,19 +252,13 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe } } }) + seedTypeButton?.setOnClickListener { toggleSeedType() } - torSwitch?.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean -> - if (b) { - useBundledTor?.visibility = View.VISIBLE - walletProxyAddressEditText?.visibility = View.VISIBLE - walletProxyPortEditText?.visibility = View.VISIBLE - } else { - useBundledTor?.visibility = View.GONE - walletProxyAddressEditText?.visibility = View.GONE - walletProxyPortEditText?.visibility = View.GONE - } + + torSwitch?.setOnCheckedChangeListener { _, b: Boolean -> mViewModel?.setUseProxy(b) } + walletProxyPortEditText?.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} @@ -213,6 +267,7 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe mViewModel?.setProxyPort(text) } }) + walletProxyAddressEditText?.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} @@ -221,16 +276,11 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe mViewModel?.setProxyAddress(text) } }) - showXmrchanSwitch?.isChecked = true + showXmrchanSwitch?.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean -> - if (b) { - xmrchanOnboardingImage?.visibility = View.VISIBLE - } else { - xmrchanOnboardingImage?.visibility = View.GONE - } + mViewModel?.setMonerochan(b) } - val node = PrefService.instance?.node // should be using default here - selectNodeButton?.text = getString(R.string.node_button_text, node?.address) + selectNodeButton?.setOnClickListener { activity?.supportFragmentManager?.let { fragmentManager -> val dialog = NodeSelectionBottomSheetDialog() @@ -240,13 +290,7 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe } useBundledTor?.setOnCheckedChangeListener { _, isChecked -> - walletProxyPortEditText?.visibility = if (isChecked) View.GONE else View.VISIBLE - walletProxyAddressEditText?.visibility = if (isChecked) View.GONE else View.VISIBLE - - if(isChecked) { - ProxyService.instance?.samouraiTorManager?.start() - } else { - ProxyService.instance?.samouraiTorManager?.stop() + if(!isChecked) { mViewModel?.setProxyAddress("") mViewModel?.setProxyPort("") } @@ -270,11 +314,10 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe walletSeed: String, restoreHeightText: String ) { - val activity: Activity? = activity - if (activity != null) { + activity?.let { act -> lifecycleScope.launch(Dispatchers.IO) { mViewModel?.createOrImportWallet( - activity, + act, walletSeed, restoreHeightText, useOffset @@ -288,10 +331,9 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe selectNodeButton?.text = getString(R.string.node_button_text, node?.address) Toast.makeText( activity, - getString(R.string.node_selected), + getString(R.string.node_selected, node?.name ?: node?.host), Toast.LENGTH_SHORT ).show() - refreshProxy() } override fun onClickedEditNode(node: Node?) {} @@ -310,10 +352,4 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe dialog.show(fragmentManager, "node_selection_dialog") } } - - private fun refreshProxy() { - val proxyAddress = walletProxyAddressEditText?.text.toString() - val proxyPort = walletProxyPortEditText?.text.toString() - ProxyService.instance?.updateProxy(proxyAddress, proxyPort) - } } \ No newline at end of file diff --git a/app/src/main/java/net/mynero/wallet/fragment/onboarding/OnboardingViewModel.kt b/app/src/main/java/net/mynero/wallet/fragment/onboarding/OnboardingViewModel.kt index 5650f6f..f208bf8 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/onboarding/OnboardingViewModel.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/onboarding/OnboardingViewModel.kt @@ -1,7 +1,6 @@ package net.mynero.wallet.fragment.onboarding import android.app.Activity -import android.util.Patterns import android.widget.Toast import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData @@ -9,7 +8,7 @@ import androidx.lifecycle.ViewModel import net.mynero.wallet.MainActivity import net.mynero.wallet.MoneroApplication import net.mynero.wallet.R -import net.mynero.wallet.livedata.combineLatestIgnoreNull +import net.mynero.wallet.livedata.combineLiveDatas import net.mynero.wallet.model.Wallet import net.mynero.wallet.model.WalletManager import net.mynero.wallet.service.PrefService @@ -30,11 +29,19 @@ class OnboardingViewModel : ViewModel() { private val _useBundledTor = MutableLiveData(false) val useBundledTor: LiveData = _useBundledTor private val _passphrase = MutableLiveData("") + val passphrase: LiveData = _passphrase private val _confirmedPassphrase = MutableLiveData("") + private val _showMonerochan = MutableLiveData(true) + val showMonerochan: LiveData = _showMonerochan var showMoreOptions: LiveData = _showMoreOptions var seedType: LiveData = _seedType - val enableButton = combineLatestIgnoreNull( + init { + _useProxy.value = ProxyService.instance?.usingProxy + _useBundledTor.value = ProxyService.instance?.useBundledTor + } + + val enableButton = combineLiveDatas( seedType, _useProxy, _proxyAddress, @@ -44,14 +51,14 @@ class OnboardingViewModel : ViewModel() { _confirmedPassphrase, _creatingWallet ) { seedType, useProxy, proxyAddress, proxyPort, useBundledTor, passphrase, confirmedPassphrase, creatingWallet -> - if(seedType == null || useProxy == null || proxyAddress == null || proxyPort == null || useBundledTor == null || passphrase == null || confirmedPassphrase == null || creatingWallet == null) return@combineLatestIgnoreNull false - if((passphrase.isNotEmpty() || confirmedPassphrase.isNotEmpty()) && passphrase != confirmedPassphrase) return@combineLatestIgnoreNull false - if(creatingWallet) return@combineLatestIgnoreNull false - if(seedType == SeedType.POLYSEED && passphrase.isEmpty()) return@combineLatestIgnoreNull false - if(useProxy && (proxyAddress.isEmpty() || proxyPort.isEmpty())) return@combineLatestIgnoreNull false - if(useBundledTor && (proxyAddress.isEmpty() || proxyPort.isEmpty())) return@combineLatestIgnoreNull false + if(seedType == null || useProxy == null || proxyAddress == null || proxyPort == null || useBundledTor == null || passphrase == null || confirmedPassphrase == null || creatingWallet == null) return@combineLiveDatas false + if((passphrase.isNotEmpty() || confirmedPassphrase.isNotEmpty()) && passphrase != confirmedPassphrase) return@combineLiveDatas false + if(creatingWallet) return@combineLiveDatas false + if(seedType == SeedType.POLYSEED && (passphrase.isEmpty() || confirmedPassphrase.isEmpty())) return@combineLiveDatas false + if(useProxy && (proxyAddress.isEmpty() || proxyPort.isEmpty())) return@combineLiveDatas false + if(useBundledTor && (proxyAddress.isEmpty() || proxyPort.isEmpty())) return@combineLiveDatas false - return@combineLatestIgnoreNull true + return@combineLiveDatas true } fun onMoreOptionsClicked() { @@ -72,7 +79,6 @@ class OnboardingViewModel : ViewModel() { ) { val passphrase = _passphrase.value ?: return val confirmedPassphrase = _confirmedPassphrase.value ?: return - val useProxy = _useProxy.value ?: return val application = mainActivity.application as MoneroApplication _creatingWallet.postValue(true) @@ -172,15 +178,6 @@ class OnboardingViewModel : ViewModel() { val ok = walletStatus?.isOk walletFile.delete() // cache is broken for some reason when recovering wallets. delete the file here. this happens in monerujo too. if (ok == true) { - var editor = PrefService.instance?.edit() - ?.putBoolean(Constants.PREF_USE_BUNDLED_TOR, _useBundledTor.value == true) - ?.putBoolean(Constants.PREF_USES_PROXY, useProxy) - if(useProxy) { - editor = editor?.putString(Constants.PREF_PROXY, "${_proxyAddress.value}:${_proxyPort.value}") - } - - editor?.apply() - (mainActivity as MainActivity).init(walletFile, passphrase) mainActivity.runOnUiThread { mainActivity.onBackPressed() } } else { @@ -228,18 +225,42 @@ class OnboardingViewModel : ViewModel() { fun setProxyAddress(address: String) { _proxyAddress.value = address + val port = _proxyPort.value ?: return + val proxyAddress = "$address:$port" + if(proxyAddress == ":") return + PrefService.instance?.edit()?.putString(Constants.PREF_PROXY, proxyAddress)?.apply() } fun setProxyPort(port: String) { _proxyPort.value = port + val address = _proxyAddress.value ?: return + val proxyAddress = "$address:$port" + if(proxyAddress == ":") return + PrefService.instance?.edit()?.putString(Constants.PREF_PROXY, proxyAddress)?.apply() } fun setUseBundledTor(useBundled: Boolean) { _useBundledTor.value = useBundled + PrefService.instance?.edit()?.putBoolean(Constants.PREF_USE_BUNDLED_TOR, useBundled)?.apply() + + val samouraiTorManager = ProxyService.instance?.samouraiTorManager + if(useBundled && useProxy.value == true) { + samouraiTorManager?.start() + } else { + samouraiTorManager?.stop() + } } fun setUseProxy(useProxy: Boolean) { _useProxy.value = useProxy + PrefService.instance?.edit()?.putBoolean(Constants.PREF_USES_PROXY, useProxy)?.apply() + + val samouraiTorManager = ProxyService.instance?.samouraiTorManager + if(useProxy && useBundledTor.value == true) { + samouraiTorManager?.start() + } else { + samouraiTorManager?.stop() + } } fun setPassphrase(passphrase: String) { @@ -250,6 +271,11 @@ class OnboardingViewModel : ViewModel() { _confirmedPassphrase.value = confirmedPassphrase } + fun setMonerochan(b: Boolean) { + _showMonerochan.value = b + PrefService.instance?.edit()?.putBoolean(Constants.PREF_MONEROCHAN, b)?.apply() + } + enum class SeedType(val descResId: Int) { LEGACY(R.string.seed_desc_legacy), POLYSEED(R.string.seed_desc_polyseed), UNKNOWN(0) diff --git a/app/src/main/java/net/mynero/wallet/fragment/settings/SettingsFragment.kt b/app/src/main/java/net/mynero/wallet/fragment/settings/SettingsFragment.kt index c5eeabc..00ecd1d 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/settings/SettingsFragment.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/settings/SettingsFragment.kt @@ -9,6 +9,8 @@ import android.widget.Button import android.widget.CheckBox import android.widget.CompoundButton import android.widget.EditText +import android.widget.ImageView +import android.widget.Switch import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.appcompat.widget.SwitchCompat @@ -17,6 +19,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController +import com.google.android.material.progressindicator.CircularProgressIndicator import net.mynero.wallet.R import net.mynero.wallet.data.Node import net.mynero.wallet.data.Node.Companion.fromJson @@ -29,6 +32,7 @@ import net.mynero.wallet.fragment.dialog.NodeSelectionBottomSheetDialog.NodeSele import net.mynero.wallet.fragment.dialog.PasswordBottomSheetDialog import net.mynero.wallet.fragment.dialog.PasswordBottomSheetDialog.PasswordListener import net.mynero.wallet.fragment.dialog.WalletKeysBottomSheetDialog +import net.mynero.wallet.model.EnumTorState import net.mynero.wallet.service.BalanceService import net.mynero.wallet.service.HistoryService import net.mynero.wallet.service.PrefService @@ -44,6 +48,14 @@ class SettingsFragment : Fragment(), PasswordListener, NodeSelectionDialogListen private var selectNodeButton: Button? = null private var cachedProxyAddress: String = "" private var cachedProxyPort: String = "" + private var streetModeSwitch: SwitchCompat? = null + private var monerochanSwitch: SwitchCompat? = null + private var donationSwitch: SwitchCompat? = null + private var useBundledTor: CheckBox? = null + private var displaySeedButton: Button? = null + private var displayUtxosButton: Button? = null + private var torSwitch: SwitchCompat? = null + private var proxySettingsLayout: ConstraintLayout? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -55,68 +67,76 @@ class SettingsFragment : Fragment(), PasswordListener, NodeSelectionDialogListen override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) mViewModel = ViewModelProvider(this)[SettingsViewModel::class.java] - val displaySeedButton = view.findViewById