Convert remaining screens to Kotlin

This commit is contained in:
pokkst 2023-12-06 17:48:45 -06:00
parent 2f400d7e8d
commit a1c43db4db
No known key found for this signature in database
GPG key ID: EC4FAAA66859FAA4
15 changed files with 965 additions and 988 deletions

View file

@ -40,8 +40,8 @@ class MainActivity : AppCompatActivity(), MoneroHandlerThread.Listener, Password
val walletKeysFile = File(applicationInfo.dataDir, Constants.WALLET_NAME + ".keys")
if (walletKeysFile.exists()) {
val promptPassword =
PrefService.instance?.getBoolean(Constants.PREF_USES_PASSWORD, false)
if (promptPassword == false) {
PrefService.instance?.getBoolean(Constants.PREF_USES_PASSWORD, false) == true
if (!promptPassword) {
init(walletFile, "")
} else {
val passwordDialog = PasswordBottomSheetDialog()

View file

@ -96,7 +96,7 @@ class CoinsInfoAdapter(val listener: CoinsInfoAdapterListener?) :
}
interface CoinsInfoAdapterListener {
fun onUtxoSelected(coinsInfo: CoinsInfo?)
fun onUtxoSelected(coinsInfo: CoinsInfo)
}
/**
@ -127,9 +127,9 @@ class CoinsInfoAdapter(val listener: CoinsInfoAdapterListener?) :
val globalIdxTextView = itemView.findViewById<TextView>(R.id.utxo_global_index_textview)
val outpointTextView = itemView.findViewById<TextView>(R.id.utxo_outpoint_textview)
val streetModeEnabled =
PrefService.instance?.getBoolean(Constants.PREF_STREET_MODE, false)
PrefService.instance?.getBoolean(Constants.PREF_STREET_MODE, false) == true
val balanceString =
if (streetModeEnabled == true) Constants.STREET_MODE_BALANCE else Wallet.getDisplayAmount(
if (streetModeEnabled) Constants.STREET_MODE_BALANCE else Wallet.getDisplayAmount(
coinsInfo.amount
)
amountTextView.text =
@ -167,7 +167,7 @@ class CoinsInfoAdapter(val listener: CoinsInfoAdapterListener?) :
if (!editing) return
val unlocked = coinsInfo?.isUnlocked == true
if (unlocked) {
listener?.onUtxoSelected(coinsInfo)
coinsInfo?.let { listener?.onUtxoSelected(it) }
}
}
@ -175,7 +175,7 @@ class CoinsInfoAdapter(val listener: CoinsInfoAdapterListener?) :
if (editing) return false
val unlocked = coinsInfo?.isUnlocked == true
if (unlocked) {
listener?.onUtxoSelected(coinsInfo)
coinsInfo?.let { listener?.onUtxoSelected(it) }
}
return unlocked
}

View file

@ -91,8 +91,8 @@ class SubaddressAdapter(val listener: SubaddressAdapterListener?) :
val amount = subaddress.amount
if (amount > 0) {
val streetMode =
PrefService.instance?.getBoolean(Constants.PREF_STREET_MODE, false)
if (streetMode == true) {
PrefService.instance?.getBoolean(Constants.PREF_STREET_MODE, false) == true
if (streetMode) {
addressAmountTextView.text = itemView.context.getString(
R.string.tx_list_amount_positive,
Constants.STREET_MODE_BALANCE

View file

@ -94,9 +94,9 @@ class TransactionInfoAdapter(val listener: TxInfoAdapterListener?) :
fun bind(txInfo: TransactionInfo) {
val streetModeEnabled =
PrefService.instance?.getBoolean(Constants.PREF_STREET_MODE, false)
PrefService.instance?.getBoolean(Constants.PREF_STREET_MODE, false) == true
val displayAmount =
if (streetModeEnabled == true) Constants.STREET_MODE_BALANCE else Helper.getDisplayAmount(
if (streetModeEnabled) Constants.STREET_MODE_BALANCE else Helper.getDisplayAmount(
txInfo.amount,
Helper.DISPLAY_DIGITS_INFO
)

View file

@ -30,17 +30,17 @@ class WalletKeysBottomSheetDialog : BottomSheetDialogFragment() {
val informationTextView = view.findViewById<TextView>(R.id.information_textview) // seed
val viewKeyTextView = view.findViewById<TextView>(R.id.viewkey_textview)
val restoreHeightTextView = view.findViewById<TextView>(R.id.restore_height_textview)
val wallet = WalletManager.instance!!.wallet
var seed = wallet!!.getSeed("")
val usesOffset = PrefService.instance!!.getBoolean(Constants.PREF_USES_OFFSET, false)
val wallet = WalletManager.instance?.wallet
var seed = wallet?.getSeed("")
val usesOffset = PrefService.instance?.getBoolean(Constants.PREF_USES_OFFSET, false) == true
if (usesOffset) {
seed = wallet.getSeed(password)
seed = wallet?.getSeed(password)
view.findViewById<View>(R.id.wallet_seed_offset_textview).visibility = View.VISIBLE
}
val privateViewKey = wallet.getSecretViewKey()
val privateViewKey = wallet?.getSecretViewKey()
informationTextView.text = seed
viewKeyTextView.text = privateViewKey
restoreHeightTextView.text = "${wallet.getRestoreHeight()}"
restoreHeightTextView.text = "${wallet?.getRestoreHeight()}"
copyViewKeyImageButton.setOnClickListener {
clipBoardCopy(
context, "private view-key", privateViewKey

View file

@ -165,8 +165,9 @@ class HomeFragment : Fragment(), TxInfoAdapterListener {
val botImageView = view.findViewById<ImageView>(R.id.monerochan_imageview)
view.findViewById<View>(R.id.no_history_layout).visibility =
if (display) View.VISIBLE else View.GONE
val displayMonerochan = PrefService.instance?.getBoolean(Constants.PREF_MONEROCHAN, true)
if (displayMonerochan == true) {
val displayMonerochan =
PrefService.instance?.getBoolean(Constants.PREF_MONEROCHAN, true) == true
if (displayMonerochan) {
botImageView.visibility = View.VISIBLE
mnrjTextView.visibility = View.VISIBLE
textView.visibility = View.GONE

View file

@ -1,326 +1,297 @@
package net.mynero.wallet.fragment.onboarding;
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;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
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
import android.widget.Button
import android.widget.CheckBox
import android.widget.CompoundButton
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.widget.SwitchCompat
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import net.mynero.wallet.MoneroApplication
import net.mynero.wallet.R
import net.mynero.wallet.data.Node
import net.mynero.wallet.fragment.dialog.AddNodeBottomSheetDialog
import net.mynero.wallet.fragment.dialog.AddNodeBottomSheetDialog.AddNodeListener
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.service.PrefService
import net.mynero.wallet.util.Constants
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.SwitchCompat;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModelProvider;
import net.mynero.wallet.MoneroApplication;
import net.mynero.wallet.R;
import net.mynero.wallet.data.Node;
import net.mynero.wallet.fragment.dialog.AddNodeBottomSheetDialog;
import net.mynero.wallet.fragment.dialog.NodeSelectionBottomSheetDialog;
import net.mynero.wallet.service.PrefService;
import net.mynero.wallet.util.Constants;
public class OnboardingFragment extends Fragment implements NodeSelectionBottomSheetDialog.NodeSelectionDialogListener, AddNodeBottomSheetDialog.AddNodeListener {
private boolean useOffset = true;
private OnboardingViewModel mViewModel;
TextWatcher proxyAddressListener = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
class OnboardingFragment : Fragment(), NodeSelectionDialogListener, AddNodeListener {
private var useOffset = true
private var mViewModel: OnboardingViewModel? = null
private var proxyAddressListener: TextWatcher = 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) {
if (mViewModel != null) {
mViewModel.setProxyAddress(editable.toString());
mViewModel.updateProxy(((MoneroApplication) getActivity().getApplication()));
mViewModel?.setProxyAddress(editable.toString())
mViewModel?.updateProxy(activity?.application as MoneroApplication)
}
}
};
TextWatcher proxyPortListener = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
private var proxyPortListener: TextWatcher = 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) {
if (mViewModel != null) {
mViewModel.setProxyPort(editable.toString());
mViewModel.updateProxy(((MoneroApplication) getActivity().getApplication()));
mViewModel?.setProxyPort(editable.toString())
mViewModel?.updateProxy(activity?.application as MoneroApplication)
}
}
};
private EditText walletProxyAddressEditText;
private EditText walletProxyPortEditText;
private EditText walletPasswordEditText;
private EditText walletPasswordConfirmEditText;
private EditText walletSeedEditText;
private EditText walletRestoreHeightEditText;
private Button createWalletButton;
private TextView moreOptionsDropdownTextView;
private SwitchCompat torSwitch;
private ConstraintLayout advancedOptionsLayout;
private ImageView moreOptionsChevronImageView;
private CheckBox seedOffsetCheckbox;
private Button selectNodeButton;
private SwitchCompat showXmrchanSwitch;
private ImageView xmrchanOnboardingImage;
private Button seedTypeButton;
private TextView seedTypeDescTextView;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_onboarding, container, false);
}
private var walletProxyAddressEditText: EditText? = null
private var walletProxyPortEditText: EditText? = null
private var walletPasswordEditText: EditText? = null
private var walletPasswordConfirmEditText: EditText? = null
private var walletSeedEditText: EditText? = null
private var walletRestoreHeightEditText: EditText? = null
private var createWalletButton: Button? = null
private var moreOptionsDropdownTextView: TextView? = null
private var torSwitch: SwitchCompat? = null
private var advancedOptionsLayout: ConstraintLayout? = null
private var moreOptionsChevronImageView: ImageView? = null
private var seedOffsetCheckbox: CheckBox? = null
private var selectNodeButton: Button? = null
private var showXmrchanSwitch: SwitchCompat? = null
private var xmrchanOnboardingImage: ImageView? = null
private var seedTypeButton: Button? = null
private var seedTypeDescTextView: TextView? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_onboarding, container, false)
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewModel = new ViewModelProvider(this).get(OnboardingViewModel.class);
selectNodeButton = view.findViewById(R.id.select_node_button);
walletPasswordEditText = view.findViewById(R.id.wallet_password_edittext);
walletPasswordConfirmEditText = view.findViewById(R.id.wallet_password_confirm_edittext);
walletSeedEditText = view.findViewById(R.id.wallet_seed_edittext);
walletRestoreHeightEditText = view.findViewById(R.id.wallet_restore_height_edittext);
createWalletButton = view.findViewById(R.id.create_wallet_button);
moreOptionsDropdownTextView = view.findViewById(R.id.advanced_settings_dropdown_textview);
moreOptionsChevronImageView = view.findViewById(R.id.advanced_settings_chevron_imageview);
torSwitch = view.findViewById(R.id.tor_onboarding_switch);
seedOffsetCheckbox = view.findViewById(R.id.seed_offset_checkbox);
walletProxyAddressEditText = view.findViewById(R.id.wallet_proxy_address_edittext);
walletProxyPortEditText = view.findViewById(R.id.wallet_proxy_port_edittext);
advancedOptionsLayout = view.findViewById(R.id.more_options_layout);
showXmrchanSwitch = view.findViewById(R.id.show_xmrchan_switch);
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);
bindListeners();
bindObservers();
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mViewModel = ViewModelProvider(this)[OnboardingViewModel::class.java]
selectNodeButton = view.findViewById(R.id.select_node_button)
walletPasswordEditText = view.findViewById(R.id.wallet_password_edittext)
walletPasswordConfirmEditText = view.findViewById(R.id.wallet_password_confirm_edittext)
walletSeedEditText = view.findViewById(R.id.wallet_seed_edittext)
walletRestoreHeightEditText = view.findViewById(R.id.wallet_restore_height_edittext)
createWalletButton = view.findViewById(R.id.create_wallet_button)
moreOptionsDropdownTextView = view.findViewById(R.id.advanced_settings_dropdown_textview)
moreOptionsChevronImageView = view.findViewById(R.id.advanced_settings_chevron_imageview)
torSwitch = view.findViewById(R.id.tor_onboarding_switch)
seedOffsetCheckbox = view.findViewById(R.id.seed_offset_checkbox)
walletProxyAddressEditText = view.findViewById(R.id.wallet_proxy_address_edittext)
walletProxyPortEditText = view.findViewById(R.id.wallet_proxy_port_edittext)
advancedOptionsLayout = view.findViewById(R.id.more_options_layout)
showXmrchanSwitch = view.findViewById(R.id.show_xmrchan_switch)
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)
bindListeners()
bindObservers()
}
private void bindObservers() {
mViewModel.showMoreOptions.observe(getViewLifecycleOwner(), show -> {
private fun bindObservers() {
mViewModel?.showMoreOptions?.observe(viewLifecycleOwner) { show: Boolean ->
if (show) {
moreOptionsChevronImageView.setImageResource(R.drawable.ic_keyboard_arrow_up);
advancedOptionsLayout.setVisibility(View.VISIBLE);
moreOptionsChevronImageView?.setImageResource(R.drawable.ic_keyboard_arrow_up)
advancedOptionsLayout?.visibility = View.VISIBLE
} else {
moreOptionsChevronImageView.setImageResource(R.drawable.ic_keyboard_arrow_down);
advancedOptionsLayout.setVisibility(View.GONE);
moreOptionsChevronImageView?.setImageResource(R.drawable.ic_keyboard_arrow_down)
advancedOptionsLayout?.visibility = View.GONE
}
});
mViewModel.enableCreateButton.observe(getViewLifecycleOwner(), enable -> {
createWalletButton.setEnabled(enable);
});
mViewModel.seedType.observe(getViewLifecycleOwner(), seedType -> {
seedTypeButton.setText(seedType.toString());
seedTypeDescTextView.setText(getText(seedType.getDescResId()));
if (seedType == OnboardingViewModel.SeedType.LEGACY) {
seedOffsetCheckbox.setVisibility(View.VISIBLE);
walletRestoreHeightEditText.setVisibility(View.VISIBLE);
walletPasswordEditText.setHint(getString(R.string.password_optional));
}
mViewModel?.enableCreateButton?.observe(viewLifecycleOwner) { enable: Boolean ->
createWalletButton?.isEnabled = enable
}
mViewModel?.seedType?.observe(viewLifecycleOwner) { seedType: SeedType ->
seedTypeButton?.text = seedType.toString()
seedTypeDescTextView?.text = getText(seedType.descResId)
if (seedType == SeedType.LEGACY) {
seedOffsetCheckbox?.visibility = View.VISIBLE
walletRestoreHeightEditText?.visibility = View.VISIBLE
walletPasswordEditText?.hint = getString(R.string.password_optional)
} else {
seedOffsetCheckbox.setVisibility(View.GONE);
walletRestoreHeightEditText.setVisibility(View.GONE);
walletPasswordEditText.setHint(getString(R.string.password_non_optional));
seedOffsetCheckbox?.visibility = View.GONE
walletRestoreHeightEditText?.visibility = View.GONE
walletPasswordEditText?.hint = getString(R.string.password_non_optional)
}
}
});
}
private void bindListeners() {
seedOffsetCheckbox.setChecked(useOffset);
private fun bindListeners() {
seedOffsetCheckbox?.isChecked = useOffset
// Disable onBack click
OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {}
}
};
FragmentActivity activity = getActivity();
if (activity != null)
activity.getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), onBackPressedCallback);
moreOptionsDropdownTextView.setOnClickListener(view12 -> mViewModel.onMoreOptionsClicked());
moreOptionsChevronImageView.setOnClickListener(view12 -> mViewModel.onMoreOptionsClicked());
seedOffsetCheckbox.setOnCheckedChangeListener((compoundButton, b) -> useOffset = b);
createWalletButton.setOnClickListener(view1 -> {
prepareDefaultNode();
onBackPressedCallback.setEnabled(false);
((MoneroApplication) getActivity().getApplication()).getExecutor().execute(() -> {
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 {
prepareDefaultNode()
onBackPressedCallback.isEnabled = false
(getActivity()?.application as MoneroApplication).executor?.execute {
createOrImportWallet(
walletPasswordEditText.getText().toString(),
walletPasswordConfirmEditText.getText().toString(),
walletSeedEditText.getText().toString().trim(),
walletRestoreHeightEditText.getText().toString().trim()
);
});
});
walletPasswordEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
walletPasswordEditText?.text.toString(),
walletPasswordConfirmEditText?.text.toString(),
walletSeedEditText?.text.toString().trim { it <= ' ' },
walletRestoreHeightEditText?.text.toString().trim { it <= ' ' }
)
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
String text = editable.toString();
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()
if (text.isEmpty()) {
walletPasswordConfirmEditText.setText(null);
walletPasswordConfirmEditText.setVisibility(View.GONE);
walletPasswordConfirmEditText?.text = null
walletPasswordConfirmEditText?.visibility = View.GONE
} else {
walletPasswordConfirmEditText.setVisibility(View.VISIBLE);
walletPasswordConfirmEditText?.visibility = View.VISIBLE
}
}
});
walletSeedEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
String text = 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) {}
override fun afterTextChanged(editable: Editable) {
val text = editable.toString()
if (text.isEmpty()) {
createWalletButton.setText(R.string.create_wallet);
createWalletButton?.setText(R.string.create_wallet)
} else {
createWalletButton.setText(R.string.menu_restore);
createWalletButton?.setText(R.string.menu_restore)
}
}
});
seedTypeButton.setOnClickListener(v -> toggleSeedType());
torSwitch.setOnCheckedChangeListener((compoundButton, b) -> {
PrefService.getInstance().edit().putBoolean(Constants.PREF_USES_TOR, b).apply();
removeProxyTextListeners();
})
seedTypeButton?.setOnClickListener { toggleSeedType() }
torSwitch?.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean ->
PrefService.instance?.edit()?.putBoolean(Constants.PREF_USES_TOR, b)?.apply()
removeProxyTextListeners()
if (b) {
if (PrefService.getInstance().hasProxySet()) {
String proxyAddress = PrefService.getInstance().getProxyAddress();
String proxyPort = PrefService.getInstance().getProxyPort();
initProxyStuff(proxyAddress, proxyPort);
if (PrefService.instance?.hasProxySet() == true) {
val proxyAddress =
PrefService.instance?.proxyAddress ?: return@setOnCheckedChangeListener
val proxyPort =
PrefService.instance?.proxyPort ?: return@setOnCheckedChangeListener
initProxyStuff(proxyAddress, proxyPort)
} else {
initProxyStuff("127.0.0.1", "9050");
initProxyStuff("127.0.0.1", "9050")
}
addProxyTextListeners();
addProxyTextListeners()
}
mViewModel.updateProxy(((MoneroApplication) getActivity().getApplication()));
});
showXmrchanSwitch.setChecked(PrefService.getInstance().getBoolean(Constants.PREF_MONEROCHAN, true));
showXmrchanSwitch.setOnCheckedChangeListener((compoundButton, b) -> {
PrefService.getInstance().edit().putBoolean(Constants.PREF_MONEROCHAN, b).apply();
mViewModel?.updateProxy(getActivity()?.application as MoneroApplication)
}
showXmrchanSwitch?.isChecked =
PrefService.instance?.getBoolean(Constants.PREF_MONEROCHAN, true) == true
showXmrchanSwitch?.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean ->
PrefService.instance?.edit()?.putBoolean(Constants.PREF_MONEROCHAN, b)?.apply()
if (b) {
xmrchanOnboardingImage.setVisibility(View.VISIBLE);
xmrchanOnboardingImage?.visibility = View.VISIBLE
} else {
xmrchanOnboardingImage.setVisibility(View.GONE);
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 {
activity?.supportFragmentManager?.let { fragmentManager ->
val dialog = NodeSelectionBottomSheetDialog()
dialog.listener = this
dialog.show(fragmentManager, "node_selection_dialog")
}
}
});
Node node = PrefService.getInstance().getNode(); // should be using default here
selectNodeButton.setText(getString(R.string.node_button_text, node.getAddress()));
selectNodeButton.setOnClickListener(view1 -> {
NodeSelectionBottomSheetDialog dialog = new NodeSelectionBottomSheetDialog();
dialog.listener = this;
dialog.show(getActivity().getSupportFragmentManager(), "node_selection_dialog");
});
}
private void toggleSeedType() {
OnboardingViewModel.SeedType seedType = mViewModel.seedType.getValue();
if (seedType == null) return;
OnboardingViewModel.SeedType newSeedType = OnboardingViewModel.SeedType.UNKNOWN;
if (seedType == OnboardingViewModel.SeedType.POLYSEED) {
newSeedType = OnboardingViewModel.SeedType.LEGACY;
} else if (seedType == OnboardingViewModel.SeedType.LEGACY) {
newSeedType = OnboardingViewModel.SeedType.POLYSEED;
private fun toggleSeedType() {
val seedType = mViewModel?.seedType?.value ?: return
var newSeedType = SeedType.UNKNOWN
if (seedType == SeedType.POLYSEED) {
newSeedType = SeedType.LEGACY
} else if (seedType == SeedType.LEGACY) {
newSeedType = SeedType.POLYSEED
}
mViewModel?.setSeedType(newSeedType)
}
mViewModel.setSeedType(newSeedType);
private fun prepareDefaultNode() {
PrefService.instance?.node
}
private void prepareDefaultNode() {
PrefService.getInstance().getNode();
}
private void createOrImportWallet(String walletPassword, String confirmedPassword, String walletSeed, String restoreHeightText) {
Activity activity = getActivity();
private fun createOrImportWallet(
walletPassword: String,
confirmedPassword: String,
walletSeed: String,
restoreHeightText: String
) {
val activity: Activity? = activity
if (activity != null) {
mViewModel.createOrImportWallet(activity, walletPassword, confirmedPassword, walletSeed, restoreHeightText, useOffset);
mViewModel?.createOrImportWallet(
activity,
walletPassword,
confirmedPassword,
walletSeed,
restoreHeightText,
useOffset
)
}
}
private void removeProxyTextListeners() {
walletProxyAddressEditText.removeTextChangedListener(proxyAddressListener);
walletProxyPortEditText.removeTextChangedListener(proxyPortListener);
private fun removeProxyTextListeners() {
walletProxyAddressEditText?.removeTextChangedListener(proxyAddressListener)
walletProxyPortEditText?.removeTextChangedListener(proxyPortListener)
}
private void addProxyTextListeners() {
walletProxyAddressEditText.addTextChangedListener(proxyAddressListener);
walletProxyPortEditText.addTextChangedListener(proxyPortListener);
private fun addProxyTextListeners() {
walletProxyAddressEditText?.addTextChangedListener(proxyAddressListener)
walletProxyPortEditText?.addTextChangedListener(proxyPortListener)
}
private void initProxyStuff(String proxyAddress, String proxyPort) {
boolean validIpAddress = Patterns.IP_ADDRESS.matcher(proxyAddress).matches();
private fun initProxyStuff(proxyAddress: String, proxyPort: String) {
val validIpAddress = Patterns.IP_ADDRESS.matcher(proxyAddress).matches()
if (validIpAddress) {
mViewModel.setProxyAddress(proxyAddress);
mViewModel.setProxyPort(proxyPort);
walletProxyAddressEditText.setText(proxyAddress);
walletProxyPortEditText.setText(proxyPort);
mViewModel?.setProxyAddress(proxyAddress)
mViewModel?.setProxyPort(proxyPort)
walletProxyAddressEditText?.setText(proxyAddress)
walletProxyPortEditText?.setText(proxyPort)
}
}
@Override
public void onNodeSelected() {
Node node = PrefService.getInstance().getNode();
selectNodeButton.setText(getString(R.string.node_button_text, node.getAddress()));
mViewModel.updateProxy(((MoneroApplication) getActivity().getApplication()));
override fun onNodeSelected() {
val node = PrefService.instance?.node
selectNodeButton?.text = getString(R.string.node_button_text, node?.address)
mViewModel?.updateProxy(activity?.application as MoneroApplication)
}
@Override
public void onClickedEditNode(Node node) {
override fun onClickedEditNode(node: Node?) {}
override fun onClickedAddNode() {
activity?.supportFragmentManager?.let { fragmentManager ->
val addNodeDialog = AddNodeBottomSheetDialog()
addNodeDialog.listener = this
addNodeDialog.show(fragmentManager, "add_node_dialog")
}
}
@Override
public void onClickedAddNode() {
AddNodeBottomSheetDialog addNodeDialog = new AddNodeBottomSheetDialog();
addNodeDialog.listener = this;
addNodeDialog.show(getActivity().getSupportFragmentManager(), "add_node_dialog");
override fun onNodeAdded() {
activity?.supportFragmentManager?.let { fragmentManager ->
val dialog = NodeSelectionBottomSheetDialog()
dialog.listener = this
dialog.show(fragmentManager, "node_selection_dialog")
}
@Override
public void onNodeAdded() {
NodeSelectionBottomSheetDialog dialog = new NodeSelectionBottomSheetDialog();
dialog.listener = this;
dialog.show(getActivity().getSupportFragmentManager(), "node_selection_dialog");
}
}

View file

@ -1,179 +1,211 @@
package net.mynero.wallet.fragment.onboarding;
package net.mynero.wallet.fragment.onboarding
import android.app.Activity;
import android.util.Patterns;
import android.widget.Toast;
import android.app.Activity
import android.util.Patterns
import android.widget.Toast
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import net.mynero.wallet.MainActivity
import net.mynero.wallet.MoneroApplication
import net.mynero.wallet.R
import net.mynero.wallet.model.Wallet
import net.mynero.wallet.model.WalletManager
import net.mynero.wallet.service.PrefService
import net.mynero.wallet.util.Constants
import net.mynero.wallet.util.RestoreHeight
import java.io.File
import java.util.Calendar
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import net.mynero.wallet.MainActivity;
import net.mynero.wallet.MoneroApplication;
import net.mynero.wallet.R;
import net.mynero.wallet.model.Wallet;
import net.mynero.wallet.model.WalletManager;
import net.mynero.wallet.service.PrefService;
import net.mynero.wallet.util.Constants;
import net.mynero.wallet.util.RestoreHeight;
import java.io.File;
import java.util.Calendar;
public class OnboardingViewModel extends ViewModel {
private final MutableLiveData<Boolean> _showMoreOptions = new MutableLiveData<>(false);
private final MutableLiveData<Boolean> _enableCreateButton = new MutableLiveData<>(true);
private final MutableLiveData<SeedType> _seedType = new MutableLiveData<>(SeedType.POLYSEED);
public LiveData<Boolean> showMoreOptions = _showMoreOptions;
public LiveData<Boolean> enableCreateButton = _enableCreateButton;
public LiveData<SeedType> seedType = _seedType;
private String proxyAddress = "";
private String proxyPort = "";
public void onMoreOptionsClicked() {
boolean currentValue = showMoreOptions.getValue() != null ? showMoreOptions.getValue() : false;
boolean newValue = !currentValue;
_showMoreOptions.setValue(newValue);
class OnboardingViewModel : ViewModel() {
private val _showMoreOptions = MutableLiveData(false)
private val _enableCreateButton = MutableLiveData(true)
private val _seedType = MutableLiveData(SeedType.POLYSEED)
var showMoreOptions: LiveData<Boolean> = _showMoreOptions
var enableCreateButton: LiveData<Boolean> = _enableCreateButton
var seedType: LiveData<SeedType> = _seedType
private var proxyAddress = ""
private var proxyPort = ""
fun onMoreOptionsClicked() {
val currentValue = showMoreOptions.value ?: false
val newValue = !currentValue
_showMoreOptions.value = newValue
}
public void updateProxy(MoneroApplication application) {
application.getExecutor().execute(() -> {
boolean usesProxy = PrefService.getInstance().getBoolean(Constants.PREF_USES_TOR, false);
fun updateProxy(application: MoneroApplication) {
application.executor?.execute {
val usesProxy = PrefService.instance?.getBoolean(Constants.PREF_USES_TOR, false) == true
if (!usesProxy) {
return;
return@execute
}
if (proxyAddress.isEmpty()) proxyAddress = "127.0.0.1";
if (proxyPort.isEmpty()) proxyPort = "9050";
boolean validIpAddress = Patterns.IP_ADDRESS.matcher(proxyAddress).matches();
if (proxyAddress.isEmpty()) proxyAddress = "127.0.0.1"
if (proxyPort.isEmpty()) proxyPort = "9050"
val validIpAddress = Patterns.IP_ADDRESS.matcher(proxyAddress).matches()
if (validIpAddress) {
String proxy = proxyAddress + ":" + proxyPort;
PrefService.getInstance().edit().putString(Constants.PREF_PROXY, proxy).apply();
val proxy = "$proxyAddress:$proxyPort"
PrefService.instance?.edit()?.putString(Constants.PREF_PROXY, proxy)?.apply()
}
}
});
}
public void setSeedType(SeedType seedType) {
this._seedType.setValue(seedType);
fun setSeedType(seedType: SeedType?) {
_seedType.value = seedType
}
public void setProxyAddress(String address) {
this.proxyAddress = address;
fun setProxyAddress(address: String) {
proxyAddress = address
}
public void setProxyPort(String port) {
this.proxyPort = port;
fun setProxyPort(port: String) {
proxyPort = port
}
public void createOrImportWallet(Activity mainActivity, String walletPassword, String confirmedPassword, String walletSeed, String restoreHeightText, boolean useOffset) {
MoneroApplication application = (MoneroApplication) mainActivity.getApplication();
application.getExecutor().execute(() -> {
_enableCreateButton.postValue(false);
String offset = useOffset ? walletPassword : "";
if (!walletPassword.isEmpty()) {
if (!walletPassword.equals(confirmedPassword)) {
_enableCreateButton.postValue(true);
mainActivity.runOnUiThread(() -> Toast.makeText(mainActivity, application.getString(R.string.invalid_confirmed_password), Toast.LENGTH_SHORT).show());
return;
fun createOrImportWallet(
mainActivity: Activity,
walletPassword: String,
confirmedPassword: String,
walletSeed: String,
restoreHeightText: String,
useOffset: Boolean
) {
val application = mainActivity.application as MoneroApplication
application.executor?.execute {
_enableCreateButton.postValue(false)
val offset = if (useOffset) walletPassword else ""
if (walletPassword.isNotEmpty()) {
if (walletPassword != confirmedPassword) {
_enableCreateButton.postValue(true)
mainActivity.runOnUiThread {
Toast.makeText(
mainActivity,
application.getString(R.string.invalid_confirmed_password),
Toast.LENGTH_SHORT
).show()
}
PrefService.getInstance().edit().putBoolean(Constants.PREF_USES_PASSWORD, true).apply();
return@execute
}
long restoreHeight = getNewRestoreHeight();
File walletFile = new File(mainActivity.getApplicationInfo().dataDir, Constants.WALLET_NAME);
Wallet wallet = null;
if (!offset.isEmpty()) {
PrefService.getInstance().edit().putBoolean(Constants.PREF_USES_OFFSET, true).apply();
PrefService.instance?.edit()?.putBoolean(Constants.PREF_USES_PASSWORD, true)
?.apply()
}
var restoreHeight = newRestoreHeight
val walletFile = File(mainActivity.applicationInfo.dataDir, Constants.WALLET_NAME)
var wallet: Wallet? = null
if (offset.isNotEmpty()) {
PrefService.instance?.edit()?.putBoolean(Constants.PREF_USES_OFFSET, true)?.apply()
}
if (walletSeed.isEmpty()) {
SeedType seedTypeValue = seedType.getValue();
if (seedTypeValue == null) return;
val seedTypeValue = seedType.value ?: return@execute
if (seedTypeValue == SeedType.POLYSEED) {
if (offset.isEmpty()) {
mainActivity.runOnUiThread(() -> {
_enableCreateButton.postValue(true);
Toast.makeText(mainActivity, application.getString(R.string.invalid_empty_passphrase), Toast.LENGTH_SHORT).show();
});
return;
wallet = if (offset.isEmpty()) {
mainActivity.runOnUiThread {
_enableCreateButton.postValue(true)
Toast.makeText(
mainActivity,
application.getString(R.string.invalid_empty_passphrase),
Toast.LENGTH_SHORT
).show()
}
return@execute
} else {
wallet = WalletManager.getInstance().createWalletPolyseed(walletFile, walletPassword, offset, Constants.MNEMONIC_LANGUAGE);
WalletManager.instance?.createWalletPolyseed(
walletFile,
walletPassword,
offset,
Constants.MNEMONIC_LANGUAGE
)
}
} else if (seedTypeValue == SeedType.LEGACY) {
File tmpWalletFile = new File(mainActivity.getApplicationInfo().dataDir, Constants.WALLET_NAME + "_tmp");
Wallet tmpWallet = createTempWallet(tmpWalletFile); //we do this to get seed, then recover wallet so we can use seed offset
wallet = WalletManager.getInstance().recoveryWallet(walletFile, walletPassword, tmpWallet.getSeed(""), offset, restoreHeight);
tmpWalletFile.delete();
val tmpWalletFile =
File(mainActivity.applicationInfo.dataDir, Constants.WALLET_NAME + "_tmp")
val tmpWallet =
createTempWallet(tmpWalletFile) //we do this to get seed, then recover wallet so we can use seed offset
tmpWallet?.let {
wallet = WalletManager.instance?.recoveryWallet(
walletFile,
walletPassword,
tmpWallet.getSeed("") ?: return@let,
offset,
restoreHeight
)
tmpWalletFile.delete()
}
}
} else {
if (getMnemonicType(walletSeed) == SeedType.UNKNOWN) {
mainActivity.runOnUiThread(() -> {
_enableCreateButton.postValue(true);
Toast.makeText(mainActivity, application.getString(R.string.invalid_mnemonic_code), Toast.LENGTH_SHORT).show();
});
return;
mainActivity.runOnUiThread {
_enableCreateButton.postValue(true)
Toast.makeText(
mainActivity,
application.getString(R.string.invalid_mnemonic_code),
Toast.LENGTH_SHORT
).show()
}
if (!restoreHeightText.isEmpty()) {
restoreHeight = Long.parseLong(restoreHeightText);
return@execute
}
wallet = WalletManager.getInstance().recoveryWallet(walletFile, walletPassword, walletSeed, offset, restoreHeight);
if (restoreHeightText.isNotEmpty()) {
restoreHeight = restoreHeightText.toLong()
}
Wallet.Status walletStatus = wallet.getStatus();
wallet.close();
boolean 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) {
((MainActivity) mainActivity).init(walletFile, walletPassword);
mainActivity.runOnUiThread(mainActivity::onBackPressed);
wallet = WalletManager.instance?.recoveryWallet(
walletFile,
walletPassword,
walletSeed,
offset,
restoreHeight
)
}
val walletStatus = wallet?.status
wallet?.close()
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) {
(mainActivity as MainActivity).init(walletFile, walletPassword)
mainActivity.runOnUiThread { mainActivity.onBackPressed() }
} else {
mainActivity.runOnUiThread(() -> {
_enableCreateButton.postValue(true);
Toast.makeText(mainActivity, application.getString(R.string.create_wallet_failed, walletStatus.errorString), Toast.LENGTH_SHORT).show();
});
mainActivity.runOnUiThread {
_enableCreateButton.postValue(true)
Toast.makeText(
mainActivity,
application.getString(
R.string.create_wallet_failed,
walletStatus?.errorString
),
Toast.LENGTH_SHORT
).show()
}
}
}
});
}
private long getNewRestoreHeight() {
Calendar restoreDate = Calendar.getInstance();
restoreDate.add(Calendar.DAY_OF_MONTH, 0);
return RestoreHeight.getInstance().getHeight(restoreDate.getTime());
private val newRestoreHeight: Long
get() {
val restoreDate = Calendar.getInstance()
restoreDate.add(Calendar.DAY_OF_MONTH, 0)
return RestoreHeight.instance?.getHeight(restoreDate.time) ?: 0
}
public SeedType getMnemonicType(String seed) {
String[] words = seed.split("\\s");
SeedType seedTypeValue = seedType.getValue();
if (seedTypeValue == null) return SeedType.LEGACY;
if (words.length == 16 && seedTypeValue == SeedType.POLYSEED) {
return SeedType.POLYSEED;
} else if (words.length == 25 && seedTypeValue == SeedType.LEGACY) {
return SeedType.LEGACY;
private fun getMnemonicType(seed: String): SeedType {
val words = seed.split("\\s".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val seedTypeValue = seedType.value ?: return SeedType.LEGACY
return if (words.size == 16 && seedTypeValue == SeedType.POLYSEED) {
SeedType.POLYSEED
} else if (words.size == 25 && seedTypeValue == SeedType.LEGACY) {
SeedType.LEGACY
} else {
return SeedType.UNKNOWN;
SeedType.UNKNOWN
}
}
private Wallet createTempWallet(File tmpWalletFile) {
return WalletManager.getInstance().createWallet(tmpWalletFile, "", Constants.MNEMONIC_LANGUAGE, 0);
private fun createTempWallet(tmpWalletFile: File): Wallet? {
return WalletManager.instance?.createWallet(
tmpWalletFile,
"",
Constants.MNEMONIC_LANGUAGE,
0
)
}
public enum SeedType {
LEGACY(R.string.seed_desc_legacy),
POLYSEED(R.string.seed_desc_polyseed),
UNKNOWN(0);
enum class SeedType(val descResId: Int) {
LEGACY(R.string.seed_desc_legacy), POLYSEED(R.string.seed_desc_polyseed), UNKNOWN(0)
private final int descResId;
SeedType(int descResId) {
this.descResId = descResId;
}
public int getDescResId() {
return descResId;
}
}
}

View file

@ -237,7 +237,10 @@ class SendFragment : Fragment() {
val address = if (destCount == 1) getAddressField(0).text.toString() else "Multiple"
addressTextView?.text = getString(R.string.tx_address_text, address)
amountTextView?.text =
getString(R.string.tx_amount_text, Helper.getDisplayAmount(pendingTx.getAmount()))
getString(
R.string.tx_amount_text,
Helper.getDisplayAmount(pendingTx.getAmount())
)
feeTextView?.text =
getString(R.string.tx_fee_text, Helper.getDisplayAmount(pendingTx.getFee()))
}

View file

@ -1,312 +1,291 @@
package net.mynero.wallet.fragment.settings;
package net.mynero.wallet.fragment.settings
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;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
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
import android.widget.Button
import android.widget.CompoundButton
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.widget.SwitchCompat
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.NavHostFragment
import net.mynero.wallet.MoneroApplication
import net.mynero.wallet.R
import net.mynero.wallet.data.Node
import net.mynero.wallet.data.Node.Companion.fromJson
import net.mynero.wallet.fragment.dialog.AddNodeBottomSheetDialog
import net.mynero.wallet.fragment.dialog.AddNodeBottomSheetDialog.AddNodeListener
import net.mynero.wallet.fragment.dialog.EditNodeBottomSheetDialog
import net.mynero.wallet.fragment.dialog.EditNodeBottomSheetDialog.EditNodeListener
import net.mynero.wallet.fragment.dialog.NodeSelectionBottomSheetDialog
import net.mynero.wallet.fragment.dialog.NodeSelectionBottomSheetDialog.NodeSelectionDialogListener
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.Wallet.ConnectionStatus
import net.mynero.wallet.model.WalletManager
import net.mynero.wallet.service.BalanceService
import net.mynero.wallet.service.BlockchainService
import net.mynero.wallet.service.HistoryService
import net.mynero.wallet.service.PrefService
import net.mynero.wallet.util.Constants
import org.json.JSONArray
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.SwitchCompat;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment;
import net.mynero.wallet.MoneroApplication;
import net.mynero.wallet.R;
import net.mynero.wallet.data.Node;
import net.mynero.wallet.fragment.dialog.AddNodeBottomSheetDialog;
import net.mynero.wallet.fragment.dialog.EditNodeBottomSheetDialog;
import net.mynero.wallet.fragment.dialog.NodeSelectionBottomSheetDialog;
import net.mynero.wallet.fragment.dialog.PasswordBottomSheetDialog;
import net.mynero.wallet.fragment.dialog.WalletKeysBottomSheetDialog;
import net.mynero.wallet.model.Wallet;
import net.mynero.wallet.model.WalletManager;
import net.mynero.wallet.service.BalanceService;
import net.mynero.wallet.service.BlockchainService;
import net.mynero.wallet.service.HistoryService;
import net.mynero.wallet.service.PrefService;
import net.mynero.wallet.util.Constants;
import org.json.JSONArray;
import org.json.JSONObject;
public class SettingsFragment extends Fragment implements PasswordBottomSheetDialog.PasswordListener, NodeSelectionBottomSheetDialog.NodeSelectionDialogListener, AddNodeBottomSheetDialog.AddNodeListener, EditNodeBottomSheetDialog.EditNodeListener {
private SettingsViewModel mViewModel;
TextWatcher proxyAddressListener = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
class SettingsFragment : Fragment(), PasswordListener, NodeSelectionDialogListener, AddNodeListener,
EditNodeListener {
private var mViewModel: SettingsViewModel? = null
private var proxyAddressListener: TextWatcher = 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) {
if (mViewModel != null) {
mViewModel.setProxyAddress(editable.toString());
mViewModel.updateProxy(((MoneroApplication) getActivity().getApplication()));
mViewModel?.setProxyAddress(editable.toString())
mViewModel?.updateProxy(activity?.application as MoneroApplication)
}
}
};
TextWatcher proxyPortListener = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
private var proxyPortListener: TextWatcher = 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) {
if (mViewModel != null) {
mViewModel.setProxyPort(editable.toString());
mViewModel.updateProxy(((MoneroApplication) getActivity().getApplication()));
mViewModel?.setProxyPort(editable.toString())
mViewModel?.updateProxy(activity?.application as MoneroApplication)
}
}
};
private EditText walletProxyAddressEditText;
private EditText walletProxyPortEditText;
private Button selectNodeButton;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_settings, container, false);
}
private var walletProxyAddressEditText: EditText? = null
private var walletProxyPortEditText: EditText? = null
private var selectNodeButton: Button? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_settings, container, false)
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewModel = new ViewModelProvider(this).get(SettingsViewModel.class);
Button displaySeedButton = view.findViewById(R.id.display_seed_button);
Button displayUtxosButton = view.findViewById(R.id.display_utxos_button);
selectNodeButton = view.findViewById(R.id.select_node_button);
SwitchCompat streetModeSwitch = view.findViewById(R.id.street_mode_switch);
SwitchCompat monerochanSwitch = view.findViewById(R.id.monerochan_switch);
SwitchCompat donationSwitch = view.findViewById(R.id.donate_per_tx_switch);
SwitchCompat torSwitch = view.findViewById(R.id.tor_switch);
ConstraintLayout proxySettingsLayout = view.findViewById(R.id.wallet_proxy_settings_layout);
walletProxyAddressEditText = view.findViewById(R.id.wallet_proxy_address_edittext);
walletProxyPortEditText = view.findViewById(R.id.wallet_proxy_port_edittext);
streetModeSwitch.setChecked(PrefService.getInstance().getBoolean(Constants.PREF_STREET_MODE, false));
streetModeSwitch.setOnCheckedChangeListener((compoundButton, b) -> {
PrefService.getInstance().edit().putBoolean(Constants.PREF_STREET_MODE, b).apply();
BalanceService.instance.refreshBalance();
});
monerochanSwitch.setChecked(PrefService.getInstance().getBoolean(Constants.PREF_MONEROCHAN, true));
monerochanSwitch.setOnCheckedChangeListener((compoundButton, b) -> {
PrefService.getInstance().edit().putBoolean(Constants.PREF_MONEROCHAN, b).apply();
HistoryService.getInstance().refreshHistory();
});
donationSwitch.setChecked(PrefService.getInstance().getBoolean(Constants.PREF_DONATE_PER_TX, false));
donationSwitch.setOnCheckedChangeListener((compoundButton, b) -> PrefService.getInstance().edit().putBoolean(Constants.PREF_DONATE_PER_TX, b).apply());
PrefService prefService = PrefService.getInstance();
boolean usesProxy = prefService.getBoolean(Constants.PREF_USES_TOR, false);
if (prefService.hasProxySet()) {
String proxyAddress = prefService.getProxyAddress();
String proxyPort = prefService.getProxyPort();
initProxyStuff(proxyAddress, proxyPort);
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mViewModel = ViewModelProvider(this)[SettingsViewModel::class.java]
val displaySeedButton = view.findViewById<Button>(R.id.display_seed_button)
val displayUtxosButton = view.findViewById<Button>(R.id.display_utxos_button)
selectNodeButton = view.findViewById(R.id.select_node_button)
val streetModeSwitch = view.findViewById<SwitchCompat>(R.id.street_mode_switch)
val monerochanSwitch = view.findViewById<SwitchCompat>(R.id.monerochan_switch)
val donationSwitch = view.findViewById<SwitchCompat>(R.id.donate_per_tx_switch)
val torSwitch = view.findViewById<SwitchCompat>(R.id.tor_switch)
val proxySettingsLayout =
view.findViewById<ConstraintLayout>(R.id.wallet_proxy_settings_layout)
walletProxyAddressEditText = view.findViewById(R.id.wallet_proxy_address_edittext)
walletProxyPortEditText = view.findViewById(R.id.wallet_proxy_port_edittext)
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()
}
torSwitch.setChecked(usesProxy);
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
val usesProxy = prefService?.getBoolean(Constants.PREF_USES_TOR, false) == true
if (prefService?.hasProxySet() == true) {
val proxyAddress = prefService.proxyAddress
val proxyPort = prefService.proxyPort
initProxyStuff(proxyAddress, proxyPort)
}
torSwitch.isChecked = usesProxy
if (usesProxy) {
proxySettingsLayout.setVisibility(View.VISIBLE);
proxySettingsLayout.visibility = View.VISIBLE
} else {
proxySettingsLayout.setVisibility(View.GONE);
proxySettingsLayout.visibility = View.GONE
}
addProxyTextListeners();
torSwitch.setOnCheckedChangeListener((compoundButton, b) -> {
prefService.edit().putBoolean(Constants.PREF_USES_TOR, b).apply();
addProxyTextListeners()
torSwitch.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean ->
prefService?.edit()?.putBoolean(Constants.PREF_USES_TOR, b)?.apply()
if (b) {
if (prefService.hasProxySet()) {
removeProxyTextListeners();
String proxyAddress = prefService.getProxyAddress();
String proxyPort = prefService.getProxyPort();
initProxyStuff(proxyAddress, proxyPort);
addProxyTextListeners();
if (prefService?.hasProxySet() == true) {
removeProxyTextListeners()
val proxyAddress = prefService.proxyAddress
val proxyPort = prefService.proxyPort
initProxyStuff(proxyAddress, proxyPort)
addProxyTextListeners()
}
proxySettingsLayout.setVisibility(View.VISIBLE);
proxySettingsLayout.visibility = View.VISIBLE
} else {
proxySettingsLayout.setVisibility(View.GONE);
proxySettingsLayout.visibility = View.GONE
}
mViewModel.updateProxy(((MoneroApplication) getActivity().getApplication()));
});
displaySeedButton.setOnClickListener(view1 -> {
boolean usesPassword = PrefService.getInstance().getBoolean(Constants.PREF_USES_PASSWORD, false);
mViewModel?.updateProxy(activity?.application as MoneroApplication)
}
displaySeedButton.setOnClickListener {
val usesPassword =
PrefService.instance?.getBoolean(Constants.PREF_USES_PASSWORD, false) == true
if (usesPassword) {
PasswordBottomSheetDialog passwordDialog = new PasswordBottomSheetDialog();
passwordDialog.cancelable = true;
passwordDialog.listener = this;
passwordDialog.show(getActivity().getSupportFragmentManager(), "password_dialog");
activity?.supportFragmentManager?.let { fragmentManager ->
val passwordDialog = PasswordBottomSheetDialog()
passwordDialog.cancelable = true
passwordDialog.listener = this
passwordDialog.show(fragmentManager, "password_dialog")
}
} else {
displaySeedDialog("");
displaySeedDialog("")
}
}
displayUtxosButton.setOnClickListener { navigate(R.id.nav_to_utxos) }
val statusTextView = view.findViewById<TextView>(R.id.status_textview)
BlockchainService.instance?.connectionStatus?.observe(viewLifecycleOwner) { connectionStatus: ConnectionStatus ->
if (connectionStatus === ConnectionStatus.ConnectionStatus_Connected) {
statusTextView.text = resources.getText(R.string.connected)
} else if (connectionStatus === ConnectionStatus.ConnectionStatus_Disconnected) {
statusTextView.text = resources.getText(R.string.disconnected)
} else if (connectionStatus === ConnectionStatus.ConnectionStatus_WrongVersion) {
statusTextView.text = resources.getText(R.string.version_mismatch)
}
}
val node = PrefService.instance?.node // shouldn't use default value here
selectNodeButton?.text = getString(R.string.node_button_text, node?.address)
selectNodeButton?.setOnClickListener {
activity?.supportFragmentManager?.let { fragmentManager ->
val dialog = NodeSelectionBottomSheetDialog()
dialog.listener = this
dialog.show(fragmentManager, "node_selection_dialog")
}
});
displayUtxosButton.setOnClickListener(view1 -> {
navigate(R.id.nav_to_utxos);
});
TextView statusTextView = view.findViewById(R.id.status_textview);
BlockchainService.instance.connectionStatus.observe(getViewLifecycleOwner(), connectionStatus -> {
if (connectionStatus == Wallet.ConnectionStatus.ConnectionStatus_Connected) {
statusTextView.setText(getResources().getText(R.string.connected));
} else if (connectionStatus == Wallet.ConnectionStatus.ConnectionStatus_Disconnected) {
statusTextView.setText(getResources().getText(R.string.disconnected));
} else if (connectionStatus == Wallet.ConnectionStatus.ConnectionStatus_WrongVersion) {
statusTextView.setText(getResources().getText(R.string.version_mismatch));
}
});
Node node = PrefService.getInstance().getNode(); // shouldn't use default value here
selectNodeButton.setText(getString(R.string.node_button_text, node.getAddress()));
selectNodeButton.setOnClickListener(view1 -> {
NodeSelectionBottomSheetDialog dialog = new NodeSelectionBottomSheetDialog();
dialog.listener = this;
dialog.show(getActivity().getSupportFragmentManager(), "node_selection_dialog");
});
}
private void displaySeedDialog(String password) {
WalletKeysBottomSheetDialog informationDialog = new WalletKeysBottomSheetDialog();
informationDialog.password = password;
informationDialog.show(getActivity().getSupportFragmentManager(), "information_seed_dialog");
private fun displaySeedDialog(password: String) {
activity?.supportFragmentManager?.let { fragmentManager ->
val informationDialog = WalletKeysBottomSheetDialog()
informationDialog.password = password
informationDialog.show(fragmentManager, "information_seed_dialog")
}
}
@Override
public void onPasswordSuccess(String password) {
displaySeedDialog(password);
override fun onPasswordSuccess(password: String) {
displaySeedDialog(password)
}
@Override
public void onPasswordFail() {
Toast.makeText(getContext(), R.string.bad_password, Toast.LENGTH_SHORT).show();
override fun onPasswordFail() {
Toast.makeText(context, R.string.bad_password, Toast.LENGTH_SHORT).show()
}
private void initProxyStuff(String proxyAddress, String proxyPort) {
boolean validIpAddress = Patterns.IP_ADDRESS.matcher(proxyAddress).matches();
private fun initProxyStuff(proxyAddress: String, proxyPort: String) {
val validIpAddress = Patterns.IP_ADDRESS.matcher(proxyAddress).matches()
if (validIpAddress) {
mViewModel.setProxyAddress(proxyAddress);
mViewModel.setProxyPort(proxyPort);
walletProxyAddressEditText.setText(proxyAddress);
walletProxyPortEditText.setText(proxyPort);
mViewModel?.setProxyAddress(proxyAddress)
mViewModel?.setProxyPort(proxyPort)
walletProxyAddressEditText?.setText(proxyAddress)
walletProxyPortEditText?.setText(proxyPort)
}
}
private void removeProxyTextListeners() {
walletProxyAddressEditText.removeTextChangedListener(proxyAddressListener);
walletProxyPortEditText.removeTextChangedListener(proxyPortListener);
private fun removeProxyTextListeners() {
walletProxyAddressEditText?.removeTextChangedListener(proxyAddressListener)
walletProxyPortEditText?.removeTextChangedListener(proxyPortListener)
}
private void addProxyTextListeners() {
walletProxyAddressEditText.addTextChangedListener(proxyAddressListener);
walletProxyPortEditText.addTextChangedListener(proxyPortListener);
private fun addProxyTextListeners() {
walletProxyAddressEditText?.addTextChangedListener(proxyAddressListener)
walletProxyPortEditText?.addTextChangedListener(proxyPortListener)
}
@Override
public void onNodeSelected() {
Node node = PrefService.getInstance().getNode();
selectNodeButton.setText(getString(R.string.node_button_text, node.getAddress()));
mViewModel.updateProxy(((MoneroApplication) getActivity().getApplication()));
((MoneroApplication) getActivity().getApplication()).getExecutor().execute(() -> {
WalletManager.getInstance().getWallet().init(0);
WalletManager.getInstance().getWallet().startRefresh();
});
override fun onNodeSelected() {
val node = PrefService.instance?.node
selectNodeButton?.text = getString(R.string.node_button_text, node?.address)
mViewModel?.updateProxy(activity?.application as MoneroApplication)
(activity?.application as MoneroApplication).executor?.execute {
WalletManager.instance?.wallet?.init(0)
WalletManager.instance?.wallet?.startRefresh()
}
}
@Override
public void onClickedAddNode() {
AddNodeBottomSheetDialog addNodeDialog = new AddNodeBottomSheetDialog();
addNodeDialog.listener = this;
addNodeDialog.show(getActivity().getSupportFragmentManager(), "add_node_dialog");
override fun onClickedAddNode() {
activity?.supportFragmentManager?.let { fragmentManager ->
val addNodeDialog = AddNodeBottomSheetDialog()
addNodeDialog.listener = this
addNodeDialog.show(fragmentManager, "add_node_dialog")
}
}
@Override
public void onClickedEditNode(Node node) {
EditNodeBottomSheetDialog editNodeDialog = new EditNodeBottomSheetDialog();
editNodeDialog.listener = this;
editNodeDialog.node = node;
editNodeDialog.show(getActivity().getSupportFragmentManager(), "edit_node_dialog");
override fun onClickedEditNode(node: Node?) {
activity?.supportFragmentManager?.let { fragmentManager ->
val editNodeDialog = EditNodeBottomSheetDialog()
editNodeDialog.listener = this
editNodeDialog.node = node
editNodeDialog.show(fragmentManager, "edit_node_dialog")
}
@Override
public void onNodeAdded() {
NodeSelectionBottomSheetDialog dialog = new NodeSelectionBottomSheetDialog();
dialog.listener = this;
dialog.show(getActivity().getSupportFragmentManager(), "node_selection_dialog");
}
private void navigate(int destination) {
FragmentActivity activity = getActivity();
override fun onNodeAdded() {
activity?.supportFragmentManager?.let { fragmentManager ->
val dialog = NodeSelectionBottomSheetDialog()
dialog.listener = this
dialog.show(fragmentManager, "node_selection_dialog")
}
}
private fun navigate(destination: Int) {
val activity = activity
if (activity != null) {
FragmentManager fm = activity.getSupportFragmentManager();
NavHostFragment navHostFragment =
(NavHostFragment) fm.findFragmentById(R.id.nav_host_fragment);
if (navHostFragment != null) {
navHostFragment.getNavController().navigate(destination);
}
val fm = activity.supportFragmentManager
val navHostFragment = fm.findFragmentById(R.id.nav_host_fragment) as NavHostFragment?
navHostFragment?.navController?.navigate(destination)
}
}
@Override
public void onNodeDeleted(Node node) {
override fun onNodeDeleted(node: Node?) {
try {
String nodesArray = PrefService.getInstance().getString(Constants.PREF_CUSTOM_NODES, "[]");
JSONArray jsonArray = new JSONArray(nodesArray);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject nodeJsonObject = jsonArray.getJSONObject(i);
Node savedNode = Node.fromJson(nodeJsonObject);
if (savedNode.toNodeString().equals(node.toNodeString()))
jsonArray.remove(i);
val nodesArray = PrefService.instance?.getString(Constants.PREF_CUSTOM_NODES, "[]")
val jsonArray = JSONArray(nodesArray)
for (i in 0 until jsonArray.length()) {
val nodeJsonObject = jsonArray.getJSONObject(i)
val savedNode = fromJson(nodeJsonObject)
if (savedNode?.toNodeString() == node?.toNodeString()) jsonArray.remove(i)
}
saveNodesAndReopen(jsonArray);
} catch (Exception e) {
e.printStackTrace();
saveNodesAndReopen(jsonArray)
} catch (e: Exception) {
e.printStackTrace()
}
}
@Override
public void onNodeEdited(Node oldNode, Node newNode) {
override fun onNodeEdited(oldNode: Node?, newNode: Node?) {
try {
String nodesArray = PrefService.getInstance().getString(Constants.PREF_CUSTOM_NODES, "[]");
JSONArray jsonArray = new JSONArray(nodesArray);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject nodeJsonObject = jsonArray.getJSONObject(i);
Node savedNode = Node.fromJson(nodeJsonObject);
if (savedNode.toNodeString().equals(oldNode.toNodeString()))
jsonArray.put(i, newNode.toJson());
val nodesArray = PrefService.instance?.getString(Constants.PREF_CUSTOM_NODES, "[]")
val jsonArray = JSONArray(nodesArray)
for (i in 0 until jsonArray.length()) {
val nodeJsonObject = jsonArray.getJSONObject(i)
val savedNode = fromJson(nodeJsonObject)
if (savedNode?.toNodeString() == oldNode?.toNodeString()) jsonArray.put(
i,
newNode?.toJson()
)
}
saveNodesAndReopen(jsonArray);
} catch (Exception e) {
e.printStackTrace();
saveNodesAndReopen(jsonArray)
} catch (e: Exception) {
e.printStackTrace()
}
}
private void saveNodesAndReopen(JSONArray jsonArray) {
PrefService.getInstance().edit().putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString()).apply();
onNodeAdded();
private fun saveNodesAndReopen(jsonArray: JSONArray) {
PrefService.instance?.edit()?.putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString())
?.apply()
onNodeAdded()
}
}

View file

@ -1,50 +1,43 @@
package net.mynero.wallet.fragment.settings;
package net.mynero.wallet.fragment.settings
import android.util.Patterns;
import androidx.lifecycle.ViewModel;
import net.mynero.wallet.MoneroApplication;
import net.mynero.wallet.data.Node;
import net.mynero.wallet.model.WalletManager;
import net.mynero.wallet.service.PrefService;
import net.mynero.wallet.util.Constants;
public class SettingsViewModel extends ViewModel {
private String proxyAddress = "";
private String proxyPort = "";
public void updateProxy(MoneroApplication application) {
application.getExecutor().execute(() -> {
boolean usesProxy = PrefService.getInstance().getBoolean(Constants.PREF_USES_TOR, false);
Node curretNode = PrefService.getInstance().getNode();
boolean isNodeLocalIp = curretNode.getAddress().startsWith("10.") || curretNode.getAddress().startsWith("192.168.") || curretNode.getAddress().equals("localhost") || curretNode.getAddress().equals("127.0.0.1");
import android.util.Patterns
import androidx.lifecycle.ViewModel
import net.mynero.wallet.MoneroApplication
import net.mynero.wallet.model.WalletManager
import net.mynero.wallet.service.PrefService
import net.mynero.wallet.util.Constants
class SettingsViewModel : ViewModel() {
private var proxyAddress = ""
private var proxyPort = ""
fun updateProxy(application: MoneroApplication) {
application.executor?.execute {
val usesProxy = PrefService.instance?.getBoolean(Constants.PREF_USES_TOR, false) == true
val curretNode = PrefService.instance?.node
val isNodeLocalIp =
curretNode?.address?.startsWith("10.") == true || curretNode?.address?.startsWith("192.168.") == true || curretNode?.address == "localhost" || curretNode?.address == "127.0.0.1"
if (!usesProxy || isNodeLocalIp) {
WalletManager.getInstance().setProxy("");
WalletManager.getInstance().getWallet().setProxy("");
return;
WalletManager.instance?.setProxy("")
WalletManager.instance?.wallet?.setProxy("")
return@execute
}
if (proxyAddress.isEmpty()) proxyAddress = "127.0.0.1";
if (proxyPort.isEmpty()) proxyPort = "9050";
boolean validIpAddress = Patterns.IP_ADDRESS.matcher(proxyAddress).matches();
if (proxyAddress.isEmpty()) proxyAddress = "127.0.0.1"
if (proxyPort.isEmpty()) proxyPort = "9050"
val validIpAddress = Patterns.IP_ADDRESS.matcher(proxyAddress).matches()
if (validIpAddress) {
String proxy = proxyAddress + ":" + proxyPort;
PrefService.getInstance().edit().putString(Constants.PREF_PROXY, proxy).apply();
WalletManager.getInstance().setProxy(proxy);
WalletManager.getInstance().getWallet().setProxy(proxy);
val proxy = "$proxyAddress:$proxyPort"
PrefService.instance?.edit()?.putString(Constants.PREF_PROXY, proxy)?.apply()
WalletManager.instance?.setProxy(proxy)
WalletManager.instance?.wallet?.setProxy(proxy)
}
}
});
}
public void setProxyAddress(String address) {
this.proxyAddress = address;
fun setProxyAddress(address: String) {
proxyAddress = address
}
public void setProxyPort(String port) {
this.proxyPort = port;
fun setProxyPort(port: String) {
proxyPort = port
}
}

View file

@ -1,162 +1,167 @@
package net.mynero.wallet.fragment.transaction;
package net.mynero.wallet.fragment.transaction
import static net.mynero.wallet.util.DateHelper.DATETIME_FORMATTER;
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import net.mynero.wallet.R
import net.mynero.wallet.model.TransactionInfo
import net.mynero.wallet.model.WalletManager
import net.mynero.wallet.service.HistoryService
import net.mynero.wallet.service.PrefService
import net.mynero.wallet.util.Constants
import net.mynero.wallet.util.DateHelper
import net.mynero.wallet.util.Helper
import java.util.Calendar
import java.util.Date
import java.util.Objects
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import net.mynero.wallet.R;
import net.mynero.wallet.model.TransactionInfo;
import net.mynero.wallet.model.Wallet;
import net.mynero.wallet.model.WalletManager;
import net.mynero.wallet.service.HistoryService;
import net.mynero.wallet.service.PrefService;
import net.mynero.wallet.util.Constants;
import net.mynero.wallet.util.Helper;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.TimeZone;
public class TransactionFragment extends Fragment {
private TransactionViewModel mViewModel;
private TransactionInfo transactionInfo = null;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_transaction, container, false);
class TransactionFragment : Fragment() {
private var mViewModel: TransactionViewModel? = null
private var transactionInfo: TransactionInfo? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_transaction, container, false)
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Calendar cal = Calendar.getInstance();
TimeZone tz = cal.getTimeZone(); //get the local time zone.
DATETIME_FORMATTER.setTimeZone(tz);
mViewModel = new ViewModelProvider(this).get(TransactionViewModel.class);
Bundle args = getArguments();
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val cal = Calendar.getInstance()
val tz = cal.timeZone //get the local time zone.
DateHelper.DATETIME_FORMATTER.timeZone = tz
mViewModel = ViewModelProvider(this)[TransactionViewModel::class.java]
val args = arguments
if (args != null) {
this.transactionInfo = getArguments().getParcelable(Constants.NAV_ARG_TXINFO);
transactionInfo = arguments?.getParcelable(Constants.NAV_ARG_TXINFO)
}
bindObservers(view)
bindListeners(view)
}
bindObservers(view);
bindListeners(view);
}
private void bindListeners(View view) {
ImageButton copyTxHashImageButton = view.findViewById(R.id.copy_txhash_imagebutton);
copyTxHashImageButton.setOnClickListener(view1 -> {
TransactionInfo txInfo = this.transactionInfo;
private fun bindListeners(view: View) {
val copyTxHashImageButton = view.findViewById<ImageButton>(R.id.copy_txhash_imagebutton)
copyTxHashImageButton.setOnClickListener {
val txInfo = transactionInfo
if (txInfo != null) {
Helper.clipBoardCopy(getContext(), "transaction_hash", txInfo.hash);
Helper.clipBoardCopy(context, "transaction_hash", txInfo.hash)
}
});
ImageButton copyTxAddressImageButton = view.findViewById(R.id.copy_txaddress_imagebutton);
TextView addressTextView = view.findViewById(R.id.transaction_address_textview);
copyTxAddressImageButton.setOnClickListener(view1 -> {
TransactionInfo txInfo = this.transactionInfo;
}
val copyTxAddressImageButton =
view.findViewById<ImageButton>(R.id.copy_txaddress_imagebutton)
val addressTextView = view.findViewById<TextView>(R.id.transaction_address_textview)
copyTxAddressImageButton.setOnClickListener {
val txInfo = transactionInfo
if (txInfo != null) {
String destination = addressTextView.getText().toString();
Helper.clipBoardCopy(getContext(), "transaction_address", destination);
val destination = addressTextView.text.toString()
Helper.clipBoardCopy(context, "transaction_address", destination)
}
}
});
}
private void bindObservers(View view) {
TextView txActionTextView = view.findViewById(R.id.transaction_action_textview);
TextView confLabel2 = view.findViewById(R.id.transaction_conf_label2_textview);
TextView txHashTextView = view.findViewById(R.id.transaction_hash_textview);
TextView txConfTextView = view.findViewById(R.id.transaction_conf_textview);
TextView txAddressTextView = view.findViewById(R.id.transaction_address_textview);
ImageButton copyTxAddressImageButton = view.findViewById(R.id.copy_txaddress_imagebutton);
TextView txDateTextView = view.findViewById(R.id.transaction_date_textview);
TextView txAmountTextView = view.findViewById(R.id.transaction_amount_textview);
TextView blockHeightTextView = view.findViewById(R.id.tx_block_height_textview);
HistoryService.getInstance().history.observe(getViewLifecycleOwner(), transactionInfos -> {
TransactionInfo newTransactionInfo = findNewestVersionOfTransaction(this.transactionInfo, transactionInfos);
if (newTransactionInfo == null) return;
txHashTextView.setText(newTransactionInfo.hash);
txConfTextView.setText("" + newTransactionInfo.confirmations);
txDateTextView.setText(getDateTime(newTransactionInfo.timestamp));
private fun bindObservers(view: View) {
val txActionTextView = view.findViewById<TextView>(R.id.transaction_action_textview)
val confLabel2 = view.findViewById<TextView>(R.id.transaction_conf_label2_textview)
val txHashTextView = view.findViewById<TextView>(R.id.transaction_hash_textview)
val txConfTextView = view.findViewById<TextView>(R.id.transaction_conf_textview)
val txAddressTextView = view.findViewById<TextView>(R.id.transaction_address_textview)
val copyTxAddressImageButton =
view.findViewById<ImageButton>(R.id.copy_txaddress_imagebutton)
val txDateTextView = view.findViewById<TextView>(R.id.transaction_date_textview)
val txAmountTextView = view.findViewById<TextView>(R.id.transaction_amount_textview)
val blockHeightTextView = view.findViewById<TextView>(R.id.tx_block_height_textview)
HistoryService.instance?.history?.observe(viewLifecycleOwner) { transactionInfos: List<TransactionInfo> ->
val newTransactionInfo = findNewestVersionOfTransaction(
transactionInfo, transactionInfos
)
?: return@observe
txHashTextView.text = newTransactionInfo.hash
txConfTextView.text = "${newTransactionInfo.confirmations}"
txDateTextView.text = getDateTime(newTransactionInfo.timestamp)
if (newTransactionInfo.confirmations > 1) {
confLabel2.setText(getString(R.string.transaction_conf_desc2_confirmed));
blockHeightTextView.setText("" + newTransactionInfo.blockheight);
blockHeightTextView.setVisibility(View.VISIBLE);
} else if (newTransactionInfo.confirmations == 1) {
confLabel2.setText(getString(R.string.transaction_conf_1_desc2_confirmed));
blockHeightTextView.setText("" + newTransactionInfo.blockheight);
blockHeightTextView.setVisibility(View.VISIBLE);
confLabel2.text = getString(R.string.transaction_conf_desc2_confirmed)
blockHeightTextView.text = "${newTransactionInfo.blockheight}"
blockHeightTextView.visibility = View.VISIBLE
} else if (newTransactionInfo.confirmations == 1L) {
confLabel2.text = getString(R.string.transaction_conf_1_desc2_confirmed)
blockHeightTextView.text = "${newTransactionInfo.blockheight}"
blockHeightTextView.visibility = View.VISIBLE
} else {
blockHeightTextView.setVisibility(View.GONE);
confLabel2.setText(getString(R.string.transaction_conf_desc2_unconfirmed));
blockHeightTextView.visibility = View.GONE
confLabel2.text = getString(R.string.transaction_conf_desc2_unconfirmed)
}
Context ctx = getContext();
val ctx = context
if (ctx != null) {
boolean streetModeEnabled = PrefService.getInstance().getBoolean(Constants.PREF_STREET_MODE, false);
String balanceString = streetModeEnabled ? Constants.STREET_MODE_BALANCE : Helper.getDisplayAmount(newTransactionInfo.amount, 12);
if (newTransactionInfo.direction == TransactionInfo.Direction.Direction_In) {
txActionTextView.setText(getString(R.string.transaction_action_recv));
txAmountTextView.setTextColor(ContextCompat.getColor(ctx, R.color.oled_positiveColor));
val streetModeEnabled =
PrefService.instance?.getBoolean(Constants.PREF_STREET_MODE, false) == true
val balanceString =
if (streetModeEnabled) Constants.STREET_MODE_BALANCE else Helper.getDisplayAmount(
newTransactionInfo.amount,
12
)
if (newTransactionInfo.direction === TransactionInfo.Direction.Direction_In) {
txActionTextView.text = getString(R.string.transaction_action_recv)
txAmountTextView.setTextColor(
ContextCompat.getColor(
ctx,
R.color.oled_positiveColor
)
)
} else {
txActionTextView.setText(getString(R.string.transaction_action_sent));
txAmountTextView.setTextColor(ContextCompat.getColor(ctx, R.color.oled_negativeColor));
txActionTextView.text = getString(R.string.transaction_action_sent)
txAmountTextView.setTextColor(
ContextCompat.getColor(
ctx,
R.color.oled_negativeColor
)
)
}
txAmountTextView.setText(balanceString);
txAmountTextView.text = balanceString
}
String destination = "-";
Wallet wallet = WalletManager.getInstance().getWallet();
var destination: String? = "-"
val wallet = WalletManager.instance?.wallet
if (newTransactionInfo.txKey == null) {
newTransactionInfo.txKey = wallet.getTxKey(newTransactionInfo.hash);
newTransactionInfo.txKey = wallet?.getTxKey(newTransactionInfo.hash)
}
if (newTransactionInfo.address == null && newTransactionInfo.direction == TransactionInfo.Direction.Direction_In) {
destination = wallet.getSubaddress(newTransactionInfo.accountIndex, newTransactionInfo.addressIndex);
} else if (newTransactionInfo.address != null && newTransactionInfo.direction == TransactionInfo.Direction.Direction_In) {
destination = newTransactionInfo.address;
} else if (newTransactionInfo.transfers != null && newTransactionInfo.direction == TransactionInfo.Direction.Direction_Out) {
if (newTransactionInfo.transfers.size() == 1) {
destination = newTransactionInfo.transfers.get(0).address;
if (newTransactionInfo.address == null && newTransactionInfo.direction === TransactionInfo.Direction.Direction_In) {
destination = wallet?.getSubaddress(
newTransactionInfo.accountIndex,
newTransactionInfo.addressIndex
)
} else if (newTransactionInfo.address != null && newTransactionInfo.direction === TransactionInfo.Direction.Direction_In) {
destination = newTransactionInfo.address
} else if (newTransactionInfo.transfers != null && newTransactionInfo.direction === TransactionInfo.Direction.Direction_Out) {
if (newTransactionInfo.transfers?.size == 1) {
destination = newTransactionInfo.transfers?.get(0)?.address
}
}
txAddressTextView.setText(Objects.requireNonNullElse(destination, "-"));
txAddressTextView.text = Objects.requireNonNullElse(destination, "-")
if (destination == null) {
copyTxAddressImageButton.setVisibility(View.INVISIBLE);
copyTxAddressImageButton.visibility = View.INVISIBLE
}
}
});
}
private TransactionInfo findNewestVersionOfTransaction(TransactionInfo oldTransactionInfo, List<TransactionInfo> transactionInfoList) {
for (TransactionInfo transactionInfo : transactionInfoList) {
if (transactionInfo.hash.equals(oldTransactionInfo.hash)) {
this.transactionInfo = transactionInfo;
return this.transactionInfo;
private fun findNewestVersionOfTransaction(
oldTransactionInfo: TransactionInfo?,
transactionInfoList: List<TransactionInfo>
): TransactionInfo? {
for (transactionInfo in transactionInfoList) {
if (transactionInfo.hash == oldTransactionInfo?.hash) {
this.transactionInfo = transactionInfo
return this.transactionInfo
}
}
return null;
return null
}
private String getDateTime(long time) {
return DATETIME_FORMATTER.format(new Date(time * 1000));
private fun getDateTime(time: Long): String {
return DateHelper.DATETIME_FORMATTER.format(Date(time * 1000))
}
}

View file

@ -1,6 +1,5 @@
package net.mynero.wallet.fragment.transaction;
package net.mynero.wallet.fragment.transaction
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModel
public class TransactionViewModel extends ViewModel {
}
class TransactionViewModel : ViewModel()

View file

@ -1,170 +1,166 @@
package net.mynero.wallet.fragment.utxos;
package net.mynero.wallet.fragment.utxos
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import net.mynero.wallet.R
import net.mynero.wallet.adapter.CoinsInfoAdapter
import net.mynero.wallet.adapter.CoinsInfoAdapter.CoinsInfoAdapterListener
import net.mynero.wallet.fragment.dialog.SendBottomSheetDialog
import net.mynero.wallet.model.CoinsInfo
import net.mynero.wallet.service.AddressService
import net.mynero.wallet.service.UTXOService
import net.mynero.wallet.util.MoneroThreadPoolExecutor
import net.mynero.wallet.util.UriData
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import net.mynero.wallet.R;
import net.mynero.wallet.adapter.CoinsInfoAdapter;
import net.mynero.wallet.fragment.dialog.SendBottomSheetDialog;
import net.mynero.wallet.model.CoinsInfo;
import net.mynero.wallet.service.AddressService;
import net.mynero.wallet.service.UTXOService;
import net.mynero.wallet.util.MoneroThreadPoolExecutor;
import net.mynero.wallet.util.UriData;
import java.util.ArrayList;
import java.util.HashMap;
public class UtxosFragment extends Fragment implements CoinsInfoAdapter.CoinsInfoAdapterListener, SendBottomSheetDialog.Listener {
private final CoinsInfoAdapter adapter = new CoinsInfoAdapter(this);
private UtxosViewModel mViewModel;
private Button sendUtxosButton;
private Button churnUtxosButton;
private Button freezeUtxosButton;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_utxos, container, false);
class UtxosFragment : Fragment(), CoinsInfoAdapterListener, SendBottomSheetDialog.Listener {
private val adapter = CoinsInfoAdapter(this)
private var mViewModel: UtxosViewModel? = null
private var sendUtxosButton: Button? = null
private var churnUtxosButton: Button? = null
private var freezeUtxosButton: Button? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_utxos, container, false)
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewModel = new ViewModelProvider(this).get(UtxosViewModel.class);
bindListeners(view);
bindObservers(view);
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mViewModel = ViewModelProvider(this)[UtxosViewModel::class.java]
bindListeners(view)
bindObservers(view)
}
private void bindListeners(View view) {
freezeUtxosButton = view.findViewById(R.id.freeze_utxos_button);
sendUtxosButton = view.findViewById(R.id.send_utxos_button);
churnUtxosButton = view.findViewById(R.id.churn_utxos_button);
sendUtxosButton.setVisibility(View.GONE);
churnUtxosButton.setVisibility(View.GONE);
freezeUtxosButton.setVisibility(View.GONE);
freezeUtxosButton.setOnClickListener(view1 -> {
Toast.makeText(getContext(), "Toggling freeze status, please wait.", Toast.LENGTH_SHORT).show();
MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR.execute(() -> {
UTXOService.getInstance().toggleFrozen(adapter.selectedUtxos);
getActivity().runOnUiThread(() -> {
adapter.clear();
sendUtxosButton.setVisibility(View.GONE);
churnUtxosButton.setVisibility(View.GONE);
freezeUtxosButton.setVisibility(View.GONE);
});
});
});
sendUtxosButton.setOnClickListener(view1 -> {
ArrayList<String> selectedKeyImages = new ArrayList<>();
for (CoinsInfo coinsInfo : adapter.selectedUtxos.values()) {
selectedKeyImages.add(coinsInfo.getKeyImage());
private fun bindListeners(view: View) {
freezeUtxosButton = view.findViewById(R.id.freeze_utxos_button)
sendUtxosButton = view.findViewById(R.id.send_utxos_button)
churnUtxosButton = view.findViewById(R.id.churn_utxos_button)
sendUtxosButton?.visibility = View.GONE
churnUtxosButton?.visibility = View.GONE
freezeUtxosButton?.visibility = View.GONE
freezeUtxosButton?.setOnClickListener {
Toast.makeText(context, "Toggling freeze status, please wait.", Toast.LENGTH_SHORT)
.show()
MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR?.execute {
UTXOService.instance?.toggleFrozen(adapter.selectedUtxos)
activity?.runOnUiThread {
adapter.clear()
sendUtxosButton?.visibility = View.GONE
churnUtxosButton?.visibility = View.GONE
freezeUtxosButton?.visibility = View.GONE
}
SendBottomSheetDialog sendDialog = new SendBottomSheetDialog();
sendDialog.listener = this;
sendDialog.selectedUtxos = selectedKeyImages;
sendDialog.show(getActivity().getSupportFragmentManager(), null);
});
churnUtxosButton.setOnClickListener(view1 -> {
ArrayList<String> selectedKeyImages = new ArrayList<>();
for (CoinsInfo coinsInfo : adapter.selectedUtxos.values()) {
selectedKeyImages.add(coinsInfo.getKeyImage());
}
SendBottomSheetDialog sendDialog = new SendBottomSheetDialog();
sendDialog.listener = this;
sendDialog.isChurning = true;
sendDialog.uriData = UriData.parse(AddressService.instance.currentSubaddress().address);
sendDialog.selectedUtxos = selectedKeyImages;
sendDialog.show(getActivity().getSupportFragmentManager(), null);
});
}
sendUtxosButton?.setOnClickListener {
val selectedKeyImages = ArrayList<String>()
for (coinsInfo in adapter.selectedUtxos.values) {
coinsInfo.keyImage?.let { keyImage -> selectedKeyImages.add(keyImage) }
}
activity?.supportFragmentManager?.let { fragmentManager ->
val sendDialog = SendBottomSheetDialog()
sendDialog.listener = this
sendDialog.selectedUtxos = selectedKeyImages
sendDialog.show(fragmentManager, null)
}
private void bindObservers(View view) {
RecyclerView utxosRecyclerView = view.findViewById(R.id.transaction_history_recyclerview);
UTXOService utxoService = UTXOService.getInstance();
utxosRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
utxosRecyclerView.setAdapter(adapter);
if (utxoService != null) {
utxoService.utxos.observe(getViewLifecycleOwner(), utxos -> {
HashMap<String, CoinsInfo> filteredUtxos = new HashMap<>();
for (CoinsInfo coinsInfo : utxos) {
if (!coinsInfo.isSpent()) {
filteredUtxos.put(coinsInfo.getPubKey(), coinsInfo);
}
churnUtxosButton?.setOnClickListener {
val selectedKeyImages = ArrayList<String>()
for (coinsInfo in adapter.selectedUtxos.values) {
coinsInfo.keyImage?.let { keyImage -> selectedKeyImages.add(keyImage) }
}
activity?.supportFragmentManager?.let { fragmentManager ->
val sendDialog = SendBottomSheetDialog()
sendDialog.listener = this
sendDialog.isChurning = true
sendDialog.uriData =
AddressService.instance?.currentSubaddress()?.address?.let { address ->
UriData.parse(address)
}
sendDialog.selectedUtxos = selectedKeyImages
sendDialog.show(fragmentManager, null)
}
}
}
private fun bindObservers(view: View) {
val utxosRecyclerView =
view.findViewById<RecyclerView>(R.id.transaction_history_recyclerview)
val utxoService = UTXOService.instance
utxosRecyclerView.layoutManager = LinearLayoutManager(activity)
utxosRecyclerView.adapter = adapter
utxoService?.utxos?.observe(viewLifecycleOwner) { utxos: List<CoinsInfo> ->
val filteredUtxos = HashMap<String?, CoinsInfo>()
for (coinsInfo in utxos) {
if (!coinsInfo.isSpent) {
filteredUtxos[coinsInfo.pubKey] = coinsInfo
}
}
if (filteredUtxos.isEmpty()) {
utxosRecyclerView.setVisibility(View.GONE);
utxosRecyclerView.visibility = View.GONE
} else {
adapter.submitList(filteredUtxos);
utxosRecyclerView.setVisibility(View.VISIBLE);
adapter.submitList(filteredUtxos)
utxosRecyclerView.visibility = View.VISIBLE
}
});
}
}
@Override
public void onUtxoSelected(CoinsInfo coinsInfo) {
boolean selected = adapter.contains(coinsInfo);
override fun onUtxoSelected(coinsInfo: CoinsInfo) {
val selected = adapter.contains(coinsInfo)
if (selected) {
adapter.deselectUtxo(coinsInfo);
adapter.deselectUtxo(coinsInfo)
} else {
adapter.selectUtxo(coinsInfo);
adapter.selectUtxo(coinsInfo)
}
boolean frozenExists = false, unfrozenExists = false, bothExist = false;
for (CoinsInfo selectedUtxo : adapter.selectedUtxos.values()) {
if (selectedUtxo.isFrozen() || UTXOService.getInstance().isCoinFrozen(selectedUtxo))
frozenExists = true;
var frozenExists = false
var unfrozenExists = false
for (selectedUtxo in adapter.selectedUtxos.values) {
if (selectedUtxo.isFrozen || UTXOService.instance?.isCoinFrozen(selectedUtxo) == true)
frozenExists = true
else {
unfrozenExists = true;
unfrozenExists = true
}
}
bothExist = frozenExists && unfrozenExists;
val bothExist: Boolean = frozenExists && unfrozenExists
if (adapter.selectedUtxos.isEmpty()) {
sendUtxosButton.setVisibility(View.GONE);
churnUtxosButton.setVisibility(View.GONE);
freezeUtxosButton.setVisibility(View.GONE);
freezeUtxosButton.setBackgroundResource(R.drawable.button_bg_left);
sendUtxosButton?.visibility = View.GONE
churnUtxosButton?.visibility = View.GONE
freezeUtxosButton?.visibility = View.GONE
freezeUtxosButton?.setBackgroundResource(R.drawable.button_bg_left)
} else {
if (frozenExists) {
freezeUtxosButton.setBackgroundResource(R.drawable.button_bg);
sendUtxosButton.setVisibility(View.GONE);
churnUtxosButton.setVisibility(View.GONE);
freezeUtxosButton?.setBackgroundResource(R.drawable.button_bg)
sendUtxosButton?.visibility = View.GONE
churnUtxosButton?.visibility = View.GONE
} else {
freezeUtxosButton.setBackgroundResource(R.drawable.button_bg_left);
sendUtxosButton.setVisibility(View.VISIBLE);
churnUtxosButton.setVisibility(View.VISIBLE);
freezeUtxosButton?.setBackgroundResource(R.drawable.button_bg_left)
sendUtxosButton?.visibility = View.VISIBLE
churnUtxosButton?.visibility = View.VISIBLE
}
freezeUtxosButton.setVisibility(View.VISIBLE);
freezeUtxosButton?.visibility = View.VISIBLE
}
if (bothExist) {
freezeUtxosButton.setText(R.string.toggle_freeze);
freezeUtxosButton?.setText(R.string.toggle_freeze)
} else if (frozenExists) {
freezeUtxosButton.setText(R.string.unfreeze);
freezeUtxosButton?.setText(R.string.unfreeze)
} else if (unfrozenExists) {
freezeUtxosButton.setText(R.string.freeze);
freezeUtxosButton?.setText(R.string.freeze)
}
}
@Override
public void onSentTransaction() {
adapter.clear();
churnUtxosButton.setVisibility(View.GONE);
sendUtxosButton.setVisibility(View.GONE);
freezeUtxosButton.setVisibility(View.GONE);
override fun onSentTransaction() {
adapter.clear()
churnUtxosButton?.visibility = View.GONE
sendUtxosButton?.visibility = View.GONE
freezeUtxosButton?.visibility = View.GONE
}
}

View file

@ -1,7 +1,5 @@
package net.mynero.wallet.fragment.utxos;
package net.mynero.wallet.fragment.utxos
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModel
public class UtxosViewModel extends ViewModel {
}
class UtxosViewModel : ViewModel()