mirror of
https://codeberg.org/anoncontributorxmr/mysu.git
synced 2024-12-22 05:00:24 +00:00
Cleanup more Kotlin code, and convert SendFragment
This commit is contained in:
parent
3c52bd3a55
commit
d4cb982b94
28 changed files with 647 additions and 1009 deletions
|
@ -23,7 +23,6 @@ import net.mynero.wallet.util.UriData
|
|||
import java.io.File
|
||||
|
||||
class MainActivity : AppCompatActivity(), MoneroHandlerThread.Listener, PasswordListener {
|
||||
@JvmField
|
||||
val restartEvents: SingleLiveEvent<*> = SingleLiveEvent<Any?>()
|
||||
var thread: MoneroHandlerThread? = null
|
||||
private set
|
||||
|
|
|
@ -21,6 +21,7 @@ import android.view.ViewGroup
|
|||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import net.mynero.wallet.R
|
||||
import net.mynero.wallet.data.DefaultNodes
|
||||
|
@ -83,9 +84,19 @@ class NodeSelectionAdapter(val listener: NodeSelectionAdapterListener?) :
|
|||
val currentNode = PrefService.instance?.node
|
||||
val match = node == currentNode
|
||||
if (match) {
|
||||
itemView.setBackgroundColor(itemView.resources.getColor(R.color.oled_colorSecondary))
|
||||
itemView.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
itemView.context,
|
||||
R.color.oled_colorSecondary
|
||||
)
|
||||
)
|
||||
} else {
|
||||
itemView.setBackgroundColor(itemView.resources.getColor(android.R.color.transparent))
|
||||
itemView.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
itemView.context,
|
||||
android.R.color.transparent
|
||||
)
|
||||
)
|
||||
}
|
||||
val nodeNameTextView = itemView.findViewById<TextView>(R.id.node_name_textview)
|
||||
val nodeAddressTextView = itemView.findViewById<TextView>(R.id.node_uri_textview)
|
||||
|
|
|
@ -25,7 +25,6 @@ import net.mynero.wallet.data.Subaddress
|
|||
import net.mynero.wallet.service.PrefService
|
||||
import net.mynero.wallet.util.Constants
|
||||
import net.mynero.wallet.util.Helper
|
||||
import net.mynero.wallet.util.Helper.getDisplayAmount
|
||||
|
||||
class SubaddressAdapter(val listener: SubaddressAdapterListener?) :
|
||||
RecyclerView.Adapter<SubaddressAdapter.ViewHolder>() {
|
||||
|
@ -101,7 +100,7 @@ class SubaddressAdapter(val listener: SubaddressAdapterListener?) :
|
|||
} else {
|
||||
addressAmountTextView.text = itemView.context.getString(
|
||||
R.string.tx_list_amount_positive,
|
||||
getDisplayAmount(amount, Helper.DISPLAY_DIGITS_INFO)
|
||||
Helper.getDisplayAmount(amount, Helper.DISPLAY_DIGITS_INFO)
|
||||
)
|
||||
}
|
||||
} else addressAmountTextView.text = ""
|
||||
|
|
|
@ -27,8 +27,7 @@ import net.mynero.wallet.service.PrefService
|
|||
import net.mynero.wallet.util.Constants
|
||||
import net.mynero.wallet.util.DateHelper.DATETIME_FORMATTER
|
||||
import net.mynero.wallet.util.Helper
|
||||
import net.mynero.wallet.util.Helper.getDisplayAmount
|
||||
import net.mynero.wallet.util.ThemeHelper.getThemedColor
|
||||
import net.mynero.wallet.util.ThemeHelper
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
|
||||
|
@ -84,10 +83,10 @@ class TransactionInfoAdapter(val listener: TxInfoAdapterListener?) :
|
|||
private var amountTextView: TextView? = null
|
||||
|
||||
init {
|
||||
inboundColour = getThemedColor(view.context, R.attr.positiveColor)
|
||||
outboundColour = getThemedColor(view.context, R.attr.negativeColor)
|
||||
pendingColour = getThemedColor(view.context, R.attr.neutralColor)
|
||||
failedColour = getThemedColor(view.context, R.attr.neutralColor)
|
||||
inboundColour = ThemeHelper.getThemedColor(view.context, R.attr.positiveColor)
|
||||
outboundColour = ThemeHelper.getThemedColor(view.context, R.attr.negativeColor)
|
||||
pendingColour = ThemeHelper.getThemedColor(view.context, R.attr.neutralColor)
|
||||
failedColour = ThemeHelper.getThemedColor(view.context, R.attr.neutralColor)
|
||||
val cal = Calendar.getInstance()
|
||||
val tz = cal.timeZone //get the local time zone.
|
||||
DATETIME_FORMATTER.timeZone = tz
|
||||
|
@ -97,7 +96,7 @@ class TransactionInfoAdapter(val listener: TxInfoAdapterListener?) :
|
|||
val streetModeEnabled =
|
||||
PrefService.instance?.getBoolean(Constants.PREF_STREET_MODE, false)
|
||||
val displayAmount =
|
||||
if (streetModeEnabled == true) Constants.STREET_MODE_BALANCE else getDisplayAmount(
|
||||
if (streetModeEnabled == true) Constants.STREET_MODE_BALANCE else Helper.getDisplayAmount(
|
||||
txInfo.amount,
|
||||
Helper.DISPLAY_DIGITS_INFO
|
||||
)
|
||||
|
@ -108,7 +107,7 @@ class TransactionInfoAdapter(val listener: TxInfoAdapterListener?) :
|
|||
amountTextView = itemView.findViewById(R.id.tx_amount)
|
||||
itemView.findViewById<View>(R.id.tx_failed).visibility = View.GONE
|
||||
if (txInfo.isFailed) {
|
||||
(itemView.findViewById<View>(R.id.tx_amount) as TextView).text =
|
||||
amountTextView?.text =
|
||||
itemView.context.getString(R.string.tx_list_amount_negative, displayAmount)
|
||||
itemView.findViewById<View>(R.id.tx_failed).visibility = View.VISIBLE
|
||||
setTxColour(failedColour)
|
||||
|
@ -140,10 +139,10 @@ class TransactionInfoAdapter(val listener: TxInfoAdapterListener?) :
|
|||
confirmationsTextView.visibility = View.GONE
|
||||
}
|
||||
if (txInfo.direction == TransactionInfo.Direction.Direction_Out) {
|
||||
(itemView.findViewById<View>(R.id.tx_amount) as TextView).text =
|
||||
amountTextView?.text =
|
||||
itemView.context.getString(R.string.tx_list_amount_negative, displayAmount)
|
||||
} else {
|
||||
(itemView.findViewById<View>(R.id.tx_amount) as TextView).text =
|
||||
amountTextView?.text =
|
||||
itemView.context.getString(R.string.tx_list_amount_positive, displayAmount)
|
||||
}
|
||||
(itemView.findViewById<View>(R.id.tx_datetime) as TextView).text =
|
||||
|
|
|
@ -85,13 +85,13 @@ enum class DefaultNodes(
|
|||
"mainnet",
|
||||
"Criminales78.onion"
|
||||
),
|
||||
xmrfail(
|
||||
Xmrfail(
|
||||
"mxcd4577fldb3ppzy7obmmhnu3tf57gbcbd4qhwr2kxyjj2qi3dnbfqd.onion",
|
||||
18081,
|
||||
"mainnet",
|
||||
"xmrfail.onion"
|
||||
),
|
||||
boldsuck(
|
||||
Boldsuck(
|
||||
"6dsdenp6vjkvqzy4wzsnzn6wixkdzihx3khiumyzieauxuxslmcaeiad.onion",
|
||||
18081,
|
||||
"mainnet",
|
||||
|
|
|
@ -38,7 +38,6 @@ class Subaddress(
|
|||
) "#$addressIndex" else label
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val DEFAULT_LABEL_FORMATTER: Pattern =
|
||||
Pattern.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}:[0-9]{2}:[0-9]{2}$")
|
||||
}
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.mynero.wallet.data
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class UserNotes(txNotes: String?) {
|
||||
var txNotes: String? = ""
|
||||
|
||||
@JvmField
|
||||
var note: String? = ""
|
||||
|
||||
@JvmField
|
||||
var xmrtoTag: String? = null
|
||||
|
||||
@JvmField
|
||||
var xmrtoKey: String? = null
|
||||
|
||||
@JvmField
|
||||
var xmrtoAmount: String? = null // could be a double - but we are not doing any calculations
|
||||
var xmrtoCurrency: String? = null
|
||||
|
||||
@JvmField
|
||||
var xmrtoDestination: String? = null
|
||||
|
||||
init {
|
||||
this.txNotes = txNotes
|
||||
val p = Pattern.compile("^\\{([a-z]+)-(\\w{6,}),([0-9.]*)([A-Z]+),(\\w*)\\} ?(.*)")
|
||||
val m = p.matcher(txNotes)
|
||||
if (m.find()) {
|
||||
xmrtoTag = m.group(1)
|
||||
xmrtoKey = m.group(2)
|
||||
xmrtoAmount = m.group(3)
|
||||
xmrtoCurrency = m.group(4)
|
||||
xmrtoDestination = m.group(5)
|
||||
note = m.group(6)
|
||||
} else {
|
||||
note = txNotes
|
||||
}
|
||||
}
|
||||
|
||||
fun setNote(newNote: String?) {
|
||||
note = newNote ?: ""
|
||||
txNotes = buildTxNote()
|
||||
}
|
||||
|
||||
private fun buildTxNote(): String {
|
||||
val sb = StringBuilder()
|
||||
if (xmrtoKey != null) {
|
||||
require(!(xmrtoAmount == null || xmrtoDestination == null)) { "Broken notes" }
|
||||
sb.append("{")
|
||||
sb.append(xmrtoTag)
|
||||
sb.append("-")
|
||||
sb.append(xmrtoKey)
|
||||
sb.append(",")
|
||||
sb.append(xmrtoAmount)
|
||||
sb.append(xmrtoCurrency)
|
||||
sb.append(",")
|
||||
sb.append(xmrtoDestination)
|
||||
sb.append("}")
|
||||
if (note != null && note?.isNotEmpty() == true) sb.append(" ")
|
||||
}
|
||||
sb.append(note)
|
||||
return sb.toString()
|
||||
}
|
||||
}
|
|
@ -114,7 +114,8 @@ class AddNodeBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
Toast.makeText(context, "Node already exists", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
PrefService.instance?.edit()?.putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString())?.apply()
|
||||
PrefService.instance?.edit()?.putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString())
|
||||
?.apply()
|
||||
}
|
||||
|
||||
private fun addPasteListener(root: View, editText: EditText, layoutId: Int) {
|
||||
|
|
|
@ -17,9 +17,7 @@ import java.text.SimpleDateFormat
|
|||
import java.util.Locale
|
||||
|
||||
class EditAddressLabelBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
@JvmField
|
||||
var listener: LabelListener? = null
|
||||
@JvmField
|
||||
var addressIndex = 0
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.json.JSONObject
|
|||
class EditNodeBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
@JvmField
|
||||
var listener: EditNodeListener? = null
|
||||
|
||||
@JvmField
|
||||
var node: Node? = null
|
||||
override fun onCreateView(
|
||||
|
@ -95,8 +96,9 @@ class EditNodeBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
jsonObject.put("username", user)
|
||||
jsonObject.put("password", pass)
|
||||
}
|
||||
if (nodeAddr.contains(":") && !nodeAddr.startsWith("[") && !nodeAddr.endsWith("]")) nodeAddr =
|
||||
"[$nodeAddr]"
|
||||
if (nodeAddr.contains(":") && !nodeAddr.startsWith("[") && !nodeAddr.endsWith("]"))
|
||||
nodeAddr = "[$nodeAddr]"
|
||||
|
||||
jsonObject.put("host", nodeAddr)
|
||||
jsonObject.put("rpcPort", portString.toInt())
|
||||
jsonObject.put("network", "mainnet")
|
||||
|
|
|
@ -82,7 +82,8 @@ class NodeSelectionBottomSheetDialog : BottomSheetDialogFragment(), NodeSelectio
|
|||
}
|
||||
}
|
||||
}
|
||||
PrefService.instance?.edit()?.putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString())?.apply()
|
||||
PrefService.instance?.edit()?.putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString())
|
||||
?.apply()
|
||||
for (defaultNode in DefaultNodes.values()) {
|
||||
fromJson(defaultNode.json)?.let { nodes.add(it) }
|
||||
}
|
||||
|
@ -98,7 +99,8 @@ class NodeSelectionBottomSheetDialog : BottomSheetDialogFragment(), NodeSelectio
|
|||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
PrefService.instance?.edit()?.putString(Constants.PREF_NODE_2, node?.toJson().toString())?.apply()
|
||||
PrefService.instance?.edit()?.putString(Constants.PREF_NODE_2, node?.toJson().toString())
|
||||
?.apply()
|
||||
WalletManager.instance?.setDaemon(node)
|
||||
adapter?.updateSelectedNode()
|
||||
listener?.onNodeSelected()
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.io.File
|
|||
class PasswordBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
@JvmField
|
||||
var listener: PasswordListener? = null
|
||||
|
||||
@JvmField
|
||||
var cancelable = false
|
||||
override fun onCreateView(
|
||||
|
|
|
@ -14,7 +14,6 @@ import android.widget.Toast
|
|||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.google.zxing.client.android.Intents
|
||||
import com.journeyapps.barcodescanner.ScanContract
|
||||
|
@ -22,7 +21,6 @@ import com.journeyapps.barcodescanner.ScanIntentResult
|
|||
import com.journeyapps.barcodescanner.ScanOptions
|
||||
import net.mynero.wallet.MoneroApplication
|
||||
import net.mynero.wallet.R
|
||||
import net.mynero.wallet.model.BalanceInfo
|
||||
import net.mynero.wallet.model.PendingTransaction
|
||||
import net.mynero.wallet.model.Wallet
|
||||
import net.mynero.wallet.model.Wallet.Companion.getAmountFromString
|
||||
|
@ -40,10 +38,12 @@ import net.mynero.wallet.util.UriData.Companion.parse
|
|||
class SendBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
private val _sendingMax = MutableLiveData(false)
|
||||
private val _pendingTransaction = MutableLiveData<PendingTransaction?>(null)
|
||||
|
||||
@JvmField
|
||||
var selectedUtxos = ArrayList<String>()
|
||||
private var sendingMax: LiveData<Boolean?> = _sendingMax
|
||||
private var pendingTransaction: LiveData<PendingTransaction?> = _pendingTransaction
|
||||
|
||||
@JvmField
|
||||
var uriData: UriData? = null
|
||||
private val cameraPermissionsLauncher = registerForActivityResult(
|
||||
|
@ -56,8 +56,10 @@ class SendBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
@JvmField
|
||||
var isChurning = false
|
||||
|
||||
@JvmField
|
||||
var listener: Listener? = null
|
||||
var priority: PendingTransaction.Priority = PendingTransaction.Priority.Priority_Low
|
||||
|
|
|
@ -25,7 +25,6 @@ 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 kotlin.math.roundToInt
|
||||
|
||||
class HomeFragment : Fragment(), TxInfoAdapterListener {
|
||||
private var startHeight: Long = 0
|
||||
|
@ -124,11 +123,11 @@ class HomeFragment : Fragment(), TxInfoAdapterListener {
|
|||
displayEmptyHistory(true, view, textResId, botImgResId)
|
||||
} else {
|
||||
// POPULATED WALLET HISTORY
|
||||
history.sorted()
|
||||
if (history.size > 100) {
|
||||
adapter.submitList(history.subList(0, 99))
|
||||
val sortedHistory = history.sortedByDescending { it.timestamp }
|
||||
if (sortedHistory.size > 100) {
|
||||
adapter.submitList(sortedHistory.subList(0, 99))
|
||||
} else {
|
||||
adapter.submitList(history)
|
||||
adapter.submitList(sortedHistory)
|
||||
}
|
||||
txHistoryRecyclerView.visibility = View.VISIBLE
|
||||
displayEmptyHistory(
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.view.ViewGroup
|
|||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
|
@ -120,7 +121,8 @@ class ReceiveFragment : Fragment() {
|
|||
|
||||
private fun generate(text: String, width: Int, height: Int): Bitmap? {
|
||||
if (width <= 0 || height <= 0) return null
|
||||
val hints: MutableMap<EncodeHintType, Any?> = EnumMap(com.google.zxing.EncodeHintType::class.java)
|
||||
val hints: MutableMap<EncodeHintType, Any?> =
|
||||
EnumMap(com.google.zxing.EncodeHintType::class.java)
|
||||
hints[EncodeHintType.CHARACTER_SET] = StandardCharsets.UTF_8
|
||||
hints[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.M
|
||||
try {
|
||||
|
@ -131,7 +133,10 @@ class ReceiveFragment : Fragment() {
|
|||
if (bitMatrix[j, i]) {
|
||||
pixels[i * width + j] = -0x1
|
||||
} else {
|
||||
pixels[i * height + j] = resources.getColor(R.color.oled_colorBackground)
|
||||
context?.let { ctx ->
|
||||
pixels[i * height + j] =
|
||||
ContextCompat.getColor(ctx, R.color.oled_colorBackground)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,485 +1,520 @@
|
|||
package net.mynero.wallet.fragment.send;
|
||||
package net.mynero.wallet.fragment.send
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
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
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RadioGroup
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.google.zxing.client.android.Intents
|
||||
import com.journeyapps.barcodescanner.ScanContract
|
||||
import com.journeyapps.barcodescanner.ScanIntentResult
|
||||
import com.journeyapps.barcodescanner.ScanOptions
|
||||
import com.ncorti.slidetoact.SlideToActView
|
||||
import com.ncorti.slidetoact.SlideToActView.OnSlideCompleteListener
|
||||
import net.mynero.wallet.MoneroApplication
|
||||
import net.mynero.wallet.R
|
||||
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.Helper
|
||||
import net.mynero.wallet.util.UriData
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.google.zxing.client.android.Intents;
|
||||
import com.journeyapps.barcodescanner.ScanContract;
|
||||
import com.journeyapps.barcodescanner.ScanOptions;
|
||||
import com.ncorti.slidetoact.SlideToActView;
|
||||
|
||||
import net.mynero.wallet.MoneroApplication;
|
||||
import net.mynero.wallet.R;
|
||||
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.Helper;
|
||||
import net.mynero.wallet.util.UriData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import kotlin.Pair;
|
||||
|
||||
public class SendFragment extends Fragment {
|
||||
public PendingTransaction.Priority priority;
|
||||
private SendViewModel mViewModel;
|
||||
private Button sendMaxButton;
|
||||
private ImageButton addOutputImageView;
|
||||
private LinearLayout destList;
|
||||
private LayoutInflater inflater;
|
||||
private Button createButton;
|
||||
private SlideToActView sendTxSlider;
|
||||
private RadioGroup feeRadioGroup;
|
||||
private TextView feeRadioGroupLabelTextView;
|
||||
private TextView feeTextView;
|
||||
private TextView addressTextView;
|
||||
private TextView amountTextView;
|
||||
private int currentEntryIndex = -1;
|
||||
private final ActivityResultLauncher<ScanOptions> qrCodeLauncher = registerForActivityResult(new ScanContract(), result -> {
|
||||
if (result.getContents() != null) {
|
||||
if (currentEntryIndex != -1) {
|
||||
pasteAddress(getDestView(currentEntryIndex), result.getContents(), false);
|
||||
currentEntryIndex = -1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_send, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mViewModel = new ViewModelProvider(this).get(SendViewModel.class);
|
||||
sendMaxButton = view.findViewById(R.id.send_max_button);
|
||||
addOutputImageView = view.findViewById(R.id.add_output_button);
|
||||
destList = view.findViewById(R.id.transaction_destination_list);
|
||||
createButton = view.findViewById(R.id.create_tx_button);
|
||||
feeRadioGroup = view.findViewById(R.id.tx_fee_radiogroup);
|
||||
feeTextView = view.findViewById(R.id.fee_textview);
|
||||
sendTxSlider = view.findViewById(R.id.send_tx_slider);
|
||||
addressTextView = view.findViewById(R.id.address_pending_textview);
|
||||
amountTextView = view.findViewById(R.id.amount_pending_textview);
|
||||
feeRadioGroup = view.findViewById(R.id.tx_fee_radiogroup);
|
||||
feeRadioGroupLabelTextView = view.findViewById(R.id.tx_fee_radiogroup_label_textview);
|
||||
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity != null) {
|
||||
inflater = activity.getLayoutInflater();
|
||||
}
|
||||
bindListeners();
|
||||
bindObservers();
|
||||
init();
|
||||
} private final ActivityResultLauncher<String> cameraPermissionsLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(),
|
||||
granted -> {
|
||||
if (granted) {
|
||||
onScan(currentEntryIndex);
|
||||
} else {
|
||||
Toast.makeText(getActivity(), getString(R.string.no_camera_permission), Toast.LENGTH_SHORT).show();
|
||||
currentEntryIndex = -1;
|
||||
class SendFragment : Fragment() {
|
||||
var priority: PendingTransaction.Priority = PendingTransaction.Priority.Priority_Low
|
||||
private var mViewModel: SendViewModel? = null
|
||||
private var sendMaxButton: Button? = null
|
||||
private var addOutputImageView: ImageButton? = null
|
||||
private var destList: LinearLayout? = null
|
||||
private var inflater: LayoutInflater? = null
|
||||
private var createButton: Button? = null
|
||||
private var sendTxSlider: SlideToActView? = null
|
||||
private var feeRadioGroup: RadioGroup? = null
|
||||
private var feeRadioGroupLabelTextView: TextView? = null
|
||||
private var feeTextView: TextView? = null
|
||||
private var addressTextView: TextView? = null
|
||||
private var amountTextView: TextView? = null
|
||||
private var currentEntryIndex = -1
|
||||
private val qrCodeLauncher =
|
||||
registerForActivityResult(ScanContract()) { result: ScanIntentResult ->
|
||||
if (result.contents != null) {
|
||||
if (currentEntryIndex != -1) {
|
||||
pasteAddress(getDestView(currentEntryIndex), result.contents, false)
|
||||
currentEntryIndex = -1
|
||||
}
|
||||
});
|
||||
|
||||
private void init() {
|
||||
addOutput(true);
|
||||
}
|
||||
}
|
||||
private val cameraPermissionsLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { granted: Boolean ->
|
||||
if (granted) {
|
||||
onScan(currentEntryIndex)
|
||||
} else {
|
||||
Toast.makeText(activity, getString(R.string.no_camera_permission), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
currentEntryIndex = -1
|
||||
}
|
||||
}
|
||||
|
||||
private void bindListeners() {
|
||||
feeRadioGroup.check(R.id.low_fee_radiobutton);
|
||||
priority = PendingTransaction.Priority.Priority_Low;
|
||||
feeRadioGroup.setOnCheckedChangeListener((radioGroup, i) -> {
|
||||
if (i == R.id.low_fee_radiobutton) {
|
||||
priority = PendingTransaction.Priority.Priority_Low;
|
||||
} else if (i == R.id.med_fee_radiobutton) {
|
||||
priority = PendingTransaction.Priority.Priority_Medium;
|
||||
} else if (i == R.id.high_fee_radiobutton) {
|
||||
priority = PendingTransaction.Priority.Priority_High;
|
||||
}
|
||||
});
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_send, container, false)
|
||||
}
|
||||
|
||||
addOutputImageView.setOnClickListener(view1 -> {
|
||||
sendMaxButton.setVisibility(View.GONE);
|
||||
int outputCount = getDestCount();
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mViewModel = ViewModelProvider(this)[SendViewModel::class.java]
|
||||
sendMaxButton = view.findViewById(R.id.send_max_button)
|
||||
addOutputImageView = view.findViewById(R.id.add_output_button)
|
||||
destList = view.findViewById(R.id.transaction_destination_list)
|
||||
createButton = view.findViewById(R.id.create_tx_button)
|
||||
feeRadioGroup = view.findViewById(R.id.tx_fee_radiogroup)
|
||||
feeTextView = view.findViewById(R.id.fee_textview)
|
||||
sendTxSlider = view.findViewById(R.id.send_tx_slider)
|
||||
addressTextView = view.findViewById(R.id.address_pending_textview)
|
||||
amountTextView = view.findViewById(R.id.amount_pending_textview)
|
||||
feeRadioGroup = view.findViewById(R.id.tx_fee_radiogroup)
|
||||
feeRadioGroupLabelTextView = view.findViewById(R.id.tx_fee_radiogroup_label_textview)
|
||||
inflater = activity?.layoutInflater
|
||||
bindListeners()
|
||||
bindObservers()
|
||||
init()
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
addOutput(true)
|
||||
}
|
||||
|
||||
private fun bindListeners() {
|
||||
feeRadioGroup?.check(R.id.low_fee_radiobutton)
|
||||
priority = PendingTransaction.Priority.Priority_Low
|
||||
feeRadioGroup?.setOnCheckedChangeListener { _: RadioGroup?, i: Int ->
|
||||
when (i) {
|
||||
R.id.low_fee_radiobutton -> priority = PendingTransaction.Priority.Priority_Low
|
||||
R.id.med_fee_radiobutton -> priority = PendingTransaction.Priority.Priority_Medium
|
||||
R.id.high_fee_radiobutton -> priority = PendingTransaction.Priority.Priority_High
|
||||
}
|
||||
}
|
||||
addOutputImageView?.setOnClickListener {
|
||||
sendMaxButton?.visibility = View.GONE
|
||||
val outputCount = destCount
|
||||
if (outputCount < 8) {
|
||||
addOutput(false);
|
||||
addOutput(false)
|
||||
} else {
|
||||
Toast.makeText(getActivity(), getString(R.string.max_outputs_allowed), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.max_outputs_allowed),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
});
|
||||
sendMaxButton.setOnClickListener(view1 -> {
|
||||
mViewModel.setSendingMax(!isSendAll());
|
||||
});
|
||||
createButton.setOnClickListener(view1 -> {
|
||||
boolean outputsValid = checkDestsValidity(isSendAll());
|
||||
|
||||
}
|
||||
sendMaxButton?.setOnClickListener { mViewModel?.setSendingMax(!isSendAll) }
|
||||
createButton?.setOnClickListener {
|
||||
val outputsValid = checkDestsValidity(isSendAll)
|
||||
if (outputsValid) {
|
||||
Toast.makeText(getActivity(), getString(R.string.creating_tx), Toast.LENGTH_SHORT).show();
|
||||
createButton.setEnabled(false);
|
||||
sendMaxButton.setEnabled(false);
|
||||
createTx(getRawDests(), isSendAll(), priority);
|
||||
Toast.makeText(activity, getString(R.string.creating_tx), Toast.LENGTH_SHORT).show()
|
||||
createButton?.isEnabled = false
|
||||
sendMaxButton?.isEnabled = false
|
||||
createTx(rawDests, isSendAll, priority)
|
||||
} else {
|
||||
Toast.makeText(getActivity(), getString(R.string.creating_tx_failed_invalid_outputs), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.creating_tx_failed_invalid_outputs),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
});
|
||||
|
||||
sendTxSlider.setOnSlideCompleteListener(slideToActView -> {
|
||||
PendingTransaction pendingTx = mViewModel.pendingTransaction.getValue();
|
||||
if (pendingTx != null) {
|
||||
Toast.makeText(getActivity(), getString(R.string.sending_tx), Toast.LENGTH_SHORT).show();
|
||||
sendTx(pendingTx);
|
||||
}
|
||||
sendTxSlider?.onSlideCompleteListener =
|
||||
object : OnSlideCompleteListener {
|
||||
override fun onSlideComplete(view: SlideToActView) {
|
||||
val pendingTx = mViewModel?.pendingTransaction?.value ?: return
|
||||
Toast.makeText(activity, getString(R.string.sending_tx), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
sendTx(pendingTx)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean checkDestsValidity(boolean sendAll) {
|
||||
List<Pair<String, String>> dests = getRawDests();
|
||||
for (Pair<String, String> dest : dests) {
|
||||
String address = dest.component1();
|
||||
String amount = dest.component2();
|
||||
private fun checkDestsValidity(sendAll: Boolean): Boolean {
|
||||
val dests = rawDests
|
||||
for (dest in dests) {
|
||||
val address = dest.component1()
|
||||
val amount = dest.component2()
|
||||
if (!sendAll) {
|
||||
if (amount.isEmpty()) {
|
||||
Toast.makeText(getActivity(), getString(R.string.send_amount_empty), Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.send_amount_empty),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return false
|
||||
}
|
||||
|
||||
long amountRaw = Wallet.getAmountFromString(amount);
|
||||
long balance = BalanceService.instance.getUnlockedBalanceRaw();
|
||||
val amountRaw = Wallet.getAmountFromString(amount)
|
||||
val balance = BalanceService.instance?.unlockedBalanceRaw ?: 0
|
||||
if (amountRaw >= balance || amountRaw <= 0) {
|
||||
Toast.makeText(getActivity(), getString(R.string.send_amount_invalid), Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.send_amount_invalid),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return false
|
||||
}
|
||||
} else if (dests.size() > 1) {
|
||||
Toast.makeText(getActivity(), getString(R.string.send_amount_invalid_sendall_paytomany), Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
} else if (dests.size > 1) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.send_amount_invalid_sendall_paytomany),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return false
|
||||
}
|
||||
|
||||
UriData uriData = UriData.parse(address);
|
||||
boolean isValidAddress = uriData != null;
|
||||
val uriData = UriData.parse(address)
|
||||
val isValidAddress = uriData != null
|
||||
if (!isValidAddress) {
|
||||
Toast.makeText(getActivity(), getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.send_address_invalid),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return false
|
||||
}
|
||||
|
||||
if (dests.size() > 1 && uriData.hasPaymentId()) {
|
||||
Toast.makeText(getActivity(), getString(R.string.paymentid_paytomany), Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
if (dests.size > 1 && uriData?.hasPaymentId() == true) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.paymentid_paytomany),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
private boolean destsHasPaymentId() {
|
||||
List<Pair<String, String>> dests = getRawDests();
|
||||
for (Pair<String, String> dest : dests) {
|
||||
String address = dest.component1();
|
||||
UriData uriData = UriData.parse(address);
|
||||
if (uriData == null) return false;
|
||||
if (uriData.hasPaymentId()) return true;
|
||||
private fun destsHasPaymentId(): Boolean {
|
||||
val dests = rawDests
|
||||
for (dest in dests) {
|
||||
val address = dest.component1()
|
||||
val uriData = UriData.parse(address) ?: return false
|
||||
if (uriData.hasPaymentId()) return true
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
private void bindObservers() {
|
||||
mViewModel.sendingMax.observe(getViewLifecycleOwner(), sendingMax -> {
|
||||
if (mViewModel.pendingTransaction.getValue() == null) {
|
||||
if (sendingMax) {
|
||||
prepareOutputsForMaxSend();
|
||||
sendMaxButton.setText(getText(R.string.undo));
|
||||
private fun bindObservers() {
|
||||
mViewModel?.sendingMax?.observe(viewLifecycleOwner) { sendingMax: Boolean? ->
|
||||
if (mViewModel?.pendingTransaction?.value == null) {
|
||||
if (sendingMax == true) {
|
||||
prepareOutputsForMaxSend()
|
||||
sendMaxButton?.text = getText(R.string.undo)
|
||||
} else {
|
||||
unprepareMaxSend();
|
||||
sendMaxButton.setText(getText(R.string.send_max));
|
||||
unprepareMaxSend()
|
||||
sendMaxButton?.text = getText(R.string.send_max)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mViewModel.showAddOutputButton.observe(getViewLifecycleOwner(), show -> {
|
||||
setAddOutputButtonVisibility((show && !destsHasPaymentId()) ? View.VISIBLE : View.INVISIBLE);
|
||||
});
|
||||
|
||||
mViewModel.pendingTransaction.observe(getViewLifecycleOwner(), pendingTx -> {
|
||||
showConfirmationLayout(pendingTx != null);
|
||||
|
||||
}
|
||||
mViewModel?.showAddOutputButton?.observe(viewLifecycleOwner) { show: Boolean? ->
|
||||
setAddOutputButtonVisibility(
|
||||
if (show == true && !destsHasPaymentId()) View.VISIBLE else View.INVISIBLE
|
||||
)
|
||||
}
|
||||
mViewModel?.pendingTransaction?.observe(viewLifecycleOwner) { pendingTx: PendingTransaction? ->
|
||||
showConfirmationLayout(pendingTx != null)
|
||||
if (pendingTx != null) {
|
||||
String address = getDestCount() == 1 ? getAddressField(0).getText().toString() : "Multiple";
|
||||
addressTextView.setText(getString(R.string.tx_address_text, address));
|
||||
amountTextView.setText(getString(R.string.tx_amount_text, Helper.getDisplayAmount(pendingTx.getAmount())));
|
||||
feeTextView.setText(getString(R.string.tx_fee_text, Helper.getDisplayAmount(pendingTx.getFee())));
|
||||
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()))
|
||||
feeTextView?.text =
|
||||
getString(R.string.tx_fee_text, Helper.getDisplayAmount(pendingTx.getFee()))
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void addOutput(boolean initial) {
|
||||
private fun addOutput(initial: Boolean) {
|
||||
if (inflater != null) {
|
||||
int index = getDestCount();
|
||||
ConstraintLayout entryView = (ConstraintLayout) inflater.inflate(R.layout.transaction_output_item, null);
|
||||
ImageButton removeOutputImageButton = entryView.findViewById(R.id.remove_output_imagebutton);
|
||||
EditText addressField = entryView.findViewById(R.id.address_edittext);
|
||||
addressField.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
val index = destCount
|
||||
val entryView =
|
||||
inflater?.inflate(R.layout.transaction_output_item, null) as ConstraintLayout
|
||||
val removeOutputImageButton =
|
||||
entryView.findViewById<ImageButton>(R.id.remove_output_imagebutton)
|
||||
val addressField = entryView.findViewById<EditText>(R.id.address_edittext)
|
||||
addressField.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(
|
||||
charSequence: CharSequence,
|
||||
i: Int,
|
||||
i1: Int,
|
||||
i2: Int
|
||||
) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
int currentOutputs = getDestCount();
|
||||
UriData uriData = UriData.parse(editable.toString());
|
||||
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||
override fun afterTextChanged(editable: Editable) {
|
||||
val currentOutputs: Int = destCount
|
||||
val uriData = UriData.parse(editable.toString())
|
||||
if (uriData != null) {
|
||||
// we have valid address
|
||||
boolean hasPaymentId = uriData.hasPaymentId();
|
||||
val hasPaymentId = uriData.hasPaymentId()
|
||||
if (currentOutputs > 1 && hasPaymentId) {
|
||||
// multiple outputs when pasting/editing in integrated address. this is not allowed
|
||||
Toast.makeText(getActivity(), getString(R.string.paymentid_paytomany), Toast.LENGTH_SHORT).show();
|
||||
addressField.setText(null);
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.paymentid_paytomany),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
addressField.text = null
|
||||
} else if (currentOutputs == 1 && hasPaymentId) {
|
||||
// show add output button: we are sending to integrated address
|
||||
mViewModel.setShowAddOutputButton(false);
|
||||
mViewModel?.setShowAddOutputButton(false)
|
||||
}
|
||||
} else if (currentOutputs == 1 && !isSendAll()) {
|
||||
} else if (currentOutputs == 1 && !isSendAll) {
|
||||
// when send-all is false and this is our only dest and address is invalid, then show add output button
|
||||
mViewModel.setShowAddOutputButton(true);
|
||||
mViewModel?.setShowAddOutputButton(true)
|
||||
}
|
||||
}
|
||||
});
|
||||
entryView.findViewById(R.id.paste_amount_imagebutton).setOnClickListener(view1 -> {
|
||||
Context ctx = getContext();
|
||||
if (ctx != null) {
|
||||
String clipboard = Helper.getClipBoardText(ctx);
|
||||
if (clipboard != null) {
|
||||
pasteAddress(entryView, clipboard, true);
|
||||
})
|
||||
entryView.findViewById<View>(R.id.paste_amount_imagebutton)
|
||||
.setOnClickListener {
|
||||
val ctx = context
|
||||
if (ctx != null) {
|
||||
val clipboard = Helper.getClipBoardText(ctx)
|
||||
if (clipboard != null) {
|
||||
pasteAddress(entryView, clipboard, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
entryView.findViewById(R.id.paste_address_imagebutton).setOnClickListener(view1 -> {
|
||||
Context ctx = getContext();
|
||||
if (ctx != null) {
|
||||
String clipboard = Helper.getClipBoardText(ctx);
|
||||
if (clipboard != null) {
|
||||
pasteAddress(entryView, clipboard, false);
|
||||
entryView.findViewById<View>(R.id.paste_address_imagebutton)
|
||||
.setOnClickListener {
|
||||
val ctx = context
|
||||
if (ctx != null) {
|
||||
val clipboard = Helper.getClipBoardText(ctx)
|
||||
if (clipboard != null) {
|
||||
pasteAddress(entryView, clipboard, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
entryView.findViewById(R.id.scan_address_imagebutton).setOnClickListener(view -> onScan(index));
|
||||
entryView.findViewById<View>(R.id.scan_address_imagebutton)
|
||||
.setOnClickListener { onScan(index) }
|
||||
if (initial) {
|
||||
removeOutputImageButton.setVisibility(View.INVISIBLE);
|
||||
removeOutputImageButton.visibility = View.INVISIBLE
|
||||
} else {
|
||||
removeOutputImageButton.setOnClickListener(view -> {
|
||||
int currentCount = getDestCount();
|
||||
removeOutputImageButton.setOnClickListener {
|
||||
val currentCount = destCount
|
||||
if (currentCount > 1) {
|
||||
if (currentCount == 2) {
|
||||
sendMaxButton.setVisibility(View.VISIBLE);
|
||||
sendMaxButton?.visibility = View.VISIBLE
|
||||
}
|
||||
destList.removeView(entryView);
|
||||
destList?.removeView(entryView)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
destList.addView(entryView);
|
||||
destList?.addView(entryView)
|
||||
}
|
||||
}
|
||||
|
||||
private int getDestCount() {
|
||||
return destList.getChildCount();
|
||||
}
|
||||
|
||||
private List<Pair<String, String>> getRawDests() {
|
||||
ArrayList<Pair<String, String>> dests = new ArrayList<>();
|
||||
for (int i = 0; i < getDestCount(); i++) {
|
||||
ConstraintLayout entryView = getDestView(i);
|
||||
EditText amountField = entryView.findViewById(R.id.amount_edittext);
|
||||
EditText addressField = entryView.findViewById(R.id.address_edittext);
|
||||
String amount = amountField.getText().toString().trim();
|
||||
String address = addressField.getText().toString().trim();
|
||||
dests.add(new Pair<>(address, amount));
|
||||
private val destCount: Int
|
||||
get() = destList?.childCount ?: -1
|
||||
private val rawDests: List<Pair<String, String>>
|
||||
get() {
|
||||
val dests = ArrayList<Pair<String, String>>()
|
||||
for (i in 0 until destCount) {
|
||||
val entryView = getDestView(i)
|
||||
val amountField = entryView.findViewById<EditText>(R.id.amount_edittext)
|
||||
val addressField = entryView.findViewById<EditText>(R.id.address_edittext)
|
||||
val amount = amountField.text.toString().trim { it <= ' ' }
|
||||
val address = addressField.text.toString().trim { it <= ' ' }
|
||||
dests.add(Pair(address, amount))
|
||||
}
|
||||
return dests
|
||||
}
|
||||
private val isSendAll: Boolean
|
||||
get() = mViewModel?.sendingMax?.value ?: false
|
||||
|
||||
return dests;
|
||||
private fun getDestView(pos: Int): ConstraintLayout {
|
||||
return destList?.getChildAt(pos) as ConstraintLayout
|
||||
}
|
||||
|
||||
private boolean isSendAll() {
|
||||
return mViewModel.sendingMax.getValue() != null ? mViewModel.sendingMax.getValue() : false;
|
||||
private fun getAddressField(pos: Int): EditText {
|
||||
return getDestView(pos).findViewById<View>(R.id.address_edittext) as EditText
|
||||
}
|
||||
|
||||
private ConstraintLayout getDestView(int pos) {
|
||||
return (ConstraintLayout) destList.getChildAt(pos);
|
||||
private fun unprepareMaxSend() {
|
||||
val entryView = getDestView(0)
|
||||
entryView.findViewById<View>(R.id.sending_all_textview).visibility = View.INVISIBLE
|
||||
entryView.findViewById<View>(R.id.amount_edittext).visibility =
|
||||
View.VISIBLE
|
||||
}
|
||||
|
||||
private EditText getAddressField(int pos) {
|
||||
return (EditText) getDestView(pos).findViewById(R.id.address_edittext);
|
||||
private fun prepareOutputsForMaxSend() {
|
||||
val entryView = getDestView(0)
|
||||
entryView.findViewById<View>(R.id.sending_all_textview).visibility = View.VISIBLE
|
||||
entryView.findViewById<View>(R.id.amount_edittext).visibility =
|
||||
View.INVISIBLE
|
||||
}
|
||||
|
||||
private void unprepareMaxSend() {
|
||||
ConstraintLayout entryView = getDestView(0);
|
||||
entryView.findViewById(R.id.sending_all_textview).setVisibility(View.INVISIBLE);
|
||||
entryView.findViewById(R.id.amount_edittext).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void prepareOutputsForMaxSend() {
|
||||
ConstraintLayout entryView = getDestView(0);
|
||||
entryView.findViewById(R.id.sending_all_textview).setVisibility(View.VISIBLE);
|
||||
entryView.findViewById(R.id.amount_edittext).setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
private void showConfirmationLayout(boolean show) {
|
||||
private fun showConfirmationLayout(show: Boolean) {
|
||||
if (show) {
|
||||
destList.setVisibility(View.GONE);
|
||||
setAddOutputButtonVisibility(View.GONE);
|
||||
sendMaxButton.setVisibility(View.GONE);
|
||||
createButton.setVisibility(View.GONE);
|
||||
feeRadioGroup.setVisibility(View.GONE);
|
||||
feeRadioGroupLabelTextView.setVisibility(View.GONE);
|
||||
|
||||
sendTxSlider.setVisibility(View.VISIBLE);
|
||||
feeTextView.setVisibility(View.VISIBLE);
|
||||
addressTextView.setVisibility(View.VISIBLE);
|
||||
amountTextView.setVisibility(View.VISIBLE);
|
||||
destList?.visibility = View.GONE
|
||||
setAddOutputButtonVisibility(View.GONE)
|
||||
sendMaxButton?.visibility = View.GONE
|
||||
createButton?.visibility = View.GONE
|
||||
feeRadioGroup?.visibility = View.GONE
|
||||
feeRadioGroupLabelTextView?.visibility = View.GONE
|
||||
sendTxSlider?.visibility = View.VISIBLE
|
||||
feeTextView?.visibility = View.VISIBLE
|
||||
addressTextView?.visibility = View.VISIBLE
|
||||
amountTextView?.visibility = View.VISIBLE
|
||||
} else {
|
||||
destList.setVisibility(View.VISIBLE);
|
||||
setAddOutputButtonVisibility(View.VISIBLE);
|
||||
sendMaxButton.setVisibility(View.VISIBLE);
|
||||
createButton.setVisibility(View.VISIBLE);
|
||||
feeRadioGroup.setVisibility(View.VISIBLE);
|
||||
feeRadioGroupLabelTextView.setVisibility(View.VISIBLE);
|
||||
|
||||
sendTxSlider.setVisibility(View.GONE);
|
||||
feeTextView.setVisibility(View.GONE);
|
||||
addressTextView.setVisibility(View.GONE);
|
||||
amountTextView.setVisibility(View.GONE);
|
||||
destList?.visibility = View.VISIBLE
|
||||
setAddOutputButtonVisibility(View.VISIBLE)
|
||||
sendMaxButton?.visibility = View.VISIBLE
|
||||
createButton?.visibility = View.VISIBLE
|
||||
feeRadioGroup?.visibility = View.VISIBLE
|
||||
feeRadioGroupLabelTextView?.visibility = View.VISIBLE
|
||||
sendTxSlider?.visibility = View.GONE
|
||||
feeTextView?.visibility = View.GONE
|
||||
addressTextView?.visibility = View.GONE
|
||||
amountTextView?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private void onScan(int index) {
|
||||
currentEntryIndex = index;
|
||||
if (Helper.getCameraPermission(getActivity(), cameraPermissionsLauncher)) {
|
||||
ScanOptions options = new ScanOptions();
|
||||
options.setBeepEnabled(false);
|
||||
options.setOrientationLocked(true);
|
||||
options.setDesiredBarcodeFormats(List.of(Intents.Scan.QR_CODE_MODE));
|
||||
options.addExtra(Intents.Scan.SCAN_TYPE, Intents.Scan.MIXED_SCAN);
|
||||
qrCodeLauncher.launch(options);
|
||||
private fun onScan(index: Int) {
|
||||
currentEntryIndex = index
|
||||
if (activity?.let { Helper.getCameraPermission(it, cameraPermissionsLauncher) } == true) {
|
||||
val options = ScanOptions()
|
||||
options.setBeepEnabled(false)
|
||||
options.setOrientationLocked(true)
|
||||
options.setDesiredBarcodeFormats(listOf(Intents.Scan.QR_CODE_MODE))
|
||||
options.addExtra(Intents.Scan.SCAN_TYPE, Intents.Scan.MIXED_SCAN)
|
||||
qrCodeLauncher.launch(options)
|
||||
}
|
||||
}
|
||||
|
||||
private void pasteAddress(ConstraintLayout entryView, String clipboard, boolean pastingAmount) {
|
||||
private fun pasteAddress(
|
||||
entryView: ConstraintLayout,
|
||||
clipboard: String,
|
||||
pastingAmount: Boolean
|
||||
) {
|
||||
if (pastingAmount) {
|
||||
try {
|
||||
Double.parseDouble(clipboard);
|
||||
setAmount(entryView, clipboard);
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(getActivity(), getString(R.string.send_amount_invalid), Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
clipboard.toDouble()
|
||||
setAmount(entryView, clipboard)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.send_amount_invalid),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
UriData uriData = UriData.parse(clipboard);
|
||||
val uriData = UriData.parse(clipboard)
|
||||
if (uriData != null) {
|
||||
int currentOutputs = getDestCount();
|
||||
val currentOutputs = destCount
|
||||
if (currentOutputs > 1 && uriData.hasPaymentId()) {
|
||||
Toast.makeText(getActivity(), getString(R.string.paymentid_paytomany), Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.paymentid_paytomany),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return
|
||||
} else if (currentOutputs == 1 && uriData.hasPaymentId()) {
|
||||
mViewModel.setShowAddOutputButton(false);
|
||||
mViewModel?.setShowAddOutputButton(false)
|
||||
}
|
||||
EditText addressField = entryView.findViewById(R.id.address_edittext);
|
||||
addressField.setText(uriData.address);
|
||||
val addressField = entryView.findViewById<EditText>(R.id.address_edittext)
|
||||
addressField.setText(uriData.address)
|
||||
if (uriData.hasAmount()) {
|
||||
setAmount(entryView, uriData.getAmount());
|
||||
setAmount(entryView, uriData.amount)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(getActivity(), getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(activity, getString(R.string.send_address_invalid), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
private void setAmount(ConstraintLayout entryView, String amount) {
|
||||
sendMaxButton.setEnabled(false);
|
||||
EditText amountField = entryView.findViewById(R.id.amount_edittext);
|
||||
amountField.setText(amount);
|
||||
private fun setAmount(entryView: ConstraintLayout, amount: String?) {
|
||||
sendMaxButton?.isEnabled = false
|
||||
val amountField = entryView.findViewById<EditText>(R.id.amount_edittext)
|
||||
amountField.setText(amount)
|
||||
}
|
||||
|
||||
private void createTx(List<Pair<String, String>> dests, boolean sendAll, PendingTransaction.Priority feePriority) {
|
||||
((MoneroApplication) getActivity().getApplication()).getExecutor().execute(() -> {
|
||||
private fun createTx(
|
||||
dests: List<Pair<String, String>>,
|
||||
sendAll: Boolean,
|
||||
feePriority: PendingTransaction.Priority
|
||||
) {
|
||||
(activity?.application as MoneroApplication).executor?.execute {
|
||||
try {
|
||||
PendingTransaction pendingTx = TxService.instance.createTx(dests, sendAll, feePriority, new ArrayList<>());
|
||||
if (pendingTx != null && pendingTx.getStatus() == PendingTransaction.Status.Status_Ok) {
|
||||
mViewModel.setPendingTransaction(pendingTx);
|
||||
val pendingTx =
|
||||
TxService.instance?.createTx(dests, sendAll, feePriority, ArrayList())
|
||||
if (pendingTx != null && pendingTx.status === PendingTransaction.Status.Status_Ok) {
|
||||
mViewModel?.setPendingTransaction(pendingTx)
|
||||
} else {
|
||||
Activity activity = getActivity();
|
||||
val activity: Activity? = activity
|
||||
if (activity != null && pendingTx != null) {
|
||||
activity.runOnUiThread(() -> {
|
||||
createButton.setEnabled(true);
|
||||
sendMaxButton.setEnabled(true);
|
||||
if (pendingTx.getErrorString() != null)
|
||||
Toast.makeText(activity, getString(R.string.error_creating_tx, pendingTx.getErrorString()), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
activity.runOnUiThread(Runnable {
|
||||
createButton?.isEnabled = true
|
||||
sendMaxButton?.isEnabled = true
|
||||
if (pendingTx.getErrorString() != null) Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.error_creating_tx, pendingTx.getErrorString()),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Activity activity = getActivity();
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(() -> {
|
||||
createButton.setEnabled(true);
|
||||
sendMaxButton.setEnabled(true);
|
||||
Toast.makeText(activity, getString(R.string.error_creating_tx, e.getMessage()), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
val activity: Activity? = activity
|
||||
activity?.runOnUiThread {
|
||||
createButton?.isEnabled = true
|
||||
sendMaxButton?.isEnabled = true
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.error_creating_tx, e.message),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void sendTx(PendingTransaction pendingTx) {
|
||||
((MoneroApplication) getActivity().getApplication()).getExecutor().execute(() -> {
|
||||
boolean success = TxService.instance.sendTx(pendingTx);
|
||||
Activity activity = getActivity();
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(() -> {
|
||||
if (success) {
|
||||
Toast.makeText(getActivity(), getString(R.string.sent_tx), Toast.LENGTH_SHORT).show();
|
||||
getActivity().onBackPressed();
|
||||
} else {
|
||||
sendTxSlider.resetSlider();
|
||||
Toast.makeText(getActivity(), getString(R.string.error_sending_tx), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
private fun sendTx(pendingTx: PendingTransaction) {
|
||||
(activity?.application as MoneroApplication).executor?.execute {
|
||||
val success = TxService.instance?.sendTx(pendingTx)
|
||||
val activity: Activity? = activity
|
||||
activity?.runOnUiThread {
|
||||
if (success == true) {
|
||||
Toast.makeText(getActivity(), getString(R.string.sent_tx), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
activity.onBackPressed()
|
||||
} else {
|
||||
sendTxSlider?.resetSlider()
|
||||
Toast.makeText(
|
||||
getActivity(),
|
||||
getString(R.string.error_sending_tx),
|
||||
Toast.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void setAddOutputButtonVisibility(int visibility) {
|
||||
addOutputImageView.setVisibility(visibility);
|
||||
private fun setAddOutputButtonVisibility(visibility: Int) {
|
||||
addOutputImageView?.visibility = visibility
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,29 +1,27 @@
|
|||
package net.mynero.wallet.fragment.send;
|
||||
package net.mynero.wallet.fragment.send
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import net.mynero.wallet.model.PendingTransaction
|
||||
|
||||
import net.mynero.wallet.model.PendingTransaction;
|
||||
|
||||
public class SendViewModel extends ViewModel {
|
||||
private final MutableLiveData<Boolean> _sendingMax = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<Boolean> _showAddOutputButton = new MutableLiveData<>(true);
|
||||
private final MutableLiveData<PendingTransaction> _pendingTransaction = new MutableLiveData<>(null);
|
||||
public LiveData<Boolean> sendingMax = _sendingMax;
|
||||
public LiveData<Boolean> showAddOutputButton = _showAddOutputButton;
|
||||
public LiveData<PendingTransaction> pendingTransaction = _pendingTransaction;
|
||||
|
||||
public void setSendingMax(boolean value) {
|
||||
_sendingMax.setValue(value);
|
||||
setShowAddOutputButton(!value);
|
||||
class SendViewModel : ViewModel() {
|
||||
private val _sendingMax = MutableLiveData(false)
|
||||
private val _showAddOutputButton = MutableLiveData(true)
|
||||
private val _pendingTransaction = MutableLiveData<PendingTransaction?>(null)
|
||||
var sendingMax: LiveData<Boolean?> = _sendingMax
|
||||
var showAddOutputButton: LiveData<Boolean?> = _showAddOutputButton
|
||||
var pendingTransaction: LiveData<PendingTransaction?> = _pendingTransaction
|
||||
fun setSendingMax(value: Boolean) {
|
||||
_sendingMax.value = value
|
||||
setShowAddOutputButton(!value)
|
||||
}
|
||||
|
||||
public void setShowAddOutputButton(boolean value) {
|
||||
_showAddOutputButton.setValue(value);
|
||||
fun setShowAddOutputButton(value: Boolean) {
|
||||
_showAddOutputButton.value = value
|
||||
}
|
||||
|
||||
public void setPendingTransaction(PendingTransaction pendingTx) {
|
||||
_pendingTransaction.postValue(pendingTx);
|
||||
fun setPendingTransaction(pendingTx: PendingTransaction?) {
|
||||
_pendingTransaction.postValue(pendingTx)
|
||||
}
|
||||
}
|
|
@ -13,177 +13,188 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.mynero.wallet.model
|
||||
|
||||
package net.mynero.wallet.model;
|
||||
import android.os.Build
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.os.Parcelable.Creator
|
||||
import net.mynero.wallet.data.Subaddress
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
class TransactionInfo : Parcelable, Comparable<TransactionInfo> {
|
||||
@JvmField
|
||||
var direction: Direction
|
||||
var isPending: Boolean
|
||||
var isFailed: Boolean
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@JvmField
|
||||
var amount: Long
|
||||
var fee: Long
|
||||
|
||||
import net.mynero.wallet.data.Subaddress;
|
||||
@JvmField
|
||||
var blockheight: Long
|
||||
|
||||
import java.util.List;
|
||||
@JvmField
|
||||
var hash: String?
|
||||
|
||||
// this is not the TransactionInfo from the API as that is owned by the TransactionHistory
|
||||
// this is a POJO for the TransactionInfoAdapter
|
||||
public class TransactionInfo implements Parcelable, Comparable<TransactionInfo> {
|
||||
public static final int CONFIRMATION = 10; // blocks
|
||||
public static final Parcelable.Creator<TransactionInfo> CREATOR = new Parcelable.Creator<TransactionInfo>() {
|
||||
public TransactionInfo createFromParcel(Parcel in) {
|
||||
return new TransactionInfo(in);
|
||||
@JvmField
|
||||
var timestamp: Long
|
||||
var paymentId: String?
|
||||
|
||||
@JvmField
|
||||
var accountIndex: Int
|
||||
|
||||
@JvmField
|
||||
var addressIndex: Int
|
||||
|
||||
@JvmField
|
||||
var confirmations: Long
|
||||
var subaddressLabel: String?
|
||||
|
||||
@JvmField
|
||||
var transfers: List<Transfer>? = listOf()
|
||||
|
||||
@JvmField
|
||||
var txKey: String? = null
|
||||
var notes: String? = null
|
||||
|
||||
@JvmField
|
||||
var address: String? = null
|
||||
|
||||
constructor(
|
||||
direction: Int,
|
||||
isPending: Boolean,
|
||||
isFailed: Boolean,
|
||||
amount: Long,
|
||||
fee: Long,
|
||||
blockheight: Long,
|
||||
hash: String?,
|
||||
timestamp: Long,
|
||||
paymentId: String?,
|
||||
accountIndex: Int,
|
||||
addressIndex: Int,
|
||||
confirmations: Long,
|
||||
subaddressLabel: String?,
|
||||
transfers: List<Transfer>?
|
||||
) {
|
||||
this.direction = Direction.values()[direction]
|
||||
this.isPending = isPending
|
||||
this.isFailed = isFailed
|
||||
this.amount = amount
|
||||
this.fee = fee
|
||||
this.blockheight = blockheight
|
||||
this.hash = hash
|
||||
this.timestamp = timestamp
|
||||
this.paymentId = paymentId
|
||||
this.accountIndex = accountIndex
|
||||
this.addressIndex = addressIndex
|
||||
this.confirmations = confirmations
|
||||
this.subaddressLabel = subaddressLabel
|
||||
this.transfers = transfers
|
||||
}
|
||||
|
||||
private constructor(`in`: Parcel) {
|
||||
direction = Direction.fromInteger(`in`.readInt())
|
||||
isPending = `in`.readByte().toInt() != 0
|
||||
isFailed = `in`.readByte().toInt() != 0
|
||||
amount = `in`.readLong()
|
||||
fee = `in`.readLong()
|
||||
blockheight = `in`.readLong()
|
||||
hash = `in`.readString()
|
||||
timestamp = `in`.readLong()
|
||||
paymentId = `in`.readString()
|
||||
accountIndex = `in`.readInt()
|
||||
addressIndex = `in`.readInt()
|
||||
confirmations = `in`.readLong()
|
||||
subaddressLabel = `in`.readString()
|
||||
transfers?.toMutableList()?.let { transfers ->
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
`in`.readList(transfers, Transfer::class.java.classLoader, Transfer::class.java)
|
||||
} else {
|
||||
`in`.readList(transfers, Transfer::class.java.classLoader)
|
||||
}
|
||||
}
|
||||
|
||||
public TransactionInfo[] newArray(int size) {
|
||||
return new TransactionInfo[size];
|
||||
txKey = `in`.readString()
|
||||
notes = `in`.readString()
|
||||
address = `in`.readString()
|
||||
}
|
||||
|
||||
val isConfirmed: Boolean
|
||||
get() = confirmations >= CONFIRMATION
|
||||
val displayLabel: String?
|
||||
get() = if (subaddressLabel?.isEmpty() == true || Subaddress.DEFAULT_LABEL_FORMATTER.matcher(
|
||||
subaddressLabel.toString()
|
||||
).matches()
|
||||
) "#$addressIndex" else subaddressLabel
|
||||
|
||||
override fun toString(): String {
|
||||
return "$direction@$blockheight $amount"
|
||||
}
|
||||
|
||||
override fun writeToParcel(out: Parcel, flags: Int) {
|
||||
out.writeInt(direction.value)
|
||||
out.writeByte((if (isPending) 1 else 0).toByte())
|
||||
out.writeByte((if (isFailed) 1 else 0).toByte())
|
||||
out.writeLong(amount)
|
||||
out.writeLong(fee)
|
||||
out.writeLong(blockheight)
|
||||
out.writeString(hash)
|
||||
out.writeLong(timestamp)
|
||||
out.writeString(paymentId)
|
||||
out.writeInt(accountIndex)
|
||||
out.writeInt(addressIndex)
|
||||
out.writeLong(confirmations)
|
||||
out.writeString(subaddressLabel)
|
||||
transfers?.let {
|
||||
out.writeList(transfers)
|
||||
}
|
||||
};
|
||||
public Direction direction;
|
||||
public boolean isPending;
|
||||
public boolean isFailed;
|
||||
public long amount;
|
||||
public long fee;
|
||||
public long blockheight;
|
||||
public String hash;
|
||||
public long timestamp;
|
||||
public String paymentId;
|
||||
public int accountIndex;
|
||||
public int addressIndex;
|
||||
public long confirmations;
|
||||
public String subaddressLabel;
|
||||
public List<Transfer> transfers;
|
||||
|
||||
public String txKey = null;
|
||||
public String notes = null;
|
||||
public String address = null;
|
||||
|
||||
public TransactionInfo(
|
||||
int direction,
|
||||
boolean isPending,
|
||||
boolean isFailed,
|
||||
long amount,
|
||||
long fee,
|
||||
long blockheight,
|
||||
String hash,
|
||||
long timestamp,
|
||||
String paymentId,
|
||||
int accountIndex,
|
||||
int addressIndex,
|
||||
long confirmations,
|
||||
String subaddressLabel,
|
||||
List<Transfer> transfers) {
|
||||
this.direction = Direction.values()[direction];
|
||||
this.isPending = isPending;
|
||||
this.isFailed = isFailed;
|
||||
this.amount = amount;
|
||||
this.fee = fee;
|
||||
this.blockheight = blockheight;
|
||||
this.hash = hash;
|
||||
this.timestamp = timestamp;
|
||||
this.paymentId = paymentId;
|
||||
this.accountIndex = accountIndex;
|
||||
this.addressIndex = addressIndex;
|
||||
this.confirmations = confirmations;
|
||||
this.subaddressLabel = subaddressLabel;
|
||||
this.transfers = transfers;
|
||||
out.writeString(txKey)
|
||||
out.writeString(notes)
|
||||
out.writeString(address)
|
||||
}
|
||||
|
||||
private TransactionInfo(Parcel in) {
|
||||
direction = Direction.fromInteger(in.readInt());
|
||||
isPending = in.readByte() != 0;
|
||||
isFailed = in.readByte() != 0;
|
||||
amount = in.readLong();
|
||||
fee = in.readLong();
|
||||
blockheight = in.readLong();
|
||||
hash = in.readString();
|
||||
timestamp = in.readLong();
|
||||
paymentId = in.readString();
|
||||
accountIndex = in.readInt();
|
||||
addressIndex = in.readInt();
|
||||
confirmations = in.readLong();
|
||||
subaddressLabel = in.readString();
|
||||
in.readList(transfers, Transfer.class.getClassLoader());
|
||||
txKey = in.readString();
|
||||
notes = in.readString();
|
||||
address = in.readString();
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
public boolean isConfirmed() {
|
||||
return confirmations >= CONFIRMATION;
|
||||
}
|
||||
|
||||
public String getDisplayLabel() {
|
||||
if (subaddressLabel.isEmpty() || (Subaddress.DEFAULT_LABEL_FORMATTER.matcher(subaddressLabel).matches()))
|
||||
return ("#" + addressIndex);
|
||||
else
|
||||
return subaddressLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return direction + "@" + blockheight + " " + amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeInt(direction.getValue());
|
||||
out.writeByte((byte) (isPending ? 1 : 0));
|
||||
out.writeByte((byte) (isFailed ? 1 : 0));
|
||||
out.writeLong(amount);
|
||||
out.writeLong(fee);
|
||||
out.writeLong(blockheight);
|
||||
out.writeString(hash);
|
||||
out.writeLong(timestamp);
|
||||
out.writeString(paymentId);
|
||||
out.writeInt(accountIndex);
|
||||
out.writeInt(addressIndex);
|
||||
out.writeLong(confirmations);
|
||||
out.writeString(subaddressLabel);
|
||||
out.writeList(transfers);
|
||||
out.writeString(txKey);
|
||||
out.writeString(notes);
|
||||
out.writeString(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TransactionInfo another) {
|
||||
long b1 = this.timestamp;
|
||||
long b2 = another.timestamp;
|
||||
if (b1 > b2) {
|
||||
return -1;
|
||||
override fun compareTo(other: TransactionInfo): Int {
|
||||
val b1 = timestamp
|
||||
val b2 = other.timestamp
|
||||
return if (b1 > b2) {
|
||||
-1
|
||||
} else if (b1 < b2) {
|
||||
return 1;
|
||||
1
|
||||
} else {
|
||||
return this.hash.compareTo(another.hash);
|
||||
hash?.let { other.hash?.compareTo(it) } ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
public enum Direction {
|
||||
Direction_In(0),
|
||||
Direction_Out(1);
|
||||
enum class Direction(val value: Int) {
|
||||
Direction_In(0), Direction_Out(1);
|
||||
|
||||
private final int value;
|
||||
|
||||
Direction(int value) {
|
||||
this.value = value;
|
||||
companion object {
|
||||
fun fromInteger(n: Int): Direction {
|
||||
return when (n) {
|
||||
0 -> Direction_In
|
||||
else -> Direction_Out
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Direction fromInteger(int n) {
|
||||
return switch (n) {
|
||||
case 0 -> Direction_In;
|
||||
case 1 -> Direction_Out;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
companion object {
|
||||
const val CONFIRMATION = 10 // blocks
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
@JvmField
|
||||
val CREATOR: Creator<TransactionInfo> = object : Creator<TransactionInfo> {
|
||||
override fun createFromParcel(`in`: Parcel): TransactionInfo {
|
||||
return TransactionInfo(`in`)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<TransactionInfo?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -430,8 +430,6 @@ class Wallet {
|
|||
|
||||
class Status internal constructor(status: Int, @JvmField val errorString: String) {
|
||||
val status: StatusEnum
|
||||
|
||||
@JvmField
|
||||
var connectionStatus: ConnectionStatus? = null // optional
|
||||
|
||||
init {
|
||||
|
@ -470,7 +468,6 @@ class Wallet {
|
|||
@JvmStatic
|
||||
external fun isPaymentIdValid(payment_id: String): Boolean
|
||||
|
||||
@JvmStatic
|
||||
fun isAddressValid(address: String): Boolean {
|
||||
return WalletManager.instance?.networkType?.value?.let {
|
||||
isAddressValid(
|
||||
|
|
|
@ -6,8 +6,6 @@ import net.mynero.wallet.model.BalanceInfo
|
|||
|
||||
class BalanceService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
||||
private val _balanceInfo = MutableLiveData<BalanceInfo?>(null)
|
||||
|
||||
@JvmField
|
||||
var balanceInfo: LiveData<BalanceInfo?> = _balanceInfo
|
||||
|
||||
init {
|
||||
|
|
|
@ -9,6 +9,7 @@ class BlockchainService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
|||
private val _currentHeight = MutableLiveData(0L)
|
||||
private val _connectionStatus = MutableLiveData(ConnectionStatus.ConnectionStatus_Disconnected)
|
||||
var height: LiveData<Long> = _currentHeight
|
||||
|
||||
@JvmField
|
||||
var connectionStatus: LiveData<ConnectionStatus> = _connectionStatus
|
||||
var daemonHeight: Long = 0
|
||||
|
|
|
@ -33,7 +33,6 @@ class TxService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
|||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
var instance: TxService? = null
|
||||
}
|
||||
}
|
|
@ -96,9 +96,9 @@ class UTXOService(thread: MoneroHandlerThread?) : ServiceBase(thread) {
|
|||
val seenTxs = ArrayList<String>()
|
||||
val utxos: List<CoinsInfo> = ArrayList(getUtxos())
|
||||
var amountSelected: Long = 0
|
||||
utxos.sorted()
|
||||
val sortedUtxos = utxos.sorted()
|
||||
//loop through each utxo
|
||||
for (coinsInfo in utxos) {
|
||||
for (coinsInfo in sortedUtxos) {
|
||||
if (!coinsInfo.isSpent && coinsInfo.isUnlocked && !coinsInfo.isFrozen && !frozenCoins.contains(
|
||||
coinsInfo.pubKey
|
||||
)
|
||||
|
|
|
@ -38,8 +38,6 @@ import androidx.activity.result.ActivityResultLauncher
|
|||
import androidx.core.content.ContextCompat
|
||||
import net.mynero.wallet.R
|
||||
import net.mynero.wallet.model.WalletManager
|
||||
import net.mynero.wallet.model.WalletManager.Companion.initLogger
|
||||
import net.mynero.wallet.model.WalletManager.Companion.setLogLevel
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
@ -62,8 +60,6 @@ object Helper {
|
|||
private const val MONERO_DIR = "monero"
|
||||
private val HexArray = "0123456789ABCDEF".toCharArray()
|
||||
var ALLOW_SHIFT = false
|
||||
|
||||
@JvmField
|
||||
var DISPLAY_DIGITS_INFO = 5
|
||||
private var ShakeAnimation: Animation? = null
|
||||
fun getWalletRoot(context: Context): File {
|
||||
|
@ -104,7 +100,6 @@ object Helper {
|
|||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getCameraPermission(context: Activity, launcher: ActivityResultLauncher<String?>): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (context.checkSelfPermission(Manifest.permission.CAMERA)
|
||||
|
@ -161,7 +156,6 @@ object Helper {
|
|||
return BigDecimal(amount).scaleByPowerOfTen(-XMR_DECIMALS)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getDisplayAmount(amount: Long): String {
|
||||
return getDisplayAmount(amount, XMR_DECIMALS)
|
||||
}
|
||||
|
@ -203,13 +197,10 @@ object Helper {
|
|||
}
|
||||
|
||||
fun getBitmap(context: Context, drawableId: Int): Bitmap {
|
||||
val drawable = ContextCompat.getDrawable(context, drawableId)
|
||||
return if (drawable is BitmapDrawable) {
|
||||
BitmapFactory.decodeResource(context.resources, drawableId)
|
||||
} else if (drawable is VectorDrawable) {
|
||||
getBitmap(drawable)
|
||||
} else {
|
||||
throw IllegalArgumentException("unsupported drawable type")
|
||||
return when (val drawable = ContextCompat.getDrawable(context, drawableId)) {
|
||||
is BitmapDrawable -> BitmapFactory.decodeResource(context.resources, drawableId)
|
||||
is VectorDrawable -> getBitmap(drawable)
|
||||
else -> throw IllegalArgumentException("unsupported drawable type")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,7 +259,6 @@ object Helper {
|
|||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getClipBoardText(context: Context): String? {
|
||||
val clipboardManager =
|
||||
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
|
@ -300,7 +290,7 @@ object Helper {
|
|||
// TODO make the log levels refer to the WalletManagerFactory::LogLevel enum ?
|
||||
fun initLogger(context: Context, level: Int) {
|
||||
val home = getStorage(context, MONERO_DIR).absolutePath
|
||||
initLogger("$home/monerujo", "monerujo.log")
|
||||
if (level >= WalletManager.LOGLEVEL_SILENT) setLogLevel(level)
|
||||
WalletManager.initLogger("$home/monerujo", "monerujo.log")
|
||||
if (level >= WalletManager.LOGLEVEL_SILENT) WalletManager.setLogLevel(level)
|
||||
}
|
||||
}
|
|
@ -34,7 +34,6 @@ object ThemeHelper {
|
|||
) typedValue.resourceId else 0
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@ColorInt
|
||||
fun getThemedColor(ctx: Context, attrId: Int): Int {
|
||||
val typedValue = TypedValue()
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.mynero.wallet.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class HelperTest {
|
||||
|
||||
@Test
|
||||
public void testMinus() {
|
||||
long l = -1000000000000L;
|
||||
String s = Helper.getDisplayAmount(l, 5);
|
||||
System.out.println(s);
|
||||
assertTrue(s.equals("-1.00"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTen() {
|
||||
long l = 10L;
|
||||
String s = Helper.getDisplayAmount(l);
|
||||
System.out.println(s);
|
||||
assertTrue(s.equals("0.00000000001"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZero() {
|
||||
long l = 0L;
|
||||
String s = Helper.getDisplayAmount(l);
|
||||
System.out.println(s);
|
||||
assertTrue(s.equals("0.00"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testG() {
|
||||
long l = 1234567891234L;
|
||||
String s = Helper.getDisplayAmount(l);
|
||||
System.out.println(s);
|
||||
assertTrue(s.equals("1.234567891234"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testG2() {
|
||||
long l = 1000000000000L;
|
||||
String s = Helper.getDisplayAmount(l);
|
||||
System.out.println(s);
|
||||
assertTrue(s.equals("1.00"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testE() {
|
||||
long l = 1234567891234L;
|
||||
String s = Helper.getDisplayAmount(l, 4);
|
||||
System.out.println(s);
|
||||
assertTrue(s.equals("1.2346"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testF() {
|
||||
long l = 1234567891234L;
|
||||
String s = Helper.getDisplayAmount(l, 12);
|
||||
System.out.println(s);
|
||||
assertTrue(s.equals("1.234567891234"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testH() {
|
||||
long l = 1004567891234L;
|
||||
String s = Helper.getDisplayAmount(l, 2);
|
||||
System.out.println(s);
|
||||
assertTrue(s.equals("1.00"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDisplayAmount() {
|
||||
assertTrue("0.000000051".equals(Helper.getDisplayAmount(0.000000051)));
|
||||
assertTrue("1.000000051".equals(Helper.getDisplayAmount(1.000000051)));
|
||||
assertTrue("1.0".equals(Helper.getDisplayAmount(1d)));
|
||||
assertTrue("0.0".equals(Helper.getDisplayAmount(0d)));
|
||||
}
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.mynero.wallet.util;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
// all ranges go back 5 days
|
||||
|
||||
public class RestoreHeightTest {
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void pre2014() {
|
||||
assertTrue(getHeight("2013-12-01") == 0);
|
||||
assertTrue(getHeight("1958-12-01") == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zero() {
|
||||
assertTrue(getHeight("2014-04-27") == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notZero() {
|
||||
assertTrue(getHeight("2014-05-07") > 0);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void notDateA() {
|
||||
getHeight("2013-13-04");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void notDateB() {
|
||||
getHeight("2013-13-01-");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void notDateC() {
|
||||
getHeight("x013-13-01");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void notDateD() {
|
||||
getHeight("2013-12-41");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test201709() {
|
||||
// getHeight() returns blockheight of < two days ago
|
||||
assertTrue(isInRange(getHeight("2017-09-01"), 1383957, 1387716));
|
||||
assertTrue(isInRange(getHeight("2017-09-05"), 1386967, 1390583));
|
||||
assertTrue(isInRange(getHeight("2017-09-21"), 1398492, 1402068));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test20160324() { // blocktime changed from 1 minute to 2 minutes on this day
|
||||
assertTrue(isInRange(getHeight("2016-03-23"), 998955, 1006105));
|
||||
assertTrue(isInRange(getHeight("2016-03-24"), 1000414, 1007486));
|
||||
assertTrue(isInRange(getHeight("2016-03-25"), 1001800, 1008900));
|
||||
assertTrue(isInRange(getHeight("2016-03-26"), 1003243, 1009985));
|
||||
assertTrue(isInRange(getHeight("2016-03-27"), 1004694, 1010746));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2014() {
|
||||
assertTrue(isInRange(getHeight("2014-04-26"), 0, 8501));
|
||||
assertTrue(isInRange(getHeight("2014-05-09"), 20289, 28311));
|
||||
assertTrue(isInRange(getHeight("2014-05-17"), 32608, 40075));
|
||||
assertTrue(isInRange(getHeight("2014-05-30"), 52139, 59548));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2015() {
|
||||
assertTrue(isInRange(getHeight("2015-01-26"), 397914, 405055));
|
||||
assertTrue(isInRange(getHeight("2015-08-13"), 682595, 689748));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2016() {
|
||||
assertTrue(isInRange(getHeight("2016-01-26"), 918313, 925424));
|
||||
assertTrue(isInRange(getHeight("2016-08-13"), 1107244, 1110793));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2017() {
|
||||
assertTrue(isInRange(getHeight("2017-01-26"), 1226806, 1230402));
|
||||
assertTrue(isInRange(getHeight("2017-08-13"), 1370264, 1373854));
|
||||
assertTrue(isInRange(getHeight("2017-08-31"), 1383254, 1386967));
|
||||
assertTrue(isInRange(getHeight("2017-06-09"), 1323288, 1326884));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void post201802() {
|
||||
assertTrue(isInRange(getHeight("2018-02-19"), 1507579, 1511127));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFuture() {
|
||||
long b_20180701 = 1606715L;
|
||||
long b_20190108 = b_20180701 + 720 * (31 + 31 + 30 + 31 + 30 + 31 + 7);
|
||||
assertTrue(isInRange(getHeight("2019-01-08"), b_20190108 - 720 * 5, b_20190108));
|
||||
}
|
||||
|
||||
|
||||
private boolean isInRange(long n, long min, long max) {
|
||||
if (n > max) return false;
|
||||
return n >= min;
|
||||
}
|
||||
|
||||
private long getHeight(String date) {
|
||||
return RestoreHeight.getInstance().getHeight(date);
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.mynero.wallet.util;
|
||||
|
||||
import net.mynero.wallet.data.UserNotes;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class UserNoteTest {
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_noNote() {
|
||||
UserNotes userNotes = new UserNotes("{xmrto-iyrpxU,0.009BTC,mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9}");
|
||||
assertTrue("xmrto".equals(userNotes.xmrtoTag));
|
||||
assertTrue("iyrpxU".equals(userNotes.xmrtoKey));
|
||||
assertTrue("0.009".equals(userNotes.xmrtoAmount));
|
||||
assertTrue("mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9".equals(userNotes.xmrtoDestination));
|
||||
assertTrue(userNotes.note.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_withNote() {
|
||||
UserNotes userNotes = new UserNotes("{xmrto-iyrpxU,0.009BTC,mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9} aNote");
|
||||
assertTrue("xmrto".equals(userNotes.xmrtoTag));
|
||||
assertTrue("iyrpxU".equals(userNotes.xmrtoKey));
|
||||
assertTrue("0.009".equals(userNotes.xmrtoAmount));
|
||||
assertTrue("mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9".equals(userNotes.xmrtoDestination));
|
||||
assertTrue("aNote".equals(userNotes.note));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_withNoteNoSpace() {
|
||||
UserNotes userNotes = new UserNotes("{xmrto-iyrpxU,0.009BTC,mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9}aNote");
|
||||
assertTrue("xmrto".equals(userNotes.xmrtoTag));
|
||||
assertTrue("iyrpxU".equals(userNotes.xmrtoKey));
|
||||
assertTrue("0.009".equals(userNotes.xmrtoAmount));
|
||||
assertTrue("mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9".equals(userNotes.xmrtoDestination));
|
||||
assertTrue("aNote".equals(userNotes.note));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_brokenB() {
|
||||
String brokenNote = "{xmrto-iyrpxU,0.009BTC,mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9";
|
||||
UserNotes userNotes = new UserNotes(brokenNote);
|
||||
assertNull(userNotes.xmrtoKey);
|
||||
assertNull(userNotes.xmrtoAmount);
|
||||
assertNull(userNotes.xmrtoDestination);
|
||||
assertTrue(brokenNote.equals(userNotes.note));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_normal() {
|
||||
String aNote = "aNote";
|
||||
UserNotes userNotes = new UserNotes(aNote);
|
||||
assertNull(userNotes.xmrtoKey);
|
||||
assertNull(userNotes.xmrtoAmount);
|
||||
assertNull(userNotes.xmrtoDestination);
|
||||
assertTrue(aNote.equals(userNotes.note));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_empty() {
|
||||
String aNote = "";
|
||||
UserNotes userNotes = new UserNotes(aNote);
|
||||
assertNull(userNotes.xmrtoKey);
|
||||
assertNull(userNotes.xmrtoAmount);
|
||||
assertNull(userNotes.xmrtoDestination);
|
||||
assertTrue(aNote.equals(userNotes.note));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_null() {
|
||||
UserNotes userNotes = new UserNotes(null);
|
||||
assertNull(userNotes.xmrtoKey);
|
||||
assertNull(userNotes.xmrtoAmount);
|
||||
assertNull(userNotes.xmrtoDestination);
|
||||
assertNotNull(userNotes.note);
|
||||
assertTrue(userNotes.note.isEmpty());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue