diff --git a/app/src/main/java/net/mynero/wallet/fragment/dialog/SendBottomSheetDialog.kt b/app/src/main/java/net/mynero/wallet/fragment/dialog/SendBottomSheetDialog.kt index 60f3895..fcb06dc 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/dialog/SendBottomSheetDialog.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/dialog/SendBottomSheetDialog.kt @@ -2,6 +2,8 @@ package net.mynero.wallet.fragment.dialog import android.app.Activity import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -78,6 +80,7 @@ class SendBottomSheetDialog : BottomSheetDialogFragment() { private var scanAddressImageButton: ImageButton? = null private var feeRadioGroup: RadioGroup? = null private var donateTextView: TextView? = null + private var donatingTextView: TextView? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -103,11 +106,28 @@ class SendBottomSheetDialog : BottomSheetDialogFragment() { feeRadioGroupLabelTextView = view.findViewById(R.id.tx_fee_radiogroup_label_textview) selectedUtxosValueTextView = view.findViewById(R.id.selected_utxos_value_textview) donateTextView = view.findViewById(R.id.donate_label_textview) + donatingTextView = view.findViewById(R.id.donating_label_textview) donateTextView?.setOnClickListener { addressEditText?.setText( Constants.DONATE_ADDRESS ) } + donatingTextView?.setOnClickListener { + addressEditText?.setText("") + } + addressEditText?.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {} + override fun afterTextChanged(s: Editable) { + if (s.toString() == Constants.DONATE_ADDRESS) { + donatingTextView?.visibility = View.VISIBLE + donateTextView?.visibility = View.INVISIBLE + } else { + donatingTextView?.visibility = View.INVISIBLE + donateTextView?.visibility = View.VISIBLE + } + } + }) if (uriData != null) { addressEditText?.setText(uriData?.address) if (uriData?.hasAmount() == true) { diff --git a/app/src/main/java/net/mynero/wallet/fragment/send/SendFragment.kt b/app/src/main/java/net/mynero/wallet/fragment/send/SendFragment.kt index 6c7de0e..065662d 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/send/SendFragment.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/send/SendFragment.kt @@ -31,6 +31,7 @@ import net.mynero.wallet.model.PendingTransaction import net.mynero.wallet.model.Wallet import net.mynero.wallet.service.BalanceService import net.mynero.wallet.service.TxService +import net.mynero.wallet.util.Constants import net.mynero.wallet.util.Helper import net.mynero.wallet.util.UriData @@ -270,19 +271,22 @@ class SendFragment : Fragment() { val removeOutputImageButton = entryView.findViewById(R.id.remove_output_imagebutton) val addressField = entryView.findViewById(R.id.address_edittext) + val donateTextView = entryView.findViewById(R.id.donate_label) + val donatingTextView = entryView.findViewById(R.id.donating_label) + donateTextView.setOnClickListener { + addressField.setText( + Constants.DONATE_ADDRESS + ) + } + donatingTextView.setOnClickListener { + addressField.setText("") + } addressField.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) { + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {} + override fun afterTextChanged(s: Editable) { val currentOutputs: Int = destCount - val uriData = UriData.parse(editable.toString()) + val uriData = UriData.parse(s.toString()) if (uriData != null) { // we have valid address val hasPaymentId = uriData.hasPaymentId() @@ -302,6 +306,14 @@ class SendFragment : Fragment() { // when send-all is false and this is our only dest and address is invalid, then show add output button mViewModel?.setShowAddOutputButton(true) } + + if (s.toString() == Constants.DONATE_ADDRESS) { + donateTextView.visibility = View.INVISIBLE + donatingTextView.visibility = View.VISIBLE + } else { + donateTextView.visibility = View.VISIBLE + donatingTextView.visibility = View.INVISIBLE + } } }) entryView.findViewById(R.id.paste_amount_imagebutton) diff --git a/app/src/main/java/net/mynero/wallet/fragment/settings/SettingsFragment.kt b/app/src/main/java/net/mynero/wallet/fragment/settings/SettingsFragment.kt index 2bdd7e6..d5be296 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/settings/SettingsFragment.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/settings/SettingsFragment.kt @@ -47,7 +47,6 @@ class SettingsFragment : Fragment(), PasswordListener, NodeSelectionDialogListen private var selectNodeButton: Button? = null 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 @@ -68,7 +67,6 @@ class SettingsFragment : Fragment(), PasswordListener, NodeSelectionDialogListen selectNodeButton = view.findViewById(R.id.select_node_button) streetModeSwitch = view.findViewById(R.id.street_mode_switch) monerochanSwitch = view.findViewById(R.id.monerochan_switch) - donationSwitch = view.findViewById(R.id.donate_per_tx_switch) torSwitch = view.findViewById(R.id.tor_switch) val proxySettingsLayout = view.findViewById(R.id.wallet_proxy_settings_layout) walletProxyAddressEditText = view.findViewById(R.id.wallet_proxy_address_edittext) @@ -88,8 +86,6 @@ class SettingsFragment : Fragment(), PasswordListener, NodeSelectionDialogListen PrefService.instance?.getBoolean(Constants.PREF_STREET_MODE, false) == true monerochanSwitch?.isChecked = PrefService.instance?.getBoolean(Constants.PREF_MONEROCHAN, Constants.DEFAULT_PREF_MONEROCHAN) == true - donationSwitch?.isChecked = - PrefService.instance?.getBoolean(Constants.PREF_DONATE_PER_TX, false) == true useBundledTor?.isChecked = cachedUsingBundledTor torSwitch?.isChecked = cachedUsingProxy updateProxy(cachedProxyAddress, cachedProxyPort) @@ -110,10 +106,6 @@ class SettingsFragment : Fragment(), PasswordListener, NodeSelectionDialogListen } 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() diff --git a/app/src/main/java/net/mynero/wallet/service/MoneroHandlerThread.kt b/app/src/main/java/net/mynero/wallet/service/MoneroHandlerThread.kt index 39d40dd..50ce055 100644 --- a/app/src/main/java/net/mynero/wallet/service/MoneroHandlerThread.kt +++ b/app/src/main/java/net/mynero/wallet/service/MoneroHandlerThread.kt @@ -21,12 +21,9 @@ import net.mynero.wallet.model.PendingTransaction import net.mynero.wallet.model.TransactionOutput import net.mynero.wallet.model.Wallet import net.mynero.wallet.model.Wallet.Companion.getAmountFromString -import net.mynero.wallet.model.Wallet.Companion.getPaymentIdFromAddress import net.mynero.wallet.model.Wallet.ConnectionStatus import net.mynero.wallet.model.WalletListener import net.mynero.wallet.model.WalletManager -import net.mynero.wallet.util.Constants -import java.security.SecureRandom /** * Handy class for starting a new thread that has a looper. The looper can then be @@ -158,65 +155,7 @@ class MoneroHandlerThread(name: String, val listener: Listener?, wallet: Wallet) val address = dest.component1() return wallet.createSweepTransaction(address, feePriority, preferredInputs) } - val finalOutputs = maybeAddDonationOutputs(totalAmount, outputs, preferredInputs) - return wallet.createTransactionMultDest(finalOutputs, feePriority, preferredInputs) - } - - @Throws(Exception::class) - private fun maybeAddDonationOutputs( - amount: Long, - outputs: List, - preferredInputs: List - ): List { - val newOutputs = ArrayList(outputs) - val networkType = WalletManager.instance?.networkType ?: return newOutputs - val mainDestination = - outputs[0] // at this point, for now, we should only have one item in the list. TODO: add multi-dest/pay-to-many feature in the UI - val paymentId = - getPaymentIdFromAddress(mainDestination.destination, networkType.value) - val donatePerTx = PrefService.instance?.getBoolean(Constants.PREF_DONATE_PER_TX, false) - if (donatePerTx == true && paymentId?.isEmpty() == true) { // only attach donation when no payment id is needed (i.e. integrated address) - val rand = SecureRandom() - val randomDonatePct = getRandomDonateAmount( - 0.005f, - 0.03f - ) // occasionally attaches a 0.5% to 3% donation. It is random so that not even I know how much exactly you are sending. - /* - It's also not entirely "per tx". It won't always attach it so as to not have a consistently uncommon fingerprint on-chain. When it does attach a donation, - it will periodically split it up into multiple outputs instead of one. - */ - val attachDonationRoll = rand.nextInt(100) - if (attachDonationRoll > 90) { // 10% chance of being added - val splitDonationRoll = rand.nextInt(100) - val donateAmount = (amount * randomDonatePct).toLong() - if (splitDonationRoll > 50) { // 50% chance of being split - // split - val split = genRandomDonationSplit( - 1, - 4 - ) // splits into at most 4 outputs, for a total of 6 outputs in the transaction (real dest + change. we don't add donations to send-all/sweep transactions) - val splitAmount = donateAmount / split - for (i in 0 until split) { - // TODO this can be expanded upon into the future to perform an auto-splitting/auto-churning for the user if their wallet is fresh and has few utxos. - // randomly split between multiple wallets - val randomDonationAddress = rand.nextInt(Constants.DONATION_ADDRESSES.size) - val donationAddress = Constants.DONATION_ADDRESSES[randomDonationAddress] - newOutputs.add(TransactionOutput(donationAddress, splitAmount)) - } - } else { - // just add one output, for a total of 3 (real dest + change) - newOutputs.add(TransactionOutput(Constants.DONATE_ADDRESS, donateAmount)) - } - val total = amount + donateAmount - checkSelectedAmounts( - preferredInputs, - total, - false - ) // check that the selected UTXOs satisfy the new amount total - } - } - newOutputs.shuffle() // shuffle the outputs just in case. i think the monero library handles this for us anyway - return newOutputs + return wallet.createTransactionMultDest(outputs, feePriority, preferredInputs) } @Throws(Exception::class) @@ -240,16 +179,6 @@ class MoneroHandlerThread(name: String, val listener: Listener?, wallet: Wallet) return pendingTx.commit("", true) } - private fun getRandomDonateAmount(min: Float, max: Float): Float { - val rand = SecureRandom() - return rand.nextFloat() * (max - min) + min - } - - private fun genRandomDonationSplit(min: Int, max: Int): Int { - val rand = SecureRandom() - return rand.nextInt(max) + min - } - interface Listener { fun onRefresh(walletSynced: Boolean) fun onConnectionFail() diff --git a/app/src/main/java/net/mynero/wallet/service/UTXOService.kt b/app/src/main/java/net/mynero/wallet/service/UTXOService.kt index 1b77b94..3ea0e67 100644 --- a/app/src/main/java/net/mynero/wallet/service/UTXOService.kt +++ b/app/src/main/java/net/mynero/wallet/service/UTXOService.kt @@ -126,7 +126,7 @@ class UTXOService(thread: MoneroHandlerThread?) : ServiceBase(thread) { val destinations = ArrayList>() destinations.add( Pair( - "87MRtZPrWUCVUgcFHdsVb5MoZUcLtqfD3FvQVGwftFb8eSdMnE39JhAJcbuSW8X2vRaRsB9RQfuCpFciybJFHaz3QYPhCLw", + Constants.DONATE_ADDRESS, amount ) ) diff --git a/app/src/main/java/net/mynero/wallet/util/Constants.kt b/app/src/main/java/net/mynero/wallet/util/Constants.kt index be7e01d..a65dbd4 100644 --- a/app/src/main/java/net/mynero/wallet/util/Constants.kt +++ b/app/src/main/java/net/mynero/wallet/util/Constants.kt @@ -11,7 +11,6 @@ object Constants { const val PREF_USES_OFFSET = "pref_uses_offset" const val PREF_STREET_MODE = "pref_street_mode" const val PREF_MONEROCHAN = "pref_monerochan" - const val PREF_DONATE_PER_TX = "pref_donate_per_tx" const val PREF_FROZEN_COINS = "pref_frozen_coins" const val PREF_USE_BUNDLED_TOR = "pref_use_bundled_tor" @@ -23,11 +22,7 @@ object Constants { const val DEFAULT_PREF_MONEROCHAN = false - val DONATION_ADDRESSES = arrayOf( - "87BqQYkugEzh6Tuyotm2uc3DzJzKM6MuZaC161e6u1TsQxxPmXVPHpdNRyK47JY4d1hhbe25YVz4e9vTXCLDxvHkRXEAeBC", // primary Mysu Donation address - "89QoPxs4cQSGbJrJddwzV3Ca7s2gVYHE1Xd1hGZafuVJVyNKt2LCQhxUdBF57PemxQiX3dmGUZLRRAzfeYyh9pq3GiWsDVo", // second Mysu Donation address - "855acibBehTQJdC1f41BHWGq1FiQ5ztiAU7LiJgDUmmyJfDtRpJoo6Mc1en73duUScdeUYjLirACnZpv2C6pPbcZKgumdCS" // third Mysu Donation address - ) + // Donation address is also specified in strings.xml, it is used as a tooltip in address fields const val DONATE_ADDRESS = "87BqQYkugEzh6Tuyotm2uc3DzJzKM6MuZaC161e6u1TsQxxPmXVPHpdNRyK47JY4d1hhbe25YVz4e9vTXCLDxvHkRXEAeBC" } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index a96e711..c38d6dc 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -87,68 +87,20 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/display_seed_button" /> - - - - - - - - + app:layout_constraintTop_toBottomOf="@+id/display_utxos_button" /> + + + + + + Copied to clipboard Street mode (hide balances) Show Monerochan - Add occasional donation - When enabled, there is a 10% chance when sending coins to add a 0.5%-3% donation to Mysu. These transaction fingerprints and donation amounts are randomized to preserve anonymity and privacy. Display wallet keys SOCKS Proxy Failed to connect. Retrying… - 87MRtZPrWUCVUgcFHdsVb5MoZUcLtqfD3FvQVGwftFb8eSdMnE39JhAJcbuSW8X2vRaRsB9RQfuCpFciybJFHaz3QYPhCLw + 87BqQYkugEzh6Tuyotm2uc3DzJzKM6MuZaC161e6u1TsQxxPmXVPHpdNRyK47JY4d1hhbe25YVz4e9vTXCLDxvHkRXEAeBC 0.00 SENDING ALL Send @@ -131,6 +129,7 @@ #%1$d: %2$s Previous addresses Donate to Mysu + Donating to Mysu. Thank you! Transactions [ auth ] [ trusted ]