Refactoring settings/onboarding screens

This commit is contained in:
pokkst 2023-12-09 02:30:55 -06:00
parent 3b77ae3673
commit bd67d6d4bd
No known key found for this signature in database
GPG key ID: EC4FAAA66859FAA4
9 changed files with 468 additions and 577 deletions

View file

@ -1,10 +1,8 @@
package net.mynero.wallet.fragment.onboarding package net.mynero.wallet.fragment.onboarding
import android.app.Activity
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.util.Patterns
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -18,13 +16,12 @@ import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.appcompat.widget.SwitchCompat import androidx.appcompat.widget.SwitchCompat
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.material.progressindicator.CircularProgressIndicator
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.mynero.wallet.MoneroApplication
import net.mynero.wallet.R import net.mynero.wallet.R
import net.mynero.wallet.data.Node import net.mynero.wallet.data.Node
import net.mynero.wallet.fragment.dialog.AddNodeBottomSheetDialog 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
import net.mynero.wallet.fragment.dialog.NodeSelectionBottomSheetDialog.NodeSelectionDialogListener import net.mynero.wallet.fragment.dialog.NodeSelectionBottomSheetDialog.NodeSelectionDialogListener
import net.mynero.wallet.fragment.onboarding.OnboardingViewModel.SeedType 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.PrefService
import net.mynero.wallet.service.ProxyService import net.mynero.wallet.service.ProxyService
import net.mynero.wallet.util.Constants
class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListener { class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListener {
private var useOffset = true private var useOffset = true
@ -56,6 +53,7 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe
private var xmrchanOnboardingImage: ImageView? = null private var xmrchanOnboardingImage: ImageView? = null
private var seedTypeButton: Button? = null private var seedTypeButton: Button? = null
private var seedTypeDescTextView: TextView? = null private var seedTypeDescTextView: TextView? = null
private var useBundledTor: CheckBox? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
@ -83,11 +81,38 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe
xmrchanOnboardingImage = view.findViewById(R.id.xmrchan_onboarding_imageview) xmrchanOnboardingImage = view.findViewById(R.id.xmrchan_onboarding_imageview)
seedTypeButton = view.findViewById(R.id.seed_type_button) seedTypeButton = view.findViewById(R.id.seed_type_button)
seedTypeDescTextView = view.findViewById(R.id.seed_type_desc_textview) 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() bindListeners()
bindObservers() bindObservers()
} }
private fun 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 -> mViewModel?.showMoreOptions?.observe(viewLifecycleOwner) { show: Boolean ->
if (show) { if (show) {
moreOptionsChevronImageView?.setImageResource(R.drawable.ic_keyboard_arrow_up) moreOptionsChevronImageView?.setImageResource(R.drawable.ic_keyboard_arrow_up)
@ -97,9 +122,11 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe
advancedOptionsLayout?.visibility = View.GONE advancedOptionsLayout?.visibility = View.GONE
} }
} }
mViewModel?.enableButton?.observe(viewLifecycleOwner) { enable: Boolean -> mViewModel?.enableButton?.observe(viewLifecycleOwner) { enable: Boolean ->
createWalletButton?.isEnabled = enable createWalletButton?.isEnabled = enable
} }
mViewModel?.seedType?.observe(viewLifecycleOwner) { seedType: SeedType -> mViewModel?.seedType?.observe(viewLifecycleOwner) { seedType: SeedType ->
seedTypeButton?.text = seedType.toString() seedTypeButton?.text = seedType.toString()
seedTypeDescTextView?.text = getText(seedType.descResId) 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 val samouraiTorManager = ProxyService.instance?.samouraiTorManager
samouraiTorManager?.getTorStateLiveData()?.observeForever { val indicatorCircle = view?.findViewById<CircularProgressIndicator>(R.id.onboarding_tor_loading_progressindicator)
println("STATE CHANGE:: ${it.state.name}") val torIcon = view?.findViewById<ImageView>(R.id.onboarding_tor_icon)
samouraiTorManager?.getTorStateLiveData()?.observe(viewLifecycleOwner) { state ->
samouraiTorManager.getProxy()?.address()?.let { socketAddress -> samouraiTorManager.getProxy()?.address()?.let { socketAddress ->
if(socketAddress.toString().isEmpty()) return@let if(socketAddress.toString().isEmpty()) return@let
println("PROXY INIT")
val proxyString = socketAddress.toString().substring(1) val proxyString = socketAddress.toString().substring(1)
val address = proxyString.split(":")[0] val address = proxyString.split(":")[0]
val port = proxyString.split(":")[1] val port = proxyString.split(":")[1]
if(mViewModel?.useProxy?.value == true && mViewModel?.useBundledTor?.value == true) { if(mViewModel?.useProxy?.value == true && mViewModel?.useBundledTor?.value == true) {
mViewModel?.setProxyAddress(address) mViewModel?.setProxyAddress(address)
mViewModel?.setProxyPort(port) 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() { private fun bindListeners() {
val useBundledTor = view?.findViewById<CheckBox>(R.id.bundled_tor_checkbox)
seedOffsetCheckbox?.isChecked = useOffset
// Disable onBack click // Disable onBack click
val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) { val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {} override fun handleOnBackPressed() {}
} }
val activity = activity
activity?.onBackPressedDispatcher?.addCallback(viewLifecycleOwner, onBackPressedCallback) activity?.onBackPressedDispatcher?.addCallback(viewLifecycleOwner, onBackPressedCallback)
moreOptionsDropdownTextView?.setOnClickListener { mViewModel?.onMoreOptionsClicked() } moreOptionsDropdownTextView?.setOnClickListener { mViewModel?.onMoreOptionsClicked() }
moreOptionsChevronImageView?.setOnClickListener { mViewModel?.onMoreOptionsClicked() } moreOptionsChevronImageView?.setOnClickListener { mViewModel?.onMoreOptionsClicked() }
seedOffsetCheckbox?.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean -> seedOffsetCheckbox?.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean ->
useOffset = b useOffset = b
} }
createWalletButton?.setOnClickListener { createWalletButton?.setOnClickListener {
onBackPressedCallback.isEnabled = false onBackPressedCallback.isEnabled = false
(getActivity()?.application as MoneroApplication).executor?.execute { createOrImportWallet(
createOrImportWallet( walletSeedEditText?.text.toString().trim { it <= ' ' },
walletSeedEditText?.text.toString().trim { it <= ' ' }, walletRestoreHeightEditText?.text.toString().trim { it <= ' ' }
walletRestoreHeightEditText?.text.toString().trim { it <= ' ' } )
)
}
} }
walletPasswordEditText?.addTextChangedListener(object : TextWatcher { walletPasswordEditText?.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun afterTextChanged(editable: Editable) { override fun afterTextChanged(editable: Editable) {
val text = editable.toString() mViewModel?.setPassphrase(editable.toString())
mViewModel?.setPassphrase(text)
if (text.isEmpty()) {
walletPasswordConfirmEditText?.text = null
walletPasswordConfirmEditText?.visibility = View.GONE
} else {
walletPasswordConfirmEditText?.visibility = View.VISIBLE
}
} }
}) })
@ -176,10 +236,10 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun afterTextChanged(editable: Editable) { override fun afterTextChanged(editable: Editable) {
val text = editable.toString() mViewModel?.setConfirmedPassphrase(editable.toString())
mViewModel?.setConfirmedPassphrase(text)
} }
}) })
walletSeedEditText?.addTextChangedListener(object : TextWatcher { walletSeedEditText?.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun onTextChanged(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() } seedTypeButton?.setOnClickListener { toggleSeedType() }
torSwitch?.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean ->
if (b) { torSwitch?.setOnCheckedChangeListener { _, b: Boolean ->
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
}
mViewModel?.setUseProxy(b) mViewModel?.setUseProxy(b)
} }
walletProxyPortEditText?.addTextChangedListener(object : TextWatcher { walletProxyPortEditText?.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun onTextChanged(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) mViewModel?.setProxyPort(text)
} }
}) })
walletProxyAddressEditText?.addTextChangedListener(object : TextWatcher { walletProxyAddressEditText?.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun onTextChanged(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) mViewModel?.setProxyAddress(text)
} }
}) })
showXmrchanSwitch?.isChecked = true
showXmrchanSwitch?.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean -> showXmrchanSwitch?.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean ->
if (b) { mViewModel?.setMonerochan(b)
xmrchanOnboardingImage?.visibility = View.VISIBLE
} else {
xmrchanOnboardingImage?.visibility = View.GONE
}
} }
val node = PrefService.instance?.node // should be using default here
selectNodeButton?.text = getString(R.string.node_button_text, node?.address)
selectNodeButton?.setOnClickListener { selectNodeButton?.setOnClickListener {
activity?.supportFragmentManager?.let { fragmentManager -> activity?.supportFragmentManager?.let { fragmentManager ->
val dialog = NodeSelectionBottomSheetDialog() val dialog = NodeSelectionBottomSheetDialog()
@ -240,13 +290,7 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe
} }
useBundledTor?.setOnCheckedChangeListener { _, isChecked -> useBundledTor?.setOnCheckedChangeListener { _, isChecked ->
walletProxyPortEditText?.visibility = if (isChecked) View.GONE else View.VISIBLE if(!isChecked) {
walletProxyAddressEditText?.visibility = if (isChecked) View.GONE else View.VISIBLE
if(isChecked) {
ProxyService.instance?.samouraiTorManager?.start()
} else {
ProxyService.instance?.samouraiTorManager?.stop()
mViewModel?.setProxyAddress("") mViewModel?.setProxyAddress("")
mViewModel?.setProxyPort("") mViewModel?.setProxyPort("")
} }
@ -270,11 +314,10 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe
walletSeed: String, walletSeed: String,
restoreHeightText: String restoreHeightText: String
) { ) {
val activity: Activity? = activity activity?.let { act ->
if (activity != null) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
mViewModel?.createOrImportWallet( mViewModel?.createOrImportWallet(
activity, act,
walletSeed, walletSeed,
restoreHeightText, restoreHeightText,
useOffset useOffset
@ -288,10 +331,9 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe
selectNodeButton?.text = getString(R.string.node_button_text, node?.address) selectNodeButton?.text = getString(R.string.node_button_text, node?.address)
Toast.makeText( Toast.makeText(
activity, activity,
getString(R.string.node_selected), getString(R.string.node_selected, node?.name ?: node?.host),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
refreshProxy()
} }
override fun onClickedEditNode(node: Node?) {} override fun onClickedEditNode(node: Node?) {}
@ -310,10 +352,4 @@ class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListe
dialog.show(fragmentManager, "node_selection_dialog") dialog.show(fragmentManager, "node_selection_dialog")
} }
} }
private fun refreshProxy() {
val proxyAddress = walletProxyAddressEditText?.text.toString()
val proxyPort = walletProxyPortEditText?.text.toString()
ProxyService.instance?.updateProxy(proxyAddress, proxyPort)
}
} }

View file

@ -1,7 +1,6 @@
package net.mynero.wallet.fragment.onboarding package net.mynero.wallet.fragment.onboarding
import android.app.Activity import android.app.Activity
import android.util.Patterns
import android.widget.Toast import android.widget.Toast
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
@ -9,7 +8,7 @@ import androidx.lifecycle.ViewModel
import net.mynero.wallet.MainActivity import net.mynero.wallet.MainActivity
import net.mynero.wallet.MoneroApplication import net.mynero.wallet.MoneroApplication
import net.mynero.wallet.R 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.Wallet
import net.mynero.wallet.model.WalletManager import net.mynero.wallet.model.WalletManager
import net.mynero.wallet.service.PrefService import net.mynero.wallet.service.PrefService
@ -30,11 +29,19 @@ class OnboardingViewModel : ViewModel() {
private val _useBundledTor = MutableLiveData(false) private val _useBundledTor = MutableLiveData(false)
val useBundledTor: LiveData<Boolean> = _useBundledTor val useBundledTor: LiveData<Boolean> = _useBundledTor
private val _passphrase = MutableLiveData("") private val _passphrase = MutableLiveData("")
val passphrase: LiveData<String> = _passphrase
private val _confirmedPassphrase = MutableLiveData("") private val _confirmedPassphrase = MutableLiveData("")
private val _showMonerochan = MutableLiveData(true)
val showMonerochan: LiveData<Boolean> = _showMonerochan
var showMoreOptions: LiveData<Boolean> = _showMoreOptions var showMoreOptions: LiveData<Boolean> = _showMoreOptions
var seedType: LiveData<SeedType> = _seedType var seedType: LiveData<SeedType> = _seedType
val enableButton = combineLatestIgnoreNull( init {
_useProxy.value = ProxyService.instance?.usingProxy
_useBundledTor.value = ProxyService.instance?.useBundledTor
}
val enableButton = combineLiveDatas(
seedType, seedType,
_useProxy, _useProxy,
_proxyAddress, _proxyAddress,
@ -44,14 +51,14 @@ class OnboardingViewModel : ViewModel() {
_confirmedPassphrase, _confirmedPassphrase,
_creatingWallet _creatingWallet
) { seedType, useProxy, proxyAddress, proxyPort, useBundledTor, passphrase, 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(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@combineLatestIgnoreNull false if((passphrase.isNotEmpty() || confirmedPassphrase.isNotEmpty()) && passphrase != confirmedPassphrase) return@combineLiveDatas false
if(creatingWallet) return@combineLatestIgnoreNull false if(creatingWallet) return@combineLiveDatas false
if(seedType == SeedType.POLYSEED && passphrase.isEmpty()) return@combineLatestIgnoreNull false if(seedType == SeedType.POLYSEED && (passphrase.isEmpty() || confirmedPassphrase.isEmpty())) return@combineLiveDatas false
if(useProxy && (proxyAddress.isEmpty() || proxyPort.isEmpty())) return@combineLatestIgnoreNull false if(useProxy && (proxyAddress.isEmpty() || proxyPort.isEmpty())) return@combineLiveDatas false
if(useBundledTor && (proxyAddress.isEmpty() || proxyPort.isEmpty())) return@combineLatestIgnoreNull false if(useBundledTor && (proxyAddress.isEmpty() || proxyPort.isEmpty())) return@combineLiveDatas false
return@combineLatestIgnoreNull true return@combineLiveDatas true
} }
fun onMoreOptionsClicked() { fun onMoreOptionsClicked() {
@ -72,7 +79,6 @@ class OnboardingViewModel : ViewModel() {
) { ) {
val passphrase = _passphrase.value ?: return val passphrase = _passphrase.value ?: return
val confirmedPassphrase = _confirmedPassphrase.value ?: return val confirmedPassphrase = _confirmedPassphrase.value ?: return
val useProxy = _useProxy.value ?: return
val application = mainActivity.application as MoneroApplication val application = mainActivity.application as MoneroApplication
_creatingWallet.postValue(true) _creatingWallet.postValue(true)
@ -172,15 +178,6 @@ class OnboardingViewModel : ViewModel() {
val ok = walletStatus?.isOk val ok = walletStatus?.isOk
walletFile.delete() // cache is broken for some reason when recovering wallets. delete the file here. this happens in monerujo too. walletFile.delete() // cache is broken for some reason when recovering wallets. delete the file here. this happens in monerujo too.
if (ok == true) { 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 as MainActivity).init(walletFile, passphrase)
mainActivity.runOnUiThread { mainActivity.onBackPressed() } mainActivity.runOnUiThread { mainActivity.onBackPressed() }
} else { } else {
@ -228,18 +225,42 @@ class OnboardingViewModel : ViewModel() {
fun setProxyAddress(address: String) { fun setProxyAddress(address: String) {
_proxyAddress.value = address _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) { fun setProxyPort(port: String) {
_proxyPort.value = port _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) { fun setUseBundledTor(useBundled: Boolean) {
_useBundledTor.value = useBundled _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) { fun setUseProxy(useProxy: Boolean) {
_useProxy.value = useProxy _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) { fun setPassphrase(passphrase: String) {
@ -250,6 +271,11 @@ class OnboardingViewModel : ViewModel() {
_confirmedPassphrase.value = confirmedPassphrase _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) { enum class SeedType(val descResId: Int) {
LEGACY(R.string.seed_desc_legacy), POLYSEED(R.string.seed_desc_polyseed), UNKNOWN(0) LEGACY(R.string.seed_desc_legacy), POLYSEED(R.string.seed_desc_polyseed), UNKNOWN(0)

View file

@ -9,6 +9,8 @@ import android.widget.Button
import android.widget.CheckBox import android.widget.CheckBox
import android.widget.CompoundButton import android.widget.CompoundButton
import android.widget.EditText import android.widget.EditText
import android.widget.ImageView
import android.widget.Switch
import android.widget.Toast import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.appcompat.widget.SwitchCompat import androidx.appcompat.widget.SwitchCompat
@ -17,6 +19,7 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.material.progressindicator.CircularProgressIndicator
import net.mynero.wallet.R import net.mynero.wallet.R
import net.mynero.wallet.data.Node import net.mynero.wallet.data.Node
import net.mynero.wallet.data.Node.Companion.fromJson 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
import net.mynero.wallet.fragment.dialog.PasswordBottomSheetDialog.PasswordListener import net.mynero.wallet.fragment.dialog.PasswordBottomSheetDialog.PasswordListener
import net.mynero.wallet.fragment.dialog.WalletKeysBottomSheetDialog import net.mynero.wallet.fragment.dialog.WalletKeysBottomSheetDialog
import net.mynero.wallet.model.EnumTorState
import net.mynero.wallet.service.BalanceService import net.mynero.wallet.service.BalanceService
import net.mynero.wallet.service.HistoryService import net.mynero.wallet.service.HistoryService
import net.mynero.wallet.service.PrefService import net.mynero.wallet.service.PrefService
@ -44,6 +48,14 @@ class SettingsFragment : Fragment(), PasswordListener, NodeSelectionDialogListen
private var selectNodeButton: Button? = null private var selectNodeButton: Button? = null
private var cachedProxyAddress: String = "" private var cachedProxyAddress: String = ""
private var cachedProxyPort: 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( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
@ -55,68 +67,76 @@ class SettingsFragment : Fragment(), PasswordListener, NodeSelectionDialogListen
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
mViewModel = ViewModelProvider(this)[SettingsViewModel::class.java] mViewModel = ViewModelProvider(this)[SettingsViewModel::class.java]
val displaySeedButton = view.findViewById<Button>(R.id.display_seed_button) displaySeedButton = view.findViewById(R.id.display_seed_button)
val displayUtxosButton = view.findViewById<Button>(R.id.display_utxos_button) displayUtxosButton = view.findViewById(R.id.display_utxos_button)
selectNodeButton = view.findViewById(R.id.select_node_button) selectNodeButton = view.findViewById(R.id.select_node_button)
val streetModeSwitch = view.findViewById<SwitchCompat>(R.id.street_mode_switch) streetModeSwitch = view.findViewById(R.id.street_mode_switch)
val monerochanSwitch = view.findViewById<SwitchCompat>(R.id.monerochan_switch) monerochanSwitch = view.findViewById(R.id.monerochan_switch)
val donationSwitch = view.findViewById<SwitchCompat>(R.id.donate_per_tx_switch) donationSwitch = view.findViewById(R.id.donate_per_tx_switch)
val torSwitch = view.findViewById<SwitchCompat>(R.id.tor_switch) torSwitch = view.findViewById(R.id.tor_switch)
val proxySettingsLayout = proxySettingsLayout = view.findViewById(R.id.wallet_proxy_settings_layout)
view.findViewById<ConstraintLayout>(R.id.wallet_proxy_settings_layout)
walletProxyAddressEditText = view.findViewById(R.id.wallet_proxy_address_edittext) walletProxyAddressEditText = view.findViewById(R.id.wallet_proxy_address_edittext)
walletProxyPortEditText = view.findViewById(R.id.wallet_proxy_port_edittext) walletProxyPortEditText = view.findViewById(R.id.wallet_proxy_port_edittext)
val useBundledTor = view.findViewById<CheckBox>(R.id.bundled_tor_checkbox) useBundledTor = view.findViewById(R.id.bundled_tor_checkbox)
useBundledTor.isChecked = ProxyService.instance?.useBundledTor == true useBundledTor?.isChecked = ProxyService.instance?.useBundledTor == true
walletProxyPortEditText?.visibility = if (useBundledTor.isChecked) View.GONE else View.VISIBLE walletProxyPortEditText?.visibility = if (useBundledTor?.isChecked == true) View.GONE else View.VISIBLE
walletProxyAddressEditText?.visibility = if (useBundledTor.isChecked) View.GONE else View.VISIBLE walletProxyAddressEditText?.visibility = if (useBundledTor?.isChecked == true) View.GONE else View.VISIBLE
streetModeSwitch?.isChecked = PrefService.instance?.getBoolean(Constants.PREF_STREET_MODE, false) == true
monerochanSwitch?.isChecked = PrefService.instance?.getBoolean(Constants.PREF_MONEROCHAN, true) == true
donationSwitch?.isChecked = PrefService.instance?.getBoolean(Constants.PREF_DONATE_PER_TX, false) == true
streetModeSwitch.isChecked =
PrefService.instance?.getBoolean(Constants.PREF_STREET_MODE, false) == true
streetModeSwitch.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean ->
PrefService.instance?.edit()?.putBoolean(Constants.PREF_STREET_MODE, b)?.apply()
BalanceService.instance?.refreshBalance()
}
monerochanSwitch.isChecked =
PrefService.instance?.getBoolean(Constants.PREF_MONEROCHAN, true) == true
monerochanSwitch.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean ->
PrefService.instance?.edit()?.putBoolean(Constants.PREF_MONEROCHAN, b)?.apply()
HistoryService.instance?.refreshHistory()
}
donationSwitch.isChecked =
PrefService.instance?.getBoolean(Constants.PREF_DONATE_PER_TX, false) == true
donationSwitch.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean ->
PrefService.instance?.edit()?.putBoolean(Constants.PREF_DONATE_PER_TX, b)?.apply()
}
val prefService = PrefService.instance ?: return
val usesProxy = ProxyService.instance?.usingProxy == true val usesProxy = ProxyService.instance?.usingProxy == true
cachedProxyAddress = ProxyService.instance?.proxyAddress ?: return cachedProxyAddress = ProxyService.instance?.proxyAddress ?: return
cachedProxyPort = ProxyService.instance?.proxyPort ?: return cachedProxyPort = ProxyService.instance?.proxyPort ?: return
if (ProxyService.instance?.hasProxySet() == true) { if (ProxyService.instance?.hasProxySet() == true) {
initProxyStuff(cachedProxyAddress, cachedProxyPort) initProxyStuff(cachedProxyAddress, cachedProxyPort)
} }
torSwitch.isChecked = usesProxy torSwitch?.isChecked = usesProxy
if (usesProxy) { proxySettingsLayout?.visibility = if (usesProxy) View.VISIBLE else View.GONE
proxySettingsLayout.visibility = View.VISIBLE val node = PrefService.instance?.node // shouldn't use default value here
} else { selectNodeButton?.text = getString(R.string.node_button_text, node?.address)
proxySettingsLayout.visibility = View.GONE
} bindListeners()
torSwitch.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean -> bindObservers()
prefService.edit()?.putBoolean(Constants.PREF_USES_PROXY, b)?.apply() }
if (b) {
if (ProxyService.instance?.hasProxySet() == true) { private fun bindListeners() {
val proxyAddress = ProxyService.instance?.proxyAddress ?: return@setOnCheckedChangeListener val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
val proxyPort = ProxyService.instance?.proxyPort ?: return@setOnCheckedChangeListener override fun handleOnBackPressed() {
initProxyStuff(proxyAddress, proxyPort) refreshProxy()
} findNavController().popBackStack()
proxySettingsLayout.visibility = View.VISIBLE
} else {
proxySettingsLayout.visibility = View.GONE
} }
refreshProxy()
} }
displaySeedButton.setOnClickListener { activity?.onBackPressedDispatcher?.addCallback(viewLifecycleOwner, onBackPressedCallback)
donationSwitch?.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean ->
PrefService.instance?.edit()?.putBoolean(Constants.PREF_DONATE_PER_TX, b)?.apply()
}
streetModeSwitch?.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean ->
PrefService.instance?.edit()?.putBoolean(Constants.PREF_STREET_MODE, b)?.apply()
BalanceService.instance?.refreshBalance()
}
monerochanSwitch?.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean ->
PrefService.instance?.edit()?.putBoolean(Constants.PREF_MONEROCHAN, b)?.apply()
HistoryService.instance?.refreshHistory()
}
selectNodeButton?.setOnClickListener {
activity?.supportFragmentManager?.let { fragmentManager ->
val dialog = NodeSelectionBottomSheetDialog()
dialog.listener = this
dialog.show(fragmentManager, "node_selection_dialog")
}
}
useBundledTor?.setOnCheckedChangeListener { _, isChecked ->
mViewModel?.setUseBundledTor(isChecked)
}
displaySeedButton?.setOnClickListener {
val usesPassword = val usesPassword =
PrefService.instance?.getBoolean(Constants.PREF_USES_PASSWORD, false) == true PrefService.instance?.getBoolean(Constants.PREF_USES_PASSWORD, false) == true
if (usesPassword) { if (usesPassword) {
@ -130,37 +150,66 @@ class SettingsFragment : Fragment(), PasswordListener, NodeSelectionDialogListen
displaySeedDialog("") displaySeedDialog("")
} }
} }
displayUtxosButton.setOnClickListener { navigate(R.id.nav_to_utxos) }
val node = PrefService.instance?.node // shouldn't use default value here displayUtxosButton?.setOnClickListener { navigate(R.id.nav_to_utxos) }
selectNodeButton?.text = getString(R.string.node_button_text, node?.address)
selectNodeButton?.setOnClickListener { torSwitch?.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean ->
activity?.supportFragmentManager?.let { fragmentManager -> mViewModel?.setUseProxy(b)
val dialog = NodeSelectionBottomSheetDialog() }
dialog.listener = this }
dialog.show(fragmentManager, "node_selection_dialog")
private fun bindObservers() {
mViewModel?.useProxy?.observe(viewLifecycleOwner) { b ->
if (b) {
if (ProxyService.instance?.hasProxySet() == true) {
val proxyAddress = ProxyService.instance?.proxyAddress ?: return@observe
val proxyPort = ProxyService.instance?.proxyPort ?: return@observe
initProxyStuff(proxyAddress, proxyPort)
}
proxySettingsLayout?.visibility = View.VISIBLE
} else {
proxySettingsLayout?.visibility = View.GONE
} }
refreshProxy()
} }
useBundledTor?.setOnCheckedChangeListener { _, isChecked -> mViewModel?.useBundledTor?.observe(viewLifecycleOwner) { isChecked ->
walletProxyPortEditText?.visibility = if (isChecked) View.GONE else View.VISIBLE walletProxyPortEditText?.visibility = if (isChecked) View.GONE else View.VISIBLE
walletProxyAddressEditText?.visibility = if (isChecked) View.GONE else View.VISIBLE walletProxyAddressEditText?.visibility = if (isChecked) View.GONE else View.VISIBLE
PrefService.instance?.edit()?.putBoolean(Constants.PREF_USE_BUNDLED_TOR, isChecked)?.apply()
if(isChecked) {
ProxyService.instance?.samouraiTorManager?.start()
} else {
ProxyService.instance?.samouraiTorManager?.stop()
}
} }
val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) { val samouraiTorManager = ProxyService.instance?.samouraiTorManager
override fun handleOnBackPressed() { val indicatorCircle = view?.findViewById<CircularProgressIndicator>(R.id.settings_tor_loading_progressindicator)
refreshProxy() val torIcon = view?.findViewById<ImageView>(R.id.settings_tor_icon)
findNavController().popBackStack()
samouraiTorManager?.getTorStateLiveData()?.observe(viewLifecycleOwner) { state ->
samouraiTorManager.getProxy()?.address()?.let { socketAddress ->
if(socketAddress.toString().isEmpty()) return@let
if(mViewModel?.useProxy?.value == true && mViewModel?.useBundledTor?.value == true) {
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 -> {}
} }
} }
activity?.onBackPressedDispatcher?.addCallback(viewLifecycleOwner, onBackPressedCallback)
} }
private fun refreshProxy() { private fun refreshProxy() {

View file

@ -1,12 +1,47 @@
package net.mynero.wallet.fragment.settings package net.mynero.wallet.fragment.settings
import android.util.Patterns import android.util.Patterns
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import net.mynero.wallet.MoneroApplication import net.mynero.wallet.MoneroApplication
import net.mynero.wallet.model.WalletManager import net.mynero.wallet.model.WalletManager
import net.mynero.wallet.service.PrefService import net.mynero.wallet.service.PrefService
import net.mynero.wallet.service.ProxyService
import net.mynero.wallet.util.Constants import net.mynero.wallet.util.Constants
class SettingsViewModel : ViewModel() { class SettingsViewModel : ViewModel() {
private val _useProxy = MutableLiveData(false)
val useProxy: LiveData<Boolean> = _useProxy
private val _useBundledTor = MutableLiveData(false)
val useBundledTor: LiveData<Boolean> = _useBundledTor
init {
_useProxy.value = ProxyService.instance?.usingProxy
_useBundledTor.value = ProxyService.instance?.useBundledTor
}
fun setUseProxy(use: Boolean) {
_useProxy.value = use
PrefService.instance?.edit()?.putBoolean(Constants.PREF_USES_PROXY, use)?.apply()
val samouraiTorManager = ProxyService.instance?.samouraiTorManager
if(use && useBundledTor.value == true) {
samouraiTorManager?.start()
} else {
samouraiTorManager?.stop()
}
}
fun setUseBundledTor(use: Boolean) {
PrefService.instance?.edit()?.putBoolean(Constants.PREF_USE_BUNDLED_TOR, use)?.apply()
_useBundledTor.value = use
val samouraiTorManager = ProxyService.instance?.samouraiTorManager
if(use && useProxy.value == true) {
samouraiTorManager?.start()
} else {
samouraiTorManager?.stop()
}
}
} }

View file

@ -0,0 +1,123 @@
package net.mynero.wallet.livedata
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
fun <T1, T2, T3, T4, T5, T6, T7, T8, S> combineLiveDatas(
source1: LiveData<T1>,
source2: LiveData<T2>,
source3: LiveData<T3>,
source4: LiveData<T4>,
source5: LiveData<T5>,
source6: LiveData<T6>,
source7: LiveData<T7>,
source8: LiveData<T8>,
func: (T1?, T2?, T3?, T4?, T5?, T6?, T7?, T8?) -> S?
): LiveData<S> {
val result = MediatorLiveData<S>()
result.addSource(source1) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
result.addSource(source2) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
result.addSource(source3) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
result.addSource(source4) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
result.addSource(source5) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
result.addSource(source6) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
result.addSource(source7) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
result.addSource(source8) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
return result
}

View file

@ -1,414 +0,0 @@
package net.mynero.wallet.livedata
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
fun <T1, T2, S> combineLatest(
source1: LiveData<T1>,
source2: LiveData<T2>,
func: (T1?, T2?) -> S?
): LiveData<S> {
val result = MediatorLiveData<S>()
result.addSource(source1) {
result.value = func.invoke(source1.value, source2.value)
}
result.addSource(source2) {
result.value = func.invoke(source1.value, source2.value)
}
return result
}
fun <T1, T2, T3, S> combineLatest(
source1: LiveData<T1>,
source2: LiveData<T2>,
source3: LiveData<T3>,
func: (T1?, T2?, T3?) -> S?
): LiveData<S> {
val result = MediatorLiveData<S>()
result.addSource(source1) {
result.value = func.invoke(source1.value, source2.value, source3.value)
}
result.addSource(source2) {
result.value = func.invoke(source1.value, source2.value, source3.value)
}
result.addSource(source3) {
result.value = func.invoke(source1.value, source2.value, source3.value)
}
return result
}
fun <T1, T2, T3, T4, S> combineLatest(
source1: LiveData<T1>,
source2: LiveData<T2>,
source3: LiveData<T3>,
source4: LiveData<T4>,
func: (T1?, T2?, T3?, T4?) -> S?
): LiveData<S> {
val result = MediatorLiveData<S>()
result.addSource(source1) {
result.value = func.invoke(source1.value, source2.value, source3.value, source4.value)
}
result.addSource(source2) {
result.value = func.invoke(source1.value, source2.value, source3.value, source4.value)
}
result.addSource(source3) {
result.value = func.invoke(source1.value, source2.value, source3.value, source4.value)
}
result.addSource(source4) {
result.value = func.invoke(source1.value, source2.value, source3.value, source4.value)
}
return result
}
fun <T1, T2, T3, T4, T5, S> combineLatest(
source1: LiveData<T1>,
source2: LiveData<T2>,
source3: LiveData<T3>,
source4: LiveData<T4>,
source5: LiveData<T5>,
func: (T1?, T2?, T3?, T4?, T5?) -> S?
): LiveData<S> {
val result = MediatorLiveData<S>()
result.addSource(source1) {
result.value = func.invoke(source1.value, source2.value, source3.value, source4.value, source5.value)
}
result.addSource(source2) {
result.value = func.invoke(source1.value, source2.value, source3.value, source4.value, source5.value)
}
result.addSource(source3) {
result.value = func.invoke(source1.value, source2.value, source3.value, source4.value, source5.value)
}
result.addSource(source4) {
result.value = func.invoke(source1.value, source2.value, source3.value, source4.value, source5.value)
}
result.addSource(source5) {
result.value = func.invoke(source1.value, source2.value, source3.value, source4.value, source5.value)
}
return result
}
fun <T1, T2, S> combineLatestIgnoreNull(
source1: LiveData<T1>,
source2: LiveData<T2>,
func: (T1?, T2?) -> S?
): LiveData<S> {
val result = MediatorLiveData<S>()
result.addSource(source1) {
func.invoke(source1.value, source2.value)?.run { result.value = this }
}
result.addSource(source2) {
func.invoke(source1.value, source2.value)?.run { result.value = this }
}
return result
}
fun <T1, T2, T3, S> combineLatestIgnoreNull(
source1: LiveData<T1>,
source2: LiveData<T2>,
source3: LiveData<T3>,
func: (T1?, T2?, T3?) -> S?
): LiveData<S> {
val result = MediatorLiveData<S>()
result.addSource(source1) {
func(source1.value, source2.value, source3.value)?.run { result.value = this }
}
result.addSource(source2) {
func(source1.value, source2.value, source3.value)?.run { result.value = this }
}
result.addSource(source3) {
func(source1.value, source2.value, source3.value)?.run { result.value = this }
}
return result
}
fun <T1, T2, T3, T4, S> combineLatestIgnoreNull(
source1: LiveData<T1>,
source2: LiveData<T2>,
source3: LiveData<T3>,
source4: LiveData<T4>,
func: (T1?, T2?, T3?, T4?) -> S?
): LiveData<S> {
val result = MediatorLiveData<S>()
result.addSource(source1) {
func(source1.value, source2.value, source3.value, source4.value)?.run { result.value = this }
}
result.addSource(source2) {
func(source1.value, source2.value, source3.value, source4.value)?.run { result.value = this }
}
result.addSource(source3) {
func(source1.value, source2.value, source3.value, source4.value)?.run { result.value = this }
}
result.addSource(source4) {
func(source1.value, source2.value, source3.value, source4.value)?.run { result.value = this }
}
return result
}
fun <T1, T2, T3, T4, T5, S> combineLatestIgnoreNull(
source1: LiveData<T1>,
source2: LiveData<T2>,
source3: LiveData<T3>,
source4: LiveData<T4>,
source5: LiveData<T5>,
func: (T1?, T2?, T3?, T4?, T5?) -> S?
): LiveData<S> {
val result = MediatorLiveData<S>()
result.addSource(source1) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value
)?.run { result.value = this }
}
result.addSource(source2) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value
)?.run { result.value = this }
}
result.addSource(source3) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value
)?.run { result.value = this }
}
result.addSource(source4) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value
)?.run { result.value = this }
}
result.addSource(source5) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value
)?.run { result.value = this }
}
return result
}
fun <T1, T2, T3, T4, T5, T6, S> combineLatestIgnoreNull(
source1: LiveData<T1>,
source2: LiveData<T2>,
source3: LiveData<T3>,
source4: LiveData<T4>,
source5: LiveData<T5>,
source6: LiveData<T6>,
func: (T1?, T2?, T3?, T4?, T5?, T6?) -> S?
): LiveData<S> {
val result = MediatorLiveData<S>()
result.addSource(source1) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value
)?.run { result.value = this }
}
result.addSource(source2) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value
)?.run { result.value = this }
}
result.addSource(source3) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value
)?.run { result.value = this }
}
result.addSource(source4) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value
)?.run { result.value = this }
}
result.addSource(source5) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value
)?.run { result.value = this }
}
result.addSource(source6) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value
)?.run { result.value = this }
}
return result
}
fun <T1, T2, T3, T4, T5, T6, T7, T8, S> combineLatestIgnoreNull(
source1: LiveData<T1>,
source2: LiveData<T2>,
source3: LiveData<T3>,
source4: LiveData<T4>,
source5: LiveData<T5>,
source6: LiveData<T6>,
source7: LiveData<T7>,
source8: LiveData<T8>,
func: (T1?, T2?, T3?, T4?, T5?, T6?, T7?, T8?) -> S?
): LiveData<S> {
val result = MediatorLiveData<S>()
result.addSource(source1) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
result.addSource(source2) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
result.addSource(source3) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
result.addSource(source4) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
result.addSource(source5) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
result.addSource(source6) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
result.addSource(source7) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
result.addSource(source8) {
func(
source1.value,
source2.value,
source3.value,
source4.value,
source5.value,
source6.value,
source7.value,
source8.value
)?.run { result.value = this }
}
return result
}

View file

@ -17,10 +17,8 @@ class ProxyService(activity: MainActivity) : ServiceBase(null) {
activity.runOnUiThread { activity.runOnUiThread {
samouraiTorManager?.getTorStateLiveData()?.observeForever { samouraiTorManager?.getTorStateLiveData()?.observeForever {
println("STATE CHANGE:: ${it.state.name}")
samouraiTorManager?.getProxy()?.address()?.let { socketAddress -> samouraiTorManager?.getProxy()?.address()?.let { socketAddress ->
if(socketAddress.toString().isEmpty()) return@let if(socketAddress.toString().isEmpty()) return@let
println("PROXY INIT")
val proxyString = socketAddress.toString().substring(1) val proxyString = socketAddress.toString().substring(1)
val address = proxyString.split(":")[0] val address = proxyString.split(":")[0]
val port = proxyString.split(":")[1] val port = proxyString.split(":")[1]

View file

@ -23,17 +23,38 @@
android:paddingTop="24dp"> android:paddingTop="24dp">
<TextView <TextView
android:id="@+id/create_wallet_textview" android:id="@+id/create_wallet_textview"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:text="@string/create_wallet" android:text="@string/create_wallet"
android:textSize="32sp" android:textSize="32sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/wallet_password_edittext" app:layout_constraintBottom_toTopOf="@id/wallet_password_edittext"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toStartOf="@id/onboarding_tor_loading_progressindicator"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/onboarding_tor_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:src="@drawable/tor"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="@id/onboarding_tor_loading_progressindicator"
app:layout_constraintStart_toStartOf="@id/onboarding_tor_loading_progressindicator"
app:layout_constraintTop_toTopOf="@id/onboarding_tor_loading_progressindicator"
app:layout_constraintBottom_toBottomOf="@id/onboarding_tor_loading_progressindicator"/>
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/onboarding_tor_loading_progressindicator"
android:layout_width="32dp"
android:layout_height="32dp"
android:indeterminate="true"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/create_wallet_textview"
app:layout_constraintBottom_toBottomOf="@id/create_wallet_textview"/>
<EditText <EditText
android:id="@+id/wallet_password_edittext" android:id="@+id/wallet_password_edittext"
android:layout_width="0dp" android:layout_width="0dp"
@ -248,7 +269,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minHeight="48dp" android:minHeight="48dp"
android:text="@string/use_bundled_tor" android:text="@string/use_bundled_tor"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/wallet_proxy_address_edittext" app:layout_constraintBottom_toTopOf="@id/wallet_proxy_address_edittext"
@ -260,7 +280,6 @@
android:background="@drawable/edittext_bg" android:background="@drawable/edittext_bg"
android:hint="@string/wallet_proxy_address_hint" android:hint="@string/wallet_proxy_address_hint"
android:minHeight="48dp" android:minHeight="48dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/wallet_proxy_port_edittext" app:layout_constraintBottom_toTopOf="@id/wallet_proxy_port_edittext"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -275,7 +294,6 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:inputType="number" android:inputType="number"
android:minHeight="48dp" android:minHeight="48dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View file

@ -19,9 +19,29 @@
android:layout_marginEnd="24dp" android:layout_marginEnd="24dp"
android:textSize="32sp" android:textSize="32sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toStartOf="@id/settings_tor_loading_progressindicator"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/settings_tor_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:src="@drawable/tor"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="@id/settings_tor_loading_progressindicator"
app:layout_constraintStart_toStartOf="@id/settings_tor_loading_progressindicator"
app:layout_constraintTop_toTopOf="@id/settings_tor_loading_progressindicator"
app:layout_constraintBottom_toBottomOf="@id/settings_tor_loading_progressindicator"/>
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/settings_tor_loading_progressindicator"
android:layout_width="32dp"
android:layout_height="32dp"
android:indeterminate="true"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/settings_textview"
app:layout_constraintBottom_toBottomOf="@id/settings_textview"/>
<TextView <TextView
android:id="@+id/wallet_settings_textview" android:id="@+id/wallet_settings_textview"