mirror of
https://codeberg.org/r4v3r23/mysu.git
synced 2024-11-09 20:53:47 +01:00
Cleanup more Kotlin code, and convert SendFragment
This commit is contained in:
parent
3c52bd3a55
commit
d4cb982b94
@ -23,7 +23,6 @@ import net.mynero.wallet.util.UriData
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(), MoneroHandlerThread.Listener, PasswordListener {
|
class MainActivity : AppCompatActivity(), MoneroHandlerThread.Listener, PasswordListener {
|
||||||
@JvmField
|
|
||||||
val restartEvents: SingleLiveEvent<*> = SingleLiveEvent<Any?>()
|
val restartEvents: SingleLiveEvent<*> = SingleLiveEvent<Any?>()
|
||||||
var thread: MoneroHandlerThread? = null
|
var thread: MoneroHandlerThread? = null
|
||||||
private set
|
private set
|
||||||
|
@ -21,6 +21,7 @@ import android.view.ViewGroup
|
|||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import net.mynero.wallet.R
|
import net.mynero.wallet.R
|
||||||
import net.mynero.wallet.data.DefaultNodes
|
import net.mynero.wallet.data.DefaultNodes
|
||||||
@ -83,9 +84,19 @@ class NodeSelectionAdapter(val listener: NodeSelectionAdapterListener?) :
|
|||||||
val currentNode = PrefService.instance?.node
|
val currentNode = PrefService.instance?.node
|
||||||
val match = node == currentNode
|
val match = node == currentNode
|
||||||
if (match) {
|
if (match) {
|
||||||
itemView.setBackgroundColor(itemView.resources.getColor(R.color.oled_colorSecondary))
|
itemView.setBackgroundColor(
|
||||||
|
ContextCompat.getColor(
|
||||||
|
itemView.context,
|
||||||
|
R.color.oled_colorSecondary
|
||||||
|
)
|
||||||
|
)
|
||||||
} else {
|
} 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 nodeNameTextView = itemView.findViewById<TextView>(R.id.node_name_textview)
|
||||||
val nodeAddressTextView = itemView.findViewById<TextView>(R.id.node_uri_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.service.PrefService
|
||||||
import net.mynero.wallet.util.Constants
|
import net.mynero.wallet.util.Constants
|
||||||
import net.mynero.wallet.util.Helper
|
import net.mynero.wallet.util.Helper
|
||||||
import net.mynero.wallet.util.Helper.getDisplayAmount
|
|
||||||
|
|
||||||
class SubaddressAdapter(val listener: SubaddressAdapterListener?) :
|
class SubaddressAdapter(val listener: SubaddressAdapterListener?) :
|
||||||
RecyclerView.Adapter<SubaddressAdapter.ViewHolder>() {
|
RecyclerView.Adapter<SubaddressAdapter.ViewHolder>() {
|
||||||
@ -101,7 +100,7 @@ class SubaddressAdapter(val listener: SubaddressAdapterListener?) :
|
|||||||
} else {
|
} else {
|
||||||
addressAmountTextView.text = itemView.context.getString(
|
addressAmountTextView.text = itemView.context.getString(
|
||||||
R.string.tx_list_amount_positive,
|
R.string.tx_list_amount_positive,
|
||||||
getDisplayAmount(amount, Helper.DISPLAY_DIGITS_INFO)
|
Helper.getDisplayAmount(amount, Helper.DISPLAY_DIGITS_INFO)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else addressAmountTextView.text = ""
|
} else addressAmountTextView.text = ""
|
||||||
|
@ -27,8 +27,7 @@ import net.mynero.wallet.service.PrefService
|
|||||||
import net.mynero.wallet.util.Constants
|
import net.mynero.wallet.util.Constants
|
||||||
import net.mynero.wallet.util.DateHelper.DATETIME_FORMATTER
|
import net.mynero.wallet.util.DateHelper.DATETIME_FORMATTER
|
||||||
import net.mynero.wallet.util.Helper
|
import net.mynero.wallet.util.Helper
|
||||||
import net.mynero.wallet.util.Helper.getDisplayAmount
|
import net.mynero.wallet.util.ThemeHelper
|
||||||
import net.mynero.wallet.util.ThemeHelper.getThemedColor
|
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
@ -84,10 +83,10 @@ class TransactionInfoAdapter(val listener: TxInfoAdapterListener?) :
|
|||||||
private var amountTextView: TextView? = null
|
private var amountTextView: TextView? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
inboundColour = getThemedColor(view.context, R.attr.positiveColor)
|
inboundColour = ThemeHelper.getThemedColor(view.context, R.attr.positiveColor)
|
||||||
outboundColour = getThemedColor(view.context, R.attr.negativeColor)
|
outboundColour = ThemeHelper.getThemedColor(view.context, R.attr.negativeColor)
|
||||||
pendingColour = getThemedColor(view.context, R.attr.neutralColor)
|
pendingColour = ThemeHelper.getThemedColor(view.context, R.attr.neutralColor)
|
||||||
failedColour = getThemedColor(view.context, R.attr.neutralColor)
|
failedColour = ThemeHelper.getThemedColor(view.context, R.attr.neutralColor)
|
||||||
val cal = Calendar.getInstance()
|
val cal = Calendar.getInstance()
|
||||||
val tz = cal.timeZone //get the local time zone.
|
val tz = cal.timeZone //get the local time zone.
|
||||||
DATETIME_FORMATTER.timeZone = tz
|
DATETIME_FORMATTER.timeZone = tz
|
||||||
@ -97,7 +96,7 @@ class TransactionInfoAdapter(val listener: TxInfoAdapterListener?) :
|
|||||||
val streetModeEnabled =
|
val streetModeEnabled =
|
||||||
PrefService.instance?.getBoolean(Constants.PREF_STREET_MODE, false)
|
PrefService.instance?.getBoolean(Constants.PREF_STREET_MODE, false)
|
||||||
val displayAmount =
|
val displayAmount =
|
||||||
if (streetModeEnabled == true) Constants.STREET_MODE_BALANCE else getDisplayAmount(
|
if (streetModeEnabled == true) Constants.STREET_MODE_BALANCE else Helper.getDisplayAmount(
|
||||||
txInfo.amount,
|
txInfo.amount,
|
||||||
Helper.DISPLAY_DIGITS_INFO
|
Helper.DISPLAY_DIGITS_INFO
|
||||||
)
|
)
|
||||||
@ -108,7 +107,7 @@ class TransactionInfoAdapter(val listener: TxInfoAdapterListener?) :
|
|||||||
amountTextView = itemView.findViewById(R.id.tx_amount)
|
amountTextView = itemView.findViewById(R.id.tx_amount)
|
||||||
itemView.findViewById<View>(R.id.tx_failed).visibility = View.GONE
|
itemView.findViewById<View>(R.id.tx_failed).visibility = View.GONE
|
||||||
if (txInfo.isFailed) {
|
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.context.getString(R.string.tx_list_amount_negative, displayAmount)
|
||||||
itemView.findViewById<View>(R.id.tx_failed).visibility = View.VISIBLE
|
itemView.findViewById<View>(R.id.tx_failed).visibility = View.VISIBLE
|
||||||
setTxColour(failedColour)
|
setTxColour(failedColour)
|
||||||
@ -140,10 +139,10 @@ class TransactionInfoAdapter(val listener: TxInfoAdapterListener?) :
|
|||||||
confirmationsTextView.visibility = View.GONE
|
confirmationsTextView.visibility = View.GONE
|
||||||
}
|
}
|
||||||
if (txInfo.direction == TransactionInfo.Direction.Direction_Out) {
|
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)
|
itemView.context.getString(R.string.tx_list_amount_negative, displayAmount)
|
||||||
} else {
|
} else {
|
||||||
(itemView.findViewById<View>(R.id.tx_amount) as TextView).text =
|
amountTextView?.text =
|
||||||
itemView.context.getString(R.string.tx_list_amount_positive, displayAmount)
|
itemView.context.getString(R.string.tx_list_amount_positive, displayAmount)
|
||||||
}
|
}
|
||||||
(itemView.findViewById<View>(R.id.tx_datetime) as TextView).text =
|
(itemView.findViewById<View>(R.id.tx_datetime) as TextView).text =
|
||||||
|
@ -85,13 +85,13 @@ enum class DefaultNodes(
|
|||||||
"mainnet",
|
"mainnet",
|
||||||
"Criminales78.onion"
|
"Criminales78.onion"
|
||||||
),
|
),
|
||||||
xmrfail(
|
Xmrfail(
|
||||||
"mxcd4577fldb3ppzy7obmmhnu3tf57gbcbd4qhwr2kxyjj2qi3dnbfqd.onion",
|
"mxcd4577fldb3ppzy7obmmhnu3tf57gbcbd4qhwr2kxyjj2qi3dnbfqd.onion",
|
||||||
18081,
|
18081,
|
||||||
"mainnet",
|
"mainnet",
|
||||||
"xmrfail.onion"
|
"xmrfail.onion"
|
||||||
),
|
),
|
||||||
boldsuck(
|
Boldsuck(
|
||||||
"6dsdenp6vjkvqzy4wzsnzn6wixkdzihx3khiumyzieauxuxslmcaeiad.onion",
|
"6dsdenp6vjkvqzy4wzsnzn6wixkdzihx3khiumyzieauxuxslmcaeiad.onion",
|
||||||
18081,
|
18081,
|
||||||
"mainnet",
|
"mainnet",
|
||||||
|
@ -38,7 +38,6 @@ class Subaddress(
|
|||||||
) "#$addressIndex" else label
|
) "#$addressIndex" else label
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmField
|
|
||||||
val DEFAULT_LABEL_FORMATTER: Pattern =
|
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}$")
|
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()
|
Toast.makeText(context, "Node already exists", Toast.LENGTH_SHORT).show()
|
||||||
return
|
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) {
|
private fun addPasteListener(root: View, editText: EditText, layoutId: Int) {
|
||||||
|
@ -17,9 +17,7 @@ import java.text.SimpleDateFormat
|
|||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class EditAddressLabelBottomSheetDialog : BottomSheetDialogFragment() {
|
class EditAddressLabelBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
@JvmField
|
|
||||||
var listener: LabelListener? = null
|
var listener: LabelListener? = null
|
||||||
@JvmField
|
|
||||||
var addressIndex = 0
|
var addressIndex = 0
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
@ -21,6 +21,7 @@ import org.json.JSONObject
|
|||||||
class EditNodeBottomSheetDialog : BottomSheetDialogFragment() {
|
class EditNodeBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
@JvmField
|
@JvmField
|
||||||
var listener: EditNodeListener? = null
|
var listener: EditNodeListener? = null
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var node: Node? = null
|
var node: Node? = null
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
@ -95,8 +96,9 @@ class EditNodeBottomSheetDialog : BottomSheetDialogFragment() {
|
|||||||
jsonObject.put("username", user)
|
jsonObject.put("username", user)
|
||||||
jsonObject.put("password", pass)
|
jsonObject.put("password", pass)
|
||||||
}
|
}
|
||||||
if (nodeAddr.contains(":") && !nodeAddr.startsWith("[") && !nodeAddr.endsWith("]")) nodeAddr =
|
if (nodeAddr.contains(":") && !nodeAddr.startsWith("[") && !nodeAddr.endsWith("]"))
|
||||||
"[$nodeAddr]"
|
nodeAddr = "[$nodeAddr]"
|
||||||
|
|
||||||
jsonObject.put("host", nodeAddr)
|
jsonObject.put("host", nodeAddr)
|
||||||
jsonObject.put("rpcPort", portString.toInt())
|
jsonObject.put("rpcPort", portString.toInt())
|
||||||
jsonObject.put("network", "mainnet")
|
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()) {
|
for (defaultNode in DefaultNodes.values()) {
|
||||||
fromJson(defaultNode.json)?.let { nodes.add(it) }
|
fromJson(defaultNode.json)?.let { nodes.add(it) }
|
||||||
}
|
}
|
||||||
@ -98,7 +99,8 @@ class NodeSelectionBottomSheetDialog : BottomSheetDialogFragment(), NodeSelectio
|
|||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).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)
|
WalletManager.instance?.setDaemon(node)
|
||||||
adapter?.updateSelectedNode()
|
adapter?.updateSelectedNode()
|
||||||
listener?.onNodeSelected()
|
listener?.onNodeSelected()
|
||||||
|
@ -17,6 +17,7 @@ import java.io.File
|
|||||||
class PasswordBottomSheetDialog : BottomSheetDialogFragment() {
|
class PasswordBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
@JvmField
|
@JvmField
|
||||||
var listener: PasswordListener? = null
|
var listener: PasswordListener? = null
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var cancelable = false
|
var cancelable = false
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
@ -14,7 +14,6 @@ import android.widget.Toast
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
import com.google.zxing.client.android.Intents
|
import com.google.zxing.client.android.Intents
|
||||||
import com.journeyapps.barcodescanner.ScanContract
|
import com.journeyapps.barcodescanner.ScanContract
|
||||||
@ -22,7 +21,6 @@ import com.journeyapps.barcodescanner.ScanIntentResult
|
|||||||
import com.journeyapps.barcodescanner.ScanOptions
|
import com.journeyapps.barcodescanner.ScanOptions
|
||||||
import net.mynero.wallet.MoneroApplication
|
import net.mynero.wallet.MoneroApplication
|
||||||
import net.mynero.wallet.R
|
import net.mynero.wallet.R
|
||||||
import net.mynero.wallet.model.BalanceInfo
|
|
||||||
import net.mynero.wallet.model.PendingTransaction
|
import net.mynero.wallet.model.PendingTransaction
|
||||||
import net.mynero.wallet.model.Wallet
|
import net.mynero.wallet.model.Wallet
|
||||||
import net.mynero.wallet.model.Wallet.Companion.getAmountFromString
|
import net.mynero.wallet.model.Wallet.Companion.getAmountFromString
|
||||||
@ -40,10 +38,12 @@ import net.mynero.wallet.util.UriData.Companion.parse
|
|||||||
class SendBottomSheetDialog : BottomSheetDialogFragment() {
|
class SendBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
private val _sendingMax = MutableLiveData(false)
|
private val _sendingMax = MutableLiveData(false)
|
||||||
private val _pendingTransaction = MutableLiveData<PendingTransaction?>(null)
|
private val _pendingTransaction = MutableLiveData<PendingTransaction?>(null)
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var selectedUtxos = ArrayList<String>()
|
var selectedUtxos = ArrayList<String>()
|
||||||
private var sendingMax: LiveData<Boolean?> = _sendingMax
|
private var sendingMax: LiveData<Boolean?> = _sendingMax
|
||||||
private var pendingTransaction: LiveData<PendingTransaction?> = _pendingTransaction
|
private var pendingTransaction: LiveData<PendingTransaction?> = _pendingTransaction
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var uriData: UriData? = null
|
var uriData: UriData? = null
|
||||||
private val cameraPermissionsLauncher = registerForActivityResult(
|
private val cameraPermissionsLauncher = registerForActivityResult(
|
||||||
@ -56,8 +56,10 @@ class SendBottomSheetDialog : BottomSheetDialogFragment() {
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var isChurning = false
|
var isChurning = false
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var listener: Listener? = null
|
var listener: Listener? = null
|
||||||
var priority: PendingTransaction.Priority = PendingTransaction.Priority.Priority_Low
|
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.HistoryService
|
||||||
import net.mynero.wallet.service.PrefService
|
import net.mynero.wallet.service.PrefService
|
||||||
import net.mynero.wallet.util.Constants
|
import net.mynero.wallet.util.Constants
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
class HomeFragment : Fragment(), TxInfoAdapterListener {
|
class HomeFragment : Fragment(), TxInfoAdapterListener {
|
||||||
private var startHeight: Long = 0
|
private var startHeight: Long = 0
|
||||||
@ -124,11 +123,11 @@ class HomeFragment : Fragment(), TxInfoAdapterListener {
|
|||||||
displayEmptyHistory(true, view, textResId, botImgResId)
|
displayEmptyHistory(true, view, textResId, botImgResId)
|
||||||
} else {
|
} else {
|
||||||
// POPULATED WALLET HISTORY
|
// POPULATED WALLET HISTORY
|
||||||
history.sorted()
|
val sortedHistory = history.sortedByDescending { it.timestamp }
|
||||||
if (history.size > 100) {
|
if (sortedHistory.size > 100) {
|
||||||
adapter.submitList(history.subList(0, 99))
|
adapter.submitList(sortedHistory.subList(0, 99))
|
||||||
} else {
|
} else {
|
||||||
adapter.submitList(history)
|
adapter.submitList(sortedHistory)
|
||||||
}
|
}
|
||||||
txHistoryRecyclerView.visibility = View.VISIBLE
|
txHistoryRecyclerView.visibility = View.VISIBLE
|
||||||
displayEmptyHistory(
|
displayEmptyHistory(
|
||||||
|
@ -8,6 +8,7 @@ import android.view.ViewGroup
|
|||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
@ -120,7 +121,8 @@ class ReceiveFragment : Fragment() {
|
|||||||
|
|
||||||
private fun generate(text: String, width: Int, height: Int): Bitmap? {
|
private fun generate(text: String, width: Int, height: Int): Bitmap? {
|
||||||
if (width <= 0 || height <= 0) return null
|
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.CHARACTER_SET] = StandardCharsets.UTF_8
|
||||||
hints[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.M
|
hints[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.M
|
||||||
try {
|
try {
|
||||||
@ -131,7 +133,10 @@ class ReceiveFragment : Fragment() {
|
|||||||
if (bitMatrix[j, i]) {
|
if (bitMatrix[j, i]) {
|
||||||
pixels[i * width + j] = -0x1
|
pixels[i * width + j] = -0x1
|
||||||
} else {
|
} 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.app.Activity
|
||||||
import android.content.Context;
|
import android.os.Bundle
|
||||||
import android.os.Bundle;
|
import android.text.Editable
|
||||||
import android.text.Editable;
|
import android.text.TextWatcher
|
||||||
import android.text.TextWatcher;
|
import android.view.LayoutInflater
|
||||||
import android.view.LayoutInflater;
|
import android.view.View
|
||||||
import android.view.View;
|
import android.view.ViewGroup
|
||||||
import android.view.ViewGroup;
|
import android.widget.Button
|
||||||
import android.widget.Button;
|
import android.widget.EditText
|
||||||
import android.widget.EditText;
|
import android.widget.ImageButton
|
||||||
import android.widget.ImageButton;
|
import android.widget.LinearLayout
|
||||||
import android.widget.LinearLayout;
|
import android.widget.RadioGroup
|
||||||
import android.widget.RadioGroup;
|
import android.widget.TextView
|
||||||
import android.widget.TextView;
|
import android.widget.Toast
|
||||||
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;
|
class SendFragment : Fragment() {
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
var priority: PendingTransaction.Priority = PendingTransaction.Priority.Priority_Low
|
||||||
import androidx.annotation.NonNull;
|
private var mViewModel: SendViewModel? = null
|
||||||
import androidx.annotation.Nullable;
|
private var sendMaxButton: Button? = null
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
private var addOutputImageView: ImageButton? = null
|
||||||
import androidx.fragment.app.Fragment;
|
private var destList: LinearLayout? = null
|
||||||
import androidx.fragment.app.FragmentActivity;
|
private var inflater: LayoutInflater? = null
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
private var createButton: Button? = null
|
||||||
|
private var sendTxSlider: SlideToActView? = null
|
||||||
import com.google.zxing.client.android.Intents;
|
private var feeRadioGroup: RadioGroup? = null
|
||||||
import com.journeyapps.barcodescanner.ScanContract;
|
private var feeRadioGroupLabelTextView: TextView? = null
|
||||||
import com.journeyapps.barcodescanner.ScanOptions;
|
private var feeTextView: TextView? = null
|
||||||
import com.ncorti.slidetoact.SlideToActView;
|
private var addressTextView: TextView? = null
|
||||||
|
private var amountTextView: TextView? = null
|
||||||
import net.mynero.wallet.MoneroApplication;
|
private var currentEntryIndex = -1
|
||||||
import net.mynero.wallet.R;
|
private val qrCodeLauncher =
|
||||||
import net.mynero.wallet.model.PendingTransaction;
|
registerForActivityResult(ScanContract()) { result: ScanIntentResult ->
|
||||||
import net.mynero.wallet.model.Wallet;
|
if (result.contents != null) {
|
||||||
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) {
|
if (currentEntryIndex != -1) {
|
||||||
pasteAddress(getDestView(currentEntryIndex), result.getContents(), false);
|
pasteAddress(getDestView(currentEntryIndex), result.contents, false)
|
||||||
currentEntryIndex = -1;
|
currentEntryIndex = -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
|
||||||
@Nullable Bundle savedInstanceState) {
|
|
||||||
return inflater.inflate(R.layout.fragment_send, container, false);
|
|
||||||
}
|
}
|
||||||
|
private val cameraPermissionsLauncher = registerForActivityResult(
|
||||||
@Override
|
ActivityResultContracts.RequestPermission()
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
) { granted: Boolean ->
|
||||||
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) {
|
if (granted) {
|
||||||
onScan(currentEntryIndex);
|
onScan(currentEntryIndex)
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(getActivity(), getString(R.string.no_camera_permission), Toast.LENGTH_SHORT).show();
|
Toast.makeText(activity, getString(R.string.no_camera_permission), Toast.LENGTH_SHORT)
|
||||||
currentEntryIndex = -1;
|
.show()
|
||||||
|
currentEntryIndex = -1
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
private void init() {
|
|
||||||
addOutput(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bindListeners() {
|
override fun onCreateView(
|
||||||
feeRadioGroup.check(R.id.low_fee_radiobutton);
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
priority = PendingTransaction.Priority.Priority_Low;
|
savedInstanceState: Bundle?
|
||||||
feeRadioGroup.setOnCheckedChangeListener((radioGroup, i) -> {
|
): View? {
|
||||||
if (i == R.id.low_fee_radiobutton) {
|
return inflater.inflate(R.layout.fragment_send, container, false)
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
addOutputImageView.setOnClickListener(view1 -> {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
sendMaxButton.setVisibility(View.GONE);
|
super.onViewCreated(view, savedInstanceState)
|
||||||
int outputCount = getDestCount();
|
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) {
|
if (outputCount < 8) {
|
||||||
addOutput(false);
|
addOutput(false)
|
||||||
} else {
|
} 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 -> {
|
sendMaxButton?.setOnClickListener { mViewModel?.setSendingMax(!isSendAll) }
|
||||||
mViewModel.setSendingMax(!isSendAll());
|
createButton?.setOnClickListener {
|
||||||
});
|
val outputsValid = checkDestsValidity(isSendAll)
|
||||||
createButton.setOnClickListener(view1 -> {
|
|
||||||
boolean outputsValid = checkDestsValidity(isSendAll());
|
|
||||||
|
|
||||||
if (outputsValid) {
|
if (outputsValid) {
|
||||||
Toast.makeText(getActivity(), getString(R.string.creating_tx), Toast.LENGTH_SHORT).show();
|
Toast.makeText(activity, getString(R.string.creating_tx), Toast.LENGTH_SHORT).show()
|
||||||
createButton.setEnabled(false);
|
createButton?.isEnabled = false
|
||||||
sendMaxButton.setEnabled(false);
|
sendMaxButton?.isEnabled = false
|
||||||
createTx(getRawDests(), isSendAll(), priority);
|
createTx(rawDests, isSendAll, priority)
|
||||||
} else {
|
} 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?.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)
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkDestsValidity(boolean sendAll) {
|
private fun checkDestsValidity(sendAll: Boolean): Boolean {
|
||||||
List<Pair<String, String>> dests = getRawDests();
|
val dests = rawDests
|
||||||
for (Pair<String, String> dest : dests) {
|
for (dest in dests) {
|
||||||
String address = dest.component1();
|
val address = dest.component1()
|
||||||
String amount = dest.component2();
|
val amount = dest.component2()
|
||||||
if (!sendAll) {
|
if (!sendAll) {
|
||||||
if (amount.isEmpty()) {
|
if (amount.isEmpty()) {
|
||||||
Toast.makeText(getActivity(), getString(R.string.send_amount_empty), Toast.LENGTH_SHORT).show();
|
Toast.makeText(
|
||||||
return false;
|
activity,
|
||||||
|
getString(R.string.send_amount_empty),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
val amountRaw = Wallet.getAmountFromString(amount)
|
||||||
long amountRaw = Wallet.getAmountFromString(amount);
|
val balance = BalanceService.instance?.unlockedBalanceRaw ?: 0
|
||||||
long balance = BalanceService.instance.getUnlockedBalanceRaw();
|
|
||||||
if (amountRaw >= balance || amountRaw <= 0) {
|
if (amountRaw >= balance || amountRaw <= 0) {
|
||||||
Toast.makeText(getActivity(), getString(R.string.send_amount_invalid), Toast.LENGTH_SHORT).show();
|
Toast.makeText(
|
||||||
return false;
|
activity,
|
||||||
|
getString(R.string.send_amount_invalid),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
} else if (dests.size() > 1) {
|
} else if (dests.size > 1) {
|
||||||
Toast.makeText(getActivity(), getString(R.string.send_amount_invalid_sendall_paytomany), Toast.LENGTH_SHORT).show();
|
Toast.makeText(
|
||||||
return false;
|
activity,
|
||||||
|
getString(R.string.send_amount_invalid_sendall_paytomany),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
val uriData = UriData.parse(address)
|
||||||
UriData uriData = UriData.parse(address);
|
val isValidAddress = uriData != null
|
||||||
boolean isValidAddress = uriData != null;
|
|
||||||
if (!isValidAddress) {
|
if (!isValidAddress) {
|
||||||
Toast.makeText(getActivity(), getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show();
|
Toast.makeText(
|
||||||
return false;
|
activity,
|
||||||
|
getString(R.string.send_address_invalid),
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dests.size() > 1 && uriData.hasPaymentId()) {
|
private fun destsHasPaymentId(): Boolean {
|
||||||
Toast.makeText(getActivity(), getString(R.string.paymentid_paytomany), Toast.LENGTH_SHORT).show();
|
val dests = rawDests
|
||||||
return false;
|
for (dest in dests) {
|
||||||
|
val address = dest.component1()
|
||||||
|
val uriData = UriData.parse(address) ?: return false
|
||||||
|
if (uriData.hasPaymentId()) return true
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
private fun bindObservers() {
|
||||||
}
|
mViewModel?.sendingMax?.observe(viewLifecycleOwner) { sendingMax: Boolean? ->
|
||||||
|
if (mViewModel?.pendingTransaction?.value == null) {
|
||||||
private boolean destsHasPaymentId() {
|
if (sendingMax == true) {
|
||||||
List<Pair<String, String>> dests = getRawDests();
|
prepareOutputsForMaxSend()
|
||||||
for (Pair<String, String> dest : dests) {
|
sendMaxButton?.text = getText(R.string.undo)
|
||||||
String address = dest.component1();
|
|
||||||
UriData uriData = UriData.parse(address);
|
|
||||||
if (uriData == null) return false;
|
|
||||||
if (uriData.hasPaymentId()) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindObservers() {
|
|
||||||
mViewModel.sendingMax.observe(getViewLifecycleOwner(), sendingMax -> {
|
|
||||||
if (mViewModel.pendingTransaction.getValue() == null) {
|
|
||||||
if (sendingMax) {
|
|
||||||
prepareOutputsForMaxSend();
|
|
||||||
sendMaxButton.setText(getText(R.string.undo));
|
|
||||||
} else {
|
} else {
|
||||||
unprepareMaxSend();
|
unprepareMaxSend()
|
||||||
sendMaxButton.setText(getText(R.string.send_max));
|
sendMaxButton?.text = getText(R.string.send_max)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
mViewModel?.showAddOutputButton?.observe(viewLifecycleOwner) { show: Boolean? ->
|
||||||
mViewModel.showAddOutputButton.observe(getViewLifecycleOwner(), show -> {
|
setAddOutputButtonVisibility(
|
||||||
setAddOutputButtonVisibility((show && !destsHasPaymentId()) ? View.VISIBLE : View.INVISIBLE);
|
if (show == true && !destsHasPaymentId()) View.VISIBLE else View.INVISIBLE
|
||||||
});
|
)
|
||||||
|
}
|
||||||
mViewModel.pendingTransaction.observe(getViewLifecycleOwner(), pendingTx -> {
|
mViewModel?.pendingTransaction?.observe(viewLifecycleOwner) { pendingTx: PendingTransaction? ->
|
||||||
showConfirmationLayout(pendingTx != null);
|
showConfirmationLayout(pendingTx != null)
|
||||||
|
|
||||||
if (pendingTx != null) {
|
if (pendingTx != null) {
|
||||||
String address = getDestCount() == 1 ? getAddressField(0).getText().toString() : "Multiple";
|
val address = if (destCount == 1) getAddressField(0).text.toString() else "Multiple"
|
||||||
addressTextView.setText(getString(R.string.tx_address_text, address));
|
addressTextView?.text = getString(R.string.tx_address_text, address)
|
||||||
amountTextView.setText(getString(R.string.tx_amount_text, Helper.getDisplayAmount(pendingTx.getAmount())));
|
amountTextView?.text =
|
||||||
feeTextView.setText(getString(R.string.tx_fee_text, Helper.getDisplayAmount(pendingTx.getFee())));
|
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) {
|
if (inflater != null) {
|
||||||
int index = getDestCount();
|
val index = destCount
|
||||||
ConstraintLayout entryView = (ConstraintLayout) inflater.inflate(R.layout.transaction_output_item, null);
|
val entryView =
|
||||||
ImageButton removeOutputImageButton = entryView.findViewById(R.id.remove_output_imagebutton);
|
inflater?.inflate(R.layout.transaction_output_item, null) as ConstraintLayout
|
||||||
EditText addressField = entryView.findViewById(R.id.address_edittext);
|
val removeOutputImageButton =
|
||||||
addressField.addTextChangedListener(new TextWatcher() {
|
entryView.findViewById<ImageButton>(R.id.remove_output_imagebutton)
|
||||||
@Override
|
val addressField = entryView.findViewById<EditText>(R.id.address_edittext)
|
||||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
addressField.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun beforeTextChanged(
|
||||||
|
charSequence: CharSequence,
|
||||||
|
i: Int,
|
||||||
|
i1: Int,
|
||||||
|
i2: Int
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
override fun afterTextChanged(editable: Editable) {
|
||||||
}
|
val currentOutputs: Int = destCount
|
||||||
|
val uriData = UriData.parse(editable.toString())
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable editable) {
|
|
||||||
int currentOutputs = getDestCount();
|
|
||||||
UriData uriData = UriData.parse(editable.toString());
|
|
||||||
if (uriData != null) {
|
if (uriData != null) {
|
||||||
// we have valid address
|
// we have valid address
|
||||||
boolean hasPaymentId = uriData.hasPaymentId();
|
val hasPaymentId = uriData.hasPaymentId()
|
||||||
if (currentOutputs > 1 && hasPaymentId) {
|
if (currentOutputs > 1 && hasPaymentId) {
|
||||||
// multiple outputs when pasting/editing in integrated address. this is not allowed
|
// multiple outputs when pasting/editing in integrated address. this is not allowed
|
||||||
Toast.makeText(getActivity(), getString(R.string.paymentid_paytomany), Toast.LENGTH_SHORT).show();
|
Toast.makeText(
|
||||||
addressField.setText(null);
|
activity,
|
||||||
|
getString(R.string.paymentid_paytomany),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
addressField.text = null
|
||||||
} else if (currentOutputs == 1 && hasPaymentId) {
|
} else if (currentOutputs == 1 && hasPaymentId) {
|
||||||
// show add output button: we are sending to integrated address
|
// 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
|
// 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 -> {
|
entryView.findViewById<View>(R.id.paste_amount_imagebutton)
|
||||||
Context ctx = getContext();
|
.setOnClickListener {
|
||||||
|
val ctx = context
|
||||||
if (ctx != null) {
|
if (ctx != null) {
|
||||||
String clipboard = Helper.getClipBoardText(ctx);
|
val clipboard = Helper.getClipBoardText(ctx)
|
||||||
if (clipboard != null) {
|
if (clipboard != null) {
|
||||||
pasteAddress(entryView, clipboard, true);
|
pasteAddress(entryView, clipboard, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
entryView.findViewById(R.id.paste_address_imagebutton).setOnClickListener(view1 -> {
|
entryView.findViewById<View>(R.id.paste_address_imagebutton)
|
||||||
Context ctx = getContext();
|
.setOnClickListener {
|
||||||
|
val ctx = context
|
||||||
if (ctx != null) {
|
if (ctx != null) {
|
||||||
String clipboard = Helper.getClipBoardText(ctx);
|
val clipboard = Helper.getClipBoardText(ctx)
|
||||||
if (clipboard != null) {
|
if (clipboard != null) {
|
||||||
pasteAddress(entryView, clipboard, false);
|
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) {
|
if (initial) {
|
||||||
removeOutputImageButton.setVisibility(View.INVISIBLE);
|
removeOutputImageButton.visibility = View.INVISIBLE
|
||||||
} else {
|
} else {
|
||||||
removeOutputImageButton.setOnClickListener(view -> {
|
removeOutputImageButton.setOnClickListener {
|
||||||
int currentCount = getDestCount();
|
val currentCount = destCount
|
||||||
if (currentCount > 1) {
|
if (currentCount > 1) {
|
||||||
if (currentCount == 2) {
|
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() {
|
private val destCount: Int
|
||||||
return destList.getChildCount();
|
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
|
||||||
|
|
||||||
|
private fun getDestView(pos: Int): ConstraintLayout {
|
||||||
|
return destList?.getChildAt(pos) as ConstraintLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Pair<String, String>> getRawDests() {
|
private fun getAddressField(pos: Int): EditText {
|
||||||
ArrayList<Pair<String, String>> dests = new ArrayList<>();
|
return getDestView(pos).findViewById<View>(R.id.address_edittext) as EditText
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return dests;
|
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 boolean isSendAll() {
|
private fun prepareOutputsForMaxSend() {
|
||||||
return mViewModel.sendingMax.getValue() != null ? mViewModel.sendingMax.getValue() : false;
|
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 ConstraintLayout getDestView(int pos) {
|
private fun showConfirmationLayout(show: Boolean) {
|
||||||
return (ConstraintLayout) destList.getChildAt(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
private EditText getAddressField(int pos) {
|
|
||||||
return (EditText) getDestView(pos).findViewById(R.id.address_edittext);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
if (show) {
|
if (show) {
|
||||||
destList.setVisibility(View.GONE);
|
destList?.visibility = View.GONE
|
||||||
setAddOutputButtonVisibility(View.GONE);
|
setAddOutputButtonVisibility(View.GONE)
|
||||||
sendMaxButton.setVisibility(View.GONE);
|
sendMaxButton?.visibility = View.GONE
|
||||||
createButton.setVisibility(View.GONE);
|
createButton?.visibility = View.GONE
|
||||||
feeRadioGroup.setVisibility(View.GONE);
|
feeRadioGroup?.visibility = View.GONE
|
||||||
feeRadioGroupLabelTextView.setVisibility(View.GONE);
|
feeRadioGroupLabelTextView?.visibility = View.GONE
|
||||||
|
sendTxSlider?.visibility = View.VISIBLE
|
||||||
sendTxSlider.setVisibility(View.VISIBLE);
|
feeTextView?.visibility = View.VISIBLE
|
||||||
feeTextView.setVisibility(View.VISIBLE);
|
addressTextView?.visibility = View.VISIBLE
|
||||||
addressTextView.setVisibility(View.VISIBLE);
|
amountTextView?.visibility = View.VISIBLE
|
||||||
amountTextView.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
} else {
|
||||||
destList.setVisibility(View.VISIBLE);
|
destList?.visibility = View.VISIBLE
|
||||||
setAddOutputButtonVisibility(View.VISIBLE);
|
setAddOutputButtonVisibility(View.VISIBLE)
|
||||||
sendMaxButton.setVisibility(View.VISIBLE);
|
sendMaxButton?.visibility = View.VISIBLE
|
||||||
createButton.setVisibility(View.VISIBLE);
|
createButton?.visibility = View.VISIBLE
|
||||||
feeRadioGroup.setVisibility(View.VISIBLE);
|
feeRadioGroup?.visibility = View.VISIBLE
|
||||||
feeRadioGroupLabelTextView.setVisibility(View.VISIBLE);
|
feeRadioGroupLabelTextView?.visibility = View.VISIBLE
|
||||||
|
sendTxSlider?.visibility = View.GONE
|
||||||
sendTxSlider.setVisibility(View.GONE);
|
feeTextView?.visibility = View.GONE
|
||||||
feeTextView.setVisibility(View.GONE);
|
addressTextView?.visibility = View.GONE
|
||||||
addressTextView.setVisibility(View.GONE);
|
amountTextView?.visibility = View.GONE
|
||||||
amountTextView.setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onScan(int index) {
|
private fun onScan(index: Int) {
|
||||||
currentEntryIndex = index;
|
currentEntryIndex = index
|
||||||
if (Helper.getCameraPermission(getActivity(), cameraPermissionsLauncher)) {
|
if (activity?.let { Helper.getCameraPermission(it, cameraPermissionsLauncher) } == true) {
|
||||||
ScanOptions options = new ScanOptions();
|
val options = ScanOptions()
|
||||||
options.setBeepEnabled(false);
|
options.setBeepEnabled(false)
|
||||||
options.setOrientationLocked(true);
|
options.setOrientationLocked(true)
|
||||||
options.setDesiredBarcodeFormats(List.of(Intents.Scan.QR_CODE_MODE));
|
options.setDesiredBarcodeFormats(listOf(Intents.Scan.QR_CODE_MODE))
|
||||||
options.addExtra(Intents.Scan.SCAN_TYPE, Intents.Scan.MIXED_SCAN);
|
options.addExtra(Intents.Scan.SCAN_TYPE, Intents.Scan.MIXED_SCAN)
|
||||||
qrCodeLauncher.launch(options);
|
qrCodeLauncher.launch(options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pasteAddress(ConstraintLayout entryView, String clipboard, boolean pastingAmount) {
|
private fun pasteAddress(
|
||||||
|
entryView: ConstraintLayout,
|
||||||
|
clipboard: String,
|
||||||
|
pastingAmount: Boolean
|
||||||
|
) {
|
||||||
if (pastingAmount) {
|
if (pastingAmount) {
|
||||||
try {
|
try {
|
||||||
Double.parseDouble(clipboard);
|
clipboard.toDouble()
|
||||||
setAmount(entryView, clipboard);
|
setAmount(entryView, clipboard)
|
||||||
} catch (Exception e) {
|
} catch (e: Exception) {
|
||||||
Toast.makeText(getActivity(), getString(R.string.send_amount_invalid), Toast.LENGTH_SHORT).show();
|
Toast.makeText(
|
||||||
return;
|
activity,
|
||||||
|
getString(R.string.send_amount_invalid),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val uriData = UriData.parse(clipboard)
|
||||||
UriData uriData = UriData.parse(clipboard);
|
|
||||||
if (uriData != null) {
|
if (uriData != null) {
|
||||||
int currentOutputs = getDestCount();
|
val currentOutputs = destCount
|
||||||
if (currentOutputs > 1 && uriData.hasPaymentId()) {
|
if (currentOutputs > 1 && uriData.hasPaymentId()) {
|
||||||
Toast.makeText(getActivity(), getString(R.string.paymentid_paytomany), Toast.LENGTH_SHORT).show();
|
Toast.makeText(
|
||||||
return;
|
activity,
|
||||||
|
getString(R.string.paymentid_paytomany),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
return
|
||||||
} else if (currentOutputs == 1 && uriData.hasPaymentId()) {
|
} else if (currentOutputs == 1 && uriData.hasPaymentId()) {
|
||||||
mViewModel.setShowAddOutputButton(false);
|
mViewModel?.setShowAddOutputButton(false)
|
||||||
}
|
}
|
||||||
EditText addressField = entryView.findViewById(R.id.address_edittext);
|
val addressField = entryView.findViewById<EditText>(R.id.address_edittext)
|
||||||
addressField.setText(uriData.address);
|
addressField.setText(uriData.address)
|
||||||
if (uriData.hasAmount()) {
|
if (uriData.hasAmount()) {
|
||||||
setAmount(entryView, uriData.getAmount());
|
setAmount(entryView, uriData.amount)
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
private fun setAmount(entryView: ConstraintLayout, amount: String?) {
|
||||||
sendMaxButton.setEnabled(false);
|
sendMaxButton?.isEnabled = false
|
||||||
EditText amountField = entryView.findViewById(R.id.amount_edittext);
|
val amountField = entryView.findViewById<EditText>(R.id.amount_edittext)
|
||||||
amountField.setText(amount);
|
amountField.setText(amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createTx(List<Pair<String, String>> dests, boolean sendAll, PendingTransaction.Priority feePriority) {
|
private fun createTx(
|
||||||
((MoneroApplication) getActivity().getApplication()).getExecutor().execute(() -> {
|
dests: List<Pair<String, String>>,
|
||||||
|
sendAll: Boolean,
|
||||||
|
feePriority: PendingTransaction.Priority
|
||||||
|
) {
|
||||||
|
(activity?.application as MoneroApplication).executor?.execute {
|
||||||
try {
|
try {
|
||||||
PendingTransaction pendingTx = TxService.instance.createTx(dests, sendAll, feePriority, new ArrayList<>());
|
val pendingTx =
|
||||||
if (pendingTx != null && pendingTx.getStatus() == PendingTransaction.Status.Status_Ok) {
|
TxService.instance?.createTx(dests, sendAll, feePriority, ArrayList())
|
||||||
mViewModel.setPendingTransaction(pendingTx);
|
if (pendingTx != null && pendingTx.status === PendingTransaction.Status.Status_Ok) {
|
||||||
|
mViewModel?.setPendingTransaction(pendingTx)
|
||||||
} else {
|
} else {
|
||||||
Activity activity = getActivity();
|
val activity: Activity? = activity
|
||||||
if (activity != null && pendingTx != null) {
|
if (activity != null && pendingTx != null) {
|
||||||
activity.runOnUiThread(() -> {
|
activity.runOnUiThread(Runnable {
|
||||||
createButton.setEnabled(true);
|
createButton?.isEnabled = true
|
||||||
sendMaxButton.setEnabled(true);
|
sendMaxButton?.isEnabled = true
|
||||||
if (pendingTx.getErrorString() != null)
|
if (pendingTx.getErrorString() != null) Toast.makeText(
|
||||||
Toast.makeText(activity, getString(R.string.error_creating_tx, pendingTx.getErrorString()), Toast.LENGTH_SHORT).show();
|
activity,
|
||||||
});
|
getString(R.string.error_creating_tx, pendingTx.getErrorString()),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace();
|
e.printStackTrace()
|
||||||
Activity activity = getActivity();
|
val activity: Activity? = activity
|
||||||
if (activity != null) {
|
activity?.runOnUiThread {
|
||||||
activity.runOnUiThread(() -> {
|
createButton?.isEnabled = true
|
||||||
createButton.setEnabled(true);
|
sendMaxButton?.isEnabled = true
|
||||||
sendMaxButton.setEnabled(true);
|
Toast.makeText(
|
||||||
Toast.makeText(activity, getString(R.string.error_creating_tx, e.getMessage()), Toast.LENGTH_SHORT).show();
|
activity,
|
||||||
});
|
getString(R.string.error_creating_tx, e.message),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendTx(PendingTransaction pendingTx) {
|
private fun sendTx(pendingTx: PendingTransaction) {
|
||||||
((MoneroApplication) getActivity().getApplication()).getExecutor().execute(() -> {
|
(activity?.application as MoneroApplication).executor?.execute {
|
||||||
boolean success = TxService.instance.sendTx(pendingTx);
|
val success = TxService.instance?.sendTx(pendingTx)
|
||||||
Activity activity = getActivity();
|
val activity: Activity? = activity
|
||||||
if (activity != null) {
|
activity?.runOnUiThread {
|
||||||
activity.runOnUiThread(() -> {
|
if (success == true) {
|
||||||
if (success) {
|
Toast.makeText(getActivity(), getString(R.string.sent_tx), Toast.LENGTH_SHORT)
|
||||||
Toast.makeText(getActivity(), getString(R.string.sent_tx), Toast.LENGTH_SHORT).show();
|
.show()
|
||||||
getActivity().onBackPressed();
|
activity.onBackPressed()
|
||||||
} else {
|
} else {
|
||||||
sendTxSlider.resetSlider();
|
sendTxSlider?.resetSlider()
|
||||||
Toast.makeText(getActivity(), getString(R.string.error_sending_tx), Toast.LENGTH_SHORT).show();
|
Toast.makeText(
|
||||||
|
getActivity(),
|
||||||
|
getString(R.string.error_sending_tx),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setAddOutputButtonVisibility(int visibility) {
|
private fun setAddOutputButtonVisibility(visibility: Int) {
|
||||||
addOutputImageView.setVisibility(visibility);
|
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.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel;
|
import androidx.lifecycle.ViewModel
|
||||||
|
import net.mynero.wallet.model.PendingTransaction
|
||||||
|
|
||||||
import net.mynero.wallet.model.PendingTransaction;
|
class SendViewModel : ViewModel() {
|
||||||
|
private val _sendingMax = MutableLiveData(false)
|
||||||
public class SendViewModel extends ViewModel {
|
private val _showAddOutputButton = MutableLiveData(true)
|
||||||
private final MutableLiveData<Boolean> _sendingMax = new MutableLiveData<>(false);
|
private val _pendingTransaction = MutableLiveData<PendingTransaction?>(null)
|
||||||
private final MutableLiveData<Boolean> _showAddOutputButton = new MutableLiveData<>(true);
|
var sendingMax: LiveData<Boolean?> = _sendingMax
|
||||||
private final MutableLiveData<PendingTransaction> _pendingTransaction = new MutableLiveData<>(null);
|
var showAddOutputButton: LiveData<Boolean?> = _showAddOutputButton
|
||||||
public LiveData<Boolean> sendingMax = _sendingMax;
|
var pendingTransaction: LiveData<PendingTransaction?> = _pendingTransaction
|
||||||
public LiveData<Boolean> showAddOutputButton = _showAddOutputButton;
|
fun setSendingMax(value: Boolean) {
|
||||||
public LiveData<PendingTransaction> pendingTransaction = _pendingTransaction;
|
_sendingMax.value = value
|
||||||
|
setShowAddOutputButton(!value)
|
||||||
public void setSendingMax(boolean value) {
|
|
||||||
_sendingMax.setValue(value);
|
|
||||||
setShowAddOutputButton(!value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setShowAddOutputButton(boolean value) {
|
fun setShowAddOutputButton(value: Boolean) {
|
||||||
_showAddOutputButton.setValue(value);
|
_showAddOutputButton.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPendingTransaction(PendingTransaction pendingTx) {
|
fun setPendingTransaction(pendingTx: PendingTransaction?) {
|
||||||
_pendingTransaction.postValue(pendingTx);
|
_pendingTransaction.postValue(pendingTx)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,177 +13,188 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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;
|
class TransactionInfo : Parcelable, Comparable<TransactionInfo> {
|
||||||
import android.os.Parcelable;
|
@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
|
@JvmField
|
||||||
// this is a POJO for the TransactionInfoAdapter
|
var timestamp: Long
|
||||||
public class TransactionInfo implements Parcelable, Comparable<TransactionInfo> {
|
var paymentId: String?
|
||||||
public static final int CONFIRMATION = 10; // blocks
|
|
||||||
public static final Parcelable.Creator<TransactionInfo> CREATOR = new Parcelable.Creator<TransactionInfo>() {
|
@JvmField
|
||||||
public TransactionInfo createFromParcel(Parcel in) {
|
var accountIndex: Int
|
||||||
return new TransactionInfo(in);
|
|
||||||
|
@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
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransactionInfo[] newArray(int size) {
|
private constructor(`in`: Parcel) {
|
||||||
return new TransactionInfo[size];
|
direction = Direction.fromInteger(`in`.readInt())
|
||||||
}
|
isPending = `in`.readByte().toInt() != 0
|
||||||
};
|
isFailed = `in`.readByte().toInt() != 0
|
||||||
public Direction direction;
|
amount = `in`.readLong()
|
||||||
public boolean isPending;
|
fee = `in`.readLong()
|
||||||
public boolean isFailed;
|
blockheight = `in`.readLong()
|
||||||
public long amount;
|
hash = `in`.readString()
|
||||||
public long fee;
|
timestamp = `in`.readLong()
|
||||||
public long blockheight;
|
paymentId = `in`.readString()
|
||||||
public String hash;
|
accountIndex = `in`.readInt()
|
||||||
public long timestamp;
|
addressIndex = `in`.readInt()
|
||||||
public String paymentId;
|
confirmations = `in`.readLong()
|
||||||
public int accountIndex;
|
subaddressLabel = `in`.readString()
|
||||||
public int addressIndex;
|
transfers?.toMutableList()?.let { transfers ->
|
||||||
public long confirmations;
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
public String subaddressLabel;
|
`in`.readList(transfers, Transfer::class.java.classLoader, Transfer::class.java)
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
} else if (b1 < b2) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
} else {
|
||||||
return this.hash.compareTo(another.hash);
|
`in`.readList(transfers, Transfer::class.java.classLoader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Direction {
|
txKey = `in`.readString()
|
||||||
Direction_In(0),
|
notes = `in`.readString()
|
||||||
Direction_Out(1);
|
address = `in`.readString()
|
||||||
|
|
||||||
private final int value;
|
|
||||||
|
|
||||||
Direction(int value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Direction fromInteger(int n) {
|
val isConfirmed: Boolean
|
||||||
return switch (n) {
|
get() = confirmations >= CONFIRMATION
|
||||||
case 0 -> Direction_In;
|
val displayLabel: String?
|
||||||
case 1 -> Direction_Out;
|
get() = if (subaddressLabel?.isEmpty() == true || Subaddress.DEFAULT_LABEL_FORMATTER.matcher(
|
||||||
default -> null;
|
subaddressLabel.toString()
|
||||||
};
|
).matches()
|
||||||
|
) "#$addressIndex" else subaddressLabel
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "$direction@$blockheight $amount"
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getValue() {
|
override fun writeToParcel(out: Parcel, flags: Int) {
|
||||||
return value;
|
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)
|
||||||
|
}
|
||||||
|
out.writeString(txKey)
|
||||||
|
out.writeString(notes)
|
||||||
|
out.writeString(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describeContents(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun compareTo(other: TransactionInfo): Int {
|
||||||
|
val b1 = timestamp
|
||||||
|
val b2 = other.timestamp
|
||||||
|
return if (b1 > b2) {
|
||||||
|
-1
|
||||||
|
} else if (b1 < b2) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
hash?.let { other.hash?.compareTo(it) } ?: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Direction(val value: Int) {
|
||||||
|
Direction_In(0), Direction_Out(1);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromInteger(n: Int): Direction {
|
||||||
|
return when (n) {
|
||||||
|
0 -> Direction_In
|
||||||
|
else -> Direction_Out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val CONFIRMATION = 10 // blocks
|
||||||
|
|
||||||
|
@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) {
|
class Status internal constructor(status: Int, @JvmField val errorString: String) {
|
||||||
val status: StatusEnum
|
val status: StatusEnum
|
||||||
|
|
||||||
@JvmField
|
|
||||||
var connectionStatus: ConnectionStatus? = null // optional
|
var connectionStatus: ConnectionStatus? = null // optional
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -470,7 +468,6 @@ class Wallet {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
external fun isPaymentIdValid(payment_id: String): Boolean
|
external fun isPaymentIdValid(payment_id: String): Boolean
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun isAddressValid(address: String): Boolean {
|
fun isAddressValid(address: String): Boolean {
|
||||||
return WalletManager.instance?.networkType?.value?.let {
|
return WalletManager.instance?.networkType?.value?.let {
|
||||||
isAddressValid(
|
isAddressValid(
|
||||||
|
@ -6,8 +6,6 @@ import net.mynero.wallet.model.BalanceInfo
|
|||||||
|
|
||||||
class BalanceService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
class BalanceService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
||||||
private val _balanceInfo = MutableLiveData<BalanceInfo?>(null)
|
private val _balanceInfo = MutableLiveData<BalanceInfo?>(null)
|
||||||
|
|
||||||
@JvmField
|
|
||||||
var balanceInfo: LiveData<BalanceInfo?> = _balanceInfo
|
var balanceInfo: LiveData<BalanceInfo?> = _balanceInfo
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -9,6 +9,7 @@ class BlockchainService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
|||||||
private val _currentHeight = MutableLiveData(0L)
|
private val _currentHeight = MutableLiveData(0L)
|
||||||
private val _connectionStatus = MutableLiveData(ConnectionStatus.ConnectionStatus_Disconnected)
|
private val _connectionStatus = MutableLiveData(ConnectionStatus.ConnectionStatus_Disconnected)
|
||||||
var height: LiveData<Long> = _currentHeight
|
var height: LiveData<Long> = _currentHeight
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var connectionStatus: LiveData<ConnectionStatus> = _connectionStatus
|
var connectionStatus: LiveData<ConnectionStatus> = _connectionStatus
|
||||||
var daemonHeight: Long = 0
|
var daemonHeight: Long = 0
|
||||||
|
@ -33,7 +33,6 @@ class TxService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmField
|
|
||||||
var instance: TxService? = null
|
var instance: TxService? = null
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -96,9 +96,9 @@ class UTXOService(thread: MoneroHandlerThread?) : ServiceBase(thread) {
|
|||||||
val seenTxs = ArrayList<String>()
|
val seenTxs = ArrayList<String>()
|
||||||
val utxos: List<CoinsInfo> = ArrayList(getUtxos())
|
val utxos: List<CoinsInfo> = ArrayList(getUtxos())
|
||||||
var amountSelected: Long = 0
|
var amountSelected: Long = 0
|
||||||
utxos.sorted()
|
val sortedUtxos = utxos.sorted()
|
||||||
//loop through each utxo
|
//loop through each utxo
|
||||||
for (coinsInfo in utxos) {
|
for (coinsInfo in sortedUtxos) {
|
||||||
if (!coinsInfo.isSpent && coinsInfo.isUnlocked && !coinsInfo.isFrozen && !frozenCoins.contains(
|
if (!coinsInfo.isSpent && coinsInfo.isUnlocked && !coinsInfo.isFrozen && !frozenCoins.contains(
|
||||||
coinsInfo.pubKey
|
coinsInfo.pubKey
|
||||||
)
|
)
|
||||||
|
@ -38,8 +38,6 @@ import androidx.activity.result.ActivityResultLauncher
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import net.mynero.wallet.R
|
import net.mynero.wallet.R
|
||||||
import net.mynero.wallet.model.WalletManager
|
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 timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -62,8 +60,6 @@ object Helper {
|
|||||||
private const val MONERO_DIR = "monero"
|
private const val MONERO_DIR = "monero"
|
||||||
private val HexArray = "0123456789ABCDEF".toCharArray()
|
private val HexArray = "0123456789ABCDEF".toCharArray()
|
||||||
var ALLOW_SHIFT = false
|
var ALLOW_SHIFT = false
|
||||||
|
|
||||||
@JvmField
|
|
||||||
var DISPLAY_DIGITS_INFO = 5
|
var DISPLAY_DIGITS_INFO = 5
|
||||||
private var ShakeAnimation: Animation? = null
|
private var ShakeAnimation: Animation? = null
|
||||||
fun getWalletRoot(context: Context): File {
|
fun getWalletRoot(context: Context): File {
|
||||||
@ -104,7 +100,6 @@ object Helper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun getCameraPermission(context: Activity, launcher: ActivityResultLauncher<String?>): Boolean {
|
fun getCameraPermission(context: Activity, launcher: ActivityResultLauncher<String?>): Boolean {
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
if (context.checkSelfPermission(Manifest.permission.CAMERA)
|
if (context.checkSelfPermission(Manifest.permission.CAMERA)
|
||||||
@ -161,7 +156,6 @@ object Helper {
|
|||||||
return BigDecimal(amount).scaleByPowerOfTen(-XMR_DECIMALS)
|
return BigDecimal(amount).scaleByPowerOfTen(-XMR_DECIMALS)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun getDisplayAmount(amount: Long): String {
|
fun getDisplayAmount(amount: Long): String {
|
||||||
return getDisplayAmount(amount, XMR_DECIMALS)
|
return getDisplayAmount(amount, XMR_DECIMALS)
|
||||||
}
|
}
|
||||||
@ -203,13 +197,10 @@ object Helper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getBitmap(context: Context, drawableId: Int): Bitmap {
|
fun getBitmap(context: Context, drawableId: Int): Bitmap {
|
||||||
val drawable = ContextCompat.getDrawable(context, drawableId)
|
return when (val drawable = ContextCompat.getDrawable(context, drawableId)) {
|
||||||
return if (drawable is BitmapDrawable) {
|
is BitmapDrawable -> BitmapFactory.decodeResource(context.resources, drawableId)
|
||||||
BitmapFactory.decodeResource(context.resources, drawableId)
|
is VectorDrawable -> getBitmap(drawable)
|
||||||
} else if (drawable is VectorDrawable) {
|
else -> throw IllegalArgumentException("unsupported drawable type")
|
||||||
getBitmap(drawable)
|
|
||||||
} else {
|
|
||||||
throw IllegalArgumentException("unsupported drawable type")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +259,6 @@ object Helper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun getClipBoardText(context: Context): String? {
|
fun getClipBoardText(context: Context): String? {
|
||||||
val clipboardManager =
|
val clipboardManager =
|
||||||
context.getSystemService(Context.CLIPBOARD_SERVICE) as 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 ?
|
// TODO make the log levels refer to the WalletManagerFactory::LogLevel enum ?
|
||||||
fun initLogger(context: Context, level: Int) {
|
fun initLogger(context: Context, level: Int) {
|
||||||
val home = getStorage(context, MONERO_DIR).absolutePath
|
val home = getStorage(context, MONERO_DIR).absolutePath
|
||||||
initLogger("$home/monerujo", "monerujo.log")
|
WalletManager.initLogger("$home/monerujo", "monerujo.log")
|
||||||
if (level >= WalletManager.LOGLEVEL_SILENT) setLogLevel(level)
|
if (level >= WalletManager.LOGLEVEL_SILENT) WalletManager.setLogLevel(level)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -34,7 +34,6 @@ object ThemeHelper {
|
|||||||
) typedValue.resourceId else 0
|
) typedValue.resourceId else 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@ColorInt
|
@ColorInt
|
||||||
fun getThemedColor(ctx: Context, attrId: Int): Int {
|
fun getThemedColor(ctx: Context, attrId: Int): Int {
|
||||||
val typedValue = TypedValue()
|
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 New Issue
Block a user