From d4cb982b945c5725168077652ea3ae9df75af4a8 Mon Sep 17 00:00:00 2001 From: pokkst Date: Wed, 6 Dec 2023 17:13:44 -0600 Subject: [PATCH] Cleanup more Kotlin code, and convert SendFragment --- .../java/net/mynero/wallet/MainActivity.kt | 1 - .../wallet/adapter/NodeSelectionAdapter.kt | 15 +- .../wallet/adapter/SubaddressAdapter.kt | 3 +- .../wallet/adapter/TransactionInfoAdapter.kt | 19 +- .../net/mynero/wallet/data/DefaultNodes.kt | 4 +- .../java/net/mynero/wallet/data/Subaddress.kt | 1 - .../java/net/mynero/wallet/data/UserNotes.kt | 79 -- .../dialog/AddNodeBottomSheetDialog.kt | 3 +- .../EditAddressLabelBottomSheetDialog.kt | 2 - .../dialog/EditNodeBottomSheetDialog.kt | 6 +- .../dialog/NodeSelectionBottomSheetDialog.kt | 6 +- .../dialog/PasswordBottomSheetDialog.kt | 1 + .../fragment/dialog/SendBottomSheetDialog.kt | 6 +- .../wallet/fragment/home/HomeFragment.kt | 9 +- .../fragment/receive/ReceiveFragment.kt | 9 +- .../wallet/fragment/send/SendFragment.kt | 781 +++++++++--------- .../wallet/fragment/send/SendViewModel.kt | 40 +- .../mynero/wallet/model/TransactionInfo.kt | 309 +++---- .../java/net/mynero/wallet/model/Wallet.kt | 3 - .../mynero/wallet/service/BalanceService.kt | 2 - .../wallet/service/BlockchainService.kt | 1 + .../net/mynero/wallet/service/TxService.kt | 1 - .../net/mynero/wallet/service/UTXOService.kt | 4 +- .../java/net/mynero/wallet/util/Helper.kt | 22 +- .../net/mynero/wallet/util/ThemeHelper.kt | 1 - .../net/mynero/wallet/util/HelperTest.java | 97 --- .../mynero/wallet/util/RestoreHeightTest.java | 133 --- .../net/mynero/wallet/util/UserNoteTest.java | 98 --- 28 files changed, 647 insertions(+), 1009 deletions(-) delete mode 100644 app/src/main/java/net/mynero/wallet/data/UserNotes.kt delete mode 100644 app/src/test/java/net/mynero/wallet/util/HelperTest.java delete mode 100644 app/src/test/java/net/mynero/wallet/util/RestoreHeightTest.java delete mode 100644 app/src/test/java/net/mynero/wallet/util/UserNoteTest.java diff --git a/app/src/main/java/net/mynero/wallet/MainActivity.kt b/app/src/main/java/net/mynero/wallet/MainActivity.kt index 7248593..596e9e0 100644 --- a/app/src/main/java/net/mynero/wallet/MainActivity.kt +++ b/app/src/main/java/net/mynero/wallet/MainActivity.kt @@ -23,7 +23,6 @@ import net.mynero.wallet.util.UriData import java.io.File class MainActivity : AppCompatActivity(), MoneroHandlerThread.Listener, PasswordListener { - @JvmField val restartEvents: SingleLiveEvent<*> = SingleLiveEvent() var thread: MoneroHandlerThread? = null private set diff --git a/app/src/main/java/net/mynero/wallet/adapter/NodeSelectionAdapter.kt b/app/src/main/java/net/mynero/wallet/adapter/NodeSelectionAdapter.kt index 498c519..c38b893 100644 --- a/app/src/main/java/net/mynero/wallet/adapter/NodeSelectionAdapter.kt +++ b/app/src/main/java/net/mynero/wallet/adapter/NodeSelectionAdapter.kt @@ -21,6 +21,7 @@ import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import android.widget.Toast +import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import net.mynero.wallet.R import net.mynero.wallet.data.DefaultNodes @@ -83,9 +84,19 @@ class NodeSelectionAdapter(val listener: NodeSelectionAdapterListener?) : val currentNode = PrefService.instance?.node val match = node == currentNode if (match) { - itemView.setBackgroundColor(itemView.resources.getColor(R.color.oled_colorSecondary)) + itemView.setBackgroundColor( + ContextCompat.getColor( + itemView.context, + R.color.oled_colorSecondary + ) + ) } else { - itemView.setBackgroundColor(itemView.resources.getColor(android.R.color.transparent)) + itemView.setBackgroundColor( + ContextCompat.getColor( + itemView.context, + android.R.color.transparent + ) + ) } val nodeNameTextView = itemView.findViewById(R.id.node_name_textview) val nodeAddressTextView = itemView.findViewById(R.id.node_uri_textview) diff --git a/app/src/main/java/net/mynero/wallet/adapter/SubaddressAdapter.kt b/app/src/main/java/net/mynero/wallet/adapter/SubaddressAdapter.kt index cb98e57..433a47a 100644 --- a/app/src/main/java/net/mynero/wallet/adapter/SubaddressAdapter.kt +++ b/app/src/main/java/net/mynero/wallet/adapter/SubaddressAdapter.kt @@ -25,7 +25,6 @@ import net.mynero.wallet.data.Subaddress import net.mynero.wallet.service.PrefService import net.mynero.wallet.util.Constants import net.mynero.wallet.util.Helper -import net.mynero.wallet.util.Helper.getDisplayAmount class SubaddressAdapter(val listener: SubaddressAdapterListener?) : RecyclerView.Adapter() { @@ -101,7 +100,7 @@ class SubaddressAdapter(val listener: SubaddressAdapterListener?) : } else { addressAmountTextView.text = itemView.context.getString( R.string.tx_list_amount_positive, - getDisplayAmount(amount, Helper.DISPLAY_DIGITS_INFO) + Helper.getDisplayAmount(amount, Helper.DISPLAY_DIGITS_INFO) ) } } else addressAmountTextView.text = "" diff --git a/app/src/main/java/net/mynero/wallet/adapter/TransactionInfoAdapter.kt b/app/src/main/java/net/mynero/wallet/adapter/TransactionInfoAdapter.kt index 813fea0..e927523 100644 --- a/app/src/main/java/net/mynero/wallet/adapter/TransactionInfoAdapter.kt +++ b/app/src/main/java/net/mynero/wallet/adapter/TransactionInfoAdapter.kt @@ -27,8 +27,7 @@ import net.mynero.wallet.service.PrefService import net.mynero.wallet.util.Constants import net.mynero.wallet.util.DateHelper.DATETIME_FORMATTER import net.mynero.wallet.util.Helper -import net.mynero.wallet.util.Helper.getDisplayAmount -import net.mynero.wallet.util.ThemeHelper.getThemedColor +import net.mynero.wallet.util.ThemeHelper import java.util.Calendar import java.util.Date @@ -84,10 +83,10 @@ class TransactionInfoAdapter(val listener: TxInfoAdapterListener?) : private var amountTextView: TextView? = null init { - inboundColour = getThemedColor(view.context, R.attr.positiveColor) - outboundColour = getThemedColor(view.context, R.attr.negativeColor) - pendingColour = getThemedColor(view.context, R.attr.neutralColor) - failedColour = getThemedColor(view.context, R.attr.neutralColor) + inboundColour = ThemeHelper.getThemedColor(view.context, R.attr.positiveColor) + outboundColour = ThemeHelper.getThemedColor(view.context, R.attr.negativeColor) + pendingColour = ThemeHelper.getThemedColor(view.context, R.attr.neutralColor) + failedColour = ThemeHelper.getThemedColor(view.context, R.attr.neutralColor) val cal = Calendar.getInstance() val tz = cal.timeZone //get the local time zone. DATETIME_FORMATTER.timeZone = tz @@ -97,7 +96,7 @@ class TransactionInfoAdapter(val listener: TxInfoAdapterListener?) : val streetModeEnabled = PrefService.instance?.getBoolean(Constants.PREF_STREET_MODE, false) val displayAmount = - if (streetModeEnabled == true) Constants.STREET_MODE_BALANCE else getDisplayAmount( + if (streetModeEnabled == true) Constants.STREET_MODE_BALANCE else Helper.getDisplayAmount( txInfo.amount, Helper.DISPLAY_DIGITS_INFO ) @@ -108,7 +107,7 @@ class TransactionInfoAdapter(val listener: TxInfoAdapterListener?) : amountTextView = itemView.findViewById(R.id.tx_amount) itemView.findViewById(R.id.tx_failed).visibility = View.GONE if (txInfo.isFailed) { - (itemView.findViewById(R.id.tx_amount) as TextView).text = + amountTextView?.text = itemView.context.getString(R.string.tx_list_amount_negative, displayAmount) itemView.findViewById(R.id.tx_failed).visibility = View.VISIBLE setTxColour(failedColour) @@ -140,10 +139,10 @@ class TransactionInfoAdapter(val listener: TxInfoAdapterListener?) : confirmationsTextView.visibility = View.GONE } if (txInfo.direction == TransactionInfo.Direction.Direction_Out) { - (itemView.findViewById(R.id.tx_amount) as TextView).text = + amountTextView?.text = itemView.context.getString(R.string.tx_list_amount_negative, displayAmount) } else { - (itemView.findViewById(R.id.tx_amount) as TextView).text = + amountTextView?.text = itemView.context.getString(R.string.tx_list_amount_positive, displayAmount) } (itemView.findViewById(R.id.tx_datetime) as TextView).text = diff --git a/app/src/main/java/net/mynero/wallet/data/DefaultNodes.kt b/app/src/main/java/net/mynero/wallet/data/DefaultNodes.kt index 0106b41..6e5c594 100644 --- a/app/src/main/java/net/mynero/wallet/data/DefaultNodes.kt +++ b/app/src/main/java/net/mynero/wallet/data/DefaultNodes.kt @@ -85,13 +85,13 @@ enum class DefaultNodes( "mainnet", "Criminales78.onion" ), - xmrfail( + Xmrfail( "mxcd4577fldb3ppzy7obmmhnu3tf57gbcbd4qhwr2kxyjj2qi3dnbfqd.onion", 18081, "mainnet", "xmrfail.onion" ), - boldsuck( + Boldsuck( "6dsdenp6vjkvqzy4wzsnzn6wixkdzihx3khiumyzieauxuxslmcaeiad.onion", 18081, "mainnet", diff --git a/app/src/main/java/net/mynero/wallet/data/Subaddress.kt b/app/src/main/java/net/mynero/wallet/data/Subaddress.kt index 5642970..90cbfa6 100644 --- a/app/src/main/java/net/mynero/wallet/data/Subaddress.kt +++ b/app/src/main/java/net/mynero/wallet/data/Subaddress.kt @@ -38,7 +38,6 @@ class Subaddress( ) "#$addressIndex" else label companion object { - @JvmField val DEFAULT_LABEL_FORMATTER: Pattern = Pattern.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}:[0-9]{2}:[0-9]{2}$") } diff --git a/app/src/main/java/net/mynero/wallet/data/UserNotes.kt b/app/src/main/java/net/mynero/wallet/data/UserNotes.kt deleted file mode 100644 index 41a6cf7..0000000 --- a/app/src/main/java/net/mynero/wallet/data/UserNotes.kt +++ /dev/null @@ -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() - } -} \ No newline at end of file diff --git a/app/src/main/java/net/mynero/wallet/fragment/dialog/AddNodeBottomSheetDialog.kt b/app/src/main/java/net/mynero/wallet/fragment/dialog/AddNodeBottomSheetDialog.kt index 9abfbe9..3b929d9 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/dialog/AddNodeBottomSheetDialog.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/dialog/AddNodeBottomSheetDialog.kt @@ -114,7 +114,8 @@ class AddNodeBottomSheetDialog : BottomSheetDialogFragment() { Toast.makeText(context, "Node already exists", Toast.LENGTH_SHORT).show() return } - PrefService.instance?.edit()?.putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString())?.apply() + PrefService.instance?.edit()?.putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString()) + ?.apply() } private fun addPasteListener(root: View, editText: EditText, layoutId: Int) { diff --git a/app/src/main/java/net/mynero/wallet/fragment/dialog/EditAddressLabelBottomSheetDialog.kt b/app/src/main/java/net/mynero/wallet/fragment/dialog/EditAddressLabelBottomSheetDialog.kt index ecd5b3d..5a192db 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/dialog/EditAddressLabelBottomSheetDialog.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/dialog/EditAddressLabelBottomSheetDialog.kt @@ -17,9 +17,7 @@ import java.text.SimpleDateFormat import java.util.Locale class EditAddressLabelBottomSheetDialog : BottomSheetDialogFragment() { - @JvmField var listener: LabelListener? = null - @JvmField var addressIndex = 0 override fun onCreateView( inflater: LayoutInflater, diff --git a/app/src/main/java/net/mynero/wallet/fragment/dialog/EditNodeBottomSheetDialog.kt b/app/src/main/java/net/mynero/wallet/fragment/dialog/EditNodeBottomSheetDialog.kt index 67f7887..86f2328 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/dialog/EditNodeBottomSheetDialog.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/dialog/EditNodeBottomSheetDialog.kt @@ -21,6 +21,7 @@ import org.json.JSONObject class EditNodeBottomSheetDialog : BottomSheetDialogFragment() { @JvmField var listener: EditNodeListener? = null + @JvmField var node: Node? = null override fun onCreateView( @@ -95,8 +96,9 @@ class EditNodeBottomSheetDialog : BottomSheetDialogFragment() { jsonObject.put("username", user) jsonObject.put("password", pass) } - if (nodeAddr.contains(":") && !nodeAddr.startsWith("[") && !nodeAddr.endsWith("]")) nodeAddr = - "[$nodeAddr]" + if (nodeAddr.contains(":") && !nodeAddr.startsWith("[") && !nodeAddr.endsWith("]")) + nodeAddr = "[$nodeAddr]" + jsonObject.put("host", nodeAddr) jsonObject.put("rpcPort", portString.toInt()) jsonObject.put("network", "mainnet") diff --git a/app/src/main/java/net/mynero/wallet/fragment/dialog/NodeSelectionBottomSheetDialog.kt b/app/src/main/java/net/mynero/wallet/fragment/dialog/NodeSelectionBottomSheetDialog.kt index fdc44bb..cf8747d 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/dialog/NodeSelectionBottomSheetDialog.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/dialog/NodeSelectionBottomSheetDialog.kt @@ -82,7 +82,8 @@ class NodeSelectionBottomSheetDialog : BottomSheetDialogFragment(), NodeSelectio } } } - PrefService.instance?.edit()?.putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString())?.apply() + PrefService.instance?.edit()?.putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString()) + ?.apply() for (defaultNode in DefaultNodes.values()) { fromJson(defaultNode.json)?.let { nodes.add(it) } } @@ -98,7 +99,8 @@ class NodeSelectionBottomSheetDialog : BottomSheetDialogFragment(), NodeSelectio Toast.LENGTH_SHORT ).show() } - PrefService.instance?.edit()?.putString(Constants.PREF_NODE_2, node?.toJson().toString())?.apply() + PrefService.instance?.edit()?.putString(Constants.PREF_NODE_2, node?.toJson().toString()) + ?.apply() WalletManager.instance?.setDaemon(node) adapter?.updateSelectedNode() listener?.onNodeSelected() diff --git a/app/src/main/java/net/mynero/wallet/fragment/dialog/PasswordBottomSheetDialog.kt b/app/src/main/java/net/mynero/wallet/fragment/dialog/PasswordBottomSheetDialog.kt index 71bf7f3..dacb8e9 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/dialog/PasswordBottomSheetDialog.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/dialog/PasswordBottomSheetDialog.kt @@ -17,6 +17,7 @@ import java.io.File class PasswordBottomSheetDialog : BottomSheetDialogFragment() { @JvmField var listener: PasswordListener? = null + @JvmField var cancelable = false override fun onCreateView( diff --git a/app/src/main/java/net/mynero/wallet/fragment/dialog/SendBottomSheetDialog.kt b/app/src/main/java/net/mynero/wallet/fragment/dialog/SendBottomSheetDialog.kt index 73c724b..bdd0ede 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/dialog/SendBottomSheetDialog.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/dialog/SendBottomSheetDialog.kt @@ -14,7 +14,6 @@ import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.Observer import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.zxing.client.android.Intents import com.journeyapps.barcodescanner.ScanContract @@ -22,7 +21,6 @@ import com.journeyapps.barcodescanner.ScanIntentResult import com.journeyapps.barcodescanner.ScanOptions import net.mynero.wallet.MoneroApplication import net.mynero.wallet.R -import net.mynero.wallet.model.BalanceInfo import net.mynero.wallet.model.PendingTransaction import net.mynero.wallet.model.Wallet import net.mynero.wallet.model.Wallet.Companion.getAmountFromString @@ -40,10 +38,12 @@ import net.mynero.wallet.util.UriData.Companion.parse class SendBottomSheetDialog : BottomSheetDialogFragment() { private val _sendingMax = MutableLiveData(false) private val _pendingTransaction = MutableLiveData(null) + @JvmField var selectedUtxos = ArrayList() private var sendingMax: LiveData = _sendingMax private var pendingTransaction: LiveData = _pendingTransaction + @JvmField var uriData: UriData? = null private val cameraPermissionsLauncher = registerForActivityResult( @@ -56,8 +56,10 @@ class SendBottomSheetDialog : BottomSheetDialogFragment() { .show() } } + @JvmField var isChurning = false + @JvmField var listener: Listener? = null var priority: PendingTransaction.Priority = PendingTransaction.Priority.Priority_Low diff --git a/app/src/main/java/net/mynero/wallet/fragment/home/HomeFragment.kt b/app/src/main/java/net/mynero/wallet/fragment/home/HomeFragment.kt index 46a4233..27bedf8 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/home/HomeFragment.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/home/HomeFragment.kt @@ -25,7 +25,6 @@ import net.mynero.wallet.service.BlockchainService import net.mynero.wallet.service.HistoryService import net.mynero.wallet.service.PrefService import net.mynero.wallet.util.Constants -import kotlin.math.roundToInt class HomeFragment : Fragment(), TxInfoAdapterListener { private var startHeight: Long = 0 @@ -124,11 +123,11 @@ class HomeFragment : Fragment(), TxInfoAdapterListener { displayEmptyHistory(true, view, textResId, botImgResId) } else { // POPULATED WALLET HISTORY - history.sorted() - if (history.size > 100) { - adapter.submitList(history.subList(0, 99)) + val sortedHistory = history.sortedByDescending { it.timestamp } + if (sortedHistory.size > 100) { + adapter.submitList(sortedHistory.subList(0, 99)) } else { - adapter.submitList(history) + adapter.submitList(sortedHistory) } txHistoryRecyclerView.visibility = View.VISIBLE displayEmptyHistory( diff --git a/app/src/main/java/net/mynero/wallet/fragment/receive/ReceiveFragment.kt b/app/src/main/java/net/mynero/wallet/fragment/receive/ReceiveFragment.kt index 434a64d..c51d030 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/receive/ReceiveFragment.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/receive/ReceiveFragment.kt @@ -8,6 +8,7 @@ import android.view.ViewGroup import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView +import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager @@ -120,7 +121,8 @@ class ReceiveFragment : Fragment() { private fun generate(text: String, width: Int, height: Int): Bitmap? { if (width <= 0 || height <= 0) return null - val hints: MutableMap = EnumMap(com.google.zxing.EncodeHintType::class.java) + val hints: MutableMap = + EnumMap(com.google.zxing.EncodeHintType::class.java) hints[EncodeHintType.CHARACTER_SET] = StandardCharsets.UTF_8 hints[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.M try { @@ -131,7 +133,10 @@ class ReceiveFragment : Fragment() { if (bitMatrix[j, i]) { pixels[i * width + j] = -0x1 } else { - pixels[i * height + j] = resources.getColor(R.color.oled_colorBackground) + context?.let { ctx -> + pixels[i * height + j] = + ContextCompat.getColor(ctx, R.color.oled_colorBackground) + } } } } diff --git a/app/src/main/java/net/mynero/wallet/fragment/send/SendFragment.kt b/app/src/main/java/net/mynero/wallet/fragment/send/SendFragment.kt index 067d28f..8086f8c 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/send/SendFragment.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/send/SendFragment.kt @@ -1,485 +1,520 @@ -package net.mynero.wallet.fragment.send; +package net.mynero.wallet.fragment.send -import android.app.Activity; -import android.content.Context; -import android.os.Bundle; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.RadioGroup; -import android.widget.TextView; -import android.widget.Toast; +import android.app.Activity +import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.EditText +import android.widget.ImageButton +import android.widget.LinearLayout +import android.widget.RadioGroup +import android.widget.TextView +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import com.google.zxing.client.android.Intents +import com.journeyapps.barcodescanner.ScanContract +import com.journeyapps.barcodescanner.ScanIntentResult +import com.journeyapps.barcodescanner.ScanOptions +import com.ncorti.slidetoact.SlideToActView +import com.ncorti.slidetoact.SlideToActView.OnSlideCompleteListener +import net.mynero.wallet.MoneroApplication +import net.mynero.wallet.R +import net.mynero.wallet.model.PendingTransaction +import net.mynero.wallet.model.Wallet +import net.mynero.wallet.service.BalanceService +import net.mynero.wallet.service.TxService +import net.mynero.wallet.util.Helper +import net.mynero.wallet.util.UriData -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.constraintlayout.widget.ConstraintLayout; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.lifecycle.ViewModelProvider; - -import com.google.zxing.client.android.Intents; -import com.journeyapps.barcodescanner.ScanContract; -import com.journeyapps.barcodescanner.ScanOptions; -import com.ncorti.slidetoact.SlideToActView; - -import net.mynero.wallet.MoneroApplication; -import net.mynero.wallet.R; -import net.mynero.wallet.model.PendingTransaction; -import net.mynero.wallet.model.Wallet; -import net.mynero.wallet.service.BalanceService; -import net.mynero.wallet.service.TxService; -import net.mynero.wallet.util.Helper; -import net.mynero.wallet.util.UriData; - -import java.util.ArrayList; -import java.util.List; - -import kotlin.Pair; - -public class SendFragment extends Fragment { - public PendingTransaction.Priority priority; - private SendViewModel mViewModel; - private Button sendMaxButton; - private ImageButton addOutputImageView; - private LinearLayout destList; - private LayoutInflater inflater; - private Button createButton; - private SlideToActView sendTxSlider; - private RadioGroup feeRadioGroup; - private TextView feeRadioGroupLabelTextView; - private TextView feeTextView; - private TextView addressTextView; - private TextView amountTextView; - private int currentEntryIndex = -1; - private final ActivityResultLauncher qrCodeLauncher = registerForActivityResult(new ScanContract(), result -> { - if (result.getContents() != null) { - if (currentEntryIndex != -1) { - pasteAddress(getDestView(currentEntryIndex), result.getContents(), false); - currentEntryIndex = -1; - } - } - }); - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_send, container, false); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - mViewModel = new ViewModelProvider(this).get(SendViewModel.class); - sendMaxButton = view.findViewById(R.id.send_max_button); - addOutputImageView = view.findViewById(R.id.add_output_button); - destList = view.findViewById(R.id.transaction_destination_list); - createButton = view.findViewById(R.id.create_tx_button); - feeRadioGroup = view.findViewById(R.id.tx_fee_radiogroup); - feeTextView = view.findViewById(R.id.fee_textview); - sendTxSlider = view.findViewById(R.id.send_tx_slider); - addressTextView = view.findViewById(R.id.address_pending_textview); - amountTextView = view.findViewById(R.id.amount_pending_textview); - feeRadioGroup = view.findViewById(R.id.tx_fee_radiogroup); - feeRadioGroupLabelTextView = view.findViewById(R.id.tx_fee_radiogroup_label_textview); - - FragmentActivity activity = getActivity(); - if (activity != null) { - inflater = activity.getLayoutInflater(); - } - bindListeners(); - bindObservers(); - init(); - } private final ActivityResultLauncher cameraPermissionsLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), - granted -> { - if (granted) { - onScan(currentEntryIndex); - } else { - Toast.makeText(getActivity(), getString(R.string.no_camera_permission), Toast.LENGTH_SHORT).show(); - currentEntryIndex = -1; +class SendFragment : Fragment() { + var priority: PendingTransaction.Priority = PendingTransaction.Priority.Priority_Low + private var mViewModel: SendViewModel? = null + private var sendMaxButton: Button? = null + private var addOutputImageView: ImageButton? = null + private var destList: LinearLayout? = null + private var inflater: LayoutInflater? = null + private var createButton: Button? = null + private var sendTxSlider: SlideToActView? = null + private var feeRadioGroup: RadioGroup? = null + private var feeRadioGroupLabelTextView: TextView? = null + private var feeTextView: TextView? = null + private var addressTextView: TextView? = null + private var amountTextView: TextView? = null + private var currentEntryIndex = -1 + private val qrCodeLauncher = + registerForActivityResult(ScanContract()) { result: ScanIntentResult -> + if (result.contents != null) { + if (currentEntryIndex != -1) { + pasteAddress(getDestView(currentEntryIndex), result.contents, false) + currentEntryIndex = -1 } - }); - - private void init() { - addOutput(true); + } + } + private val cameraPermissionsLauncher = registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { granted: Boolean -> + if (granted) { + onScan(currentEntryIndex) + } else { + Toast.makeText(activity, getString(R.string.no_camera_permission), Toast.LENGTH_SHORT) + .show() + currentEntryIndex = -1 + } } - private void bindListeners() { - feeRadioGroup.check(R.id.low_fee_radiobutton); - priority = PendingTransaction.Priority.Priority_Low; - feeRadioGroup.setOnCheckedChangeListener((radioGroup, i) -> { - if (i == R.id.low_fee_radiobutton) { - priority = PendingTransaction.Priority.Priority_Low; - } else if (i == R.id.med_fee_radiobutton) { - priority = PendingTransaction.Priority.Priority_Medium; - } else if (i == R.id.high_fee_radiobutton) { - priority = PendingTransaction.Priority.Priority_High; - } - }); + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_send, container, false) + } - addOutputImageView.setOnClickListener(view1 -> { - sendMaxButton.setVisibility(View.GONE); - int outputCount = getDestCount(); + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mViewModel = ViewModelProvider(this)[SendViewModel::class.java] + sendMaxButton = view.findViewById(R.id.send_max_button) + addOutputImageView = view.findViewById(R.id.add_output_button) + destList = view.findViewById(R.id.transaction_destination_list) + createButton = view.findViewById(R.id.create_tx_button) + feeRadioGroup = view.findViewById(R.id.tx_fee_radiogroup) + feeTextView = view.findViewById(R.id.fee_textview) + sendTxSlider = view.findViewById(R.id.send_tx_slider) + addressTextView = view.findViewById(R.id.address_pending_textview) + amountTextView = view.findViewById(R.id.amount_pending_textview) + feeRadioGroup = view.findViewById(R.id.tx_fee_radiogroup) + feeRadioGroupLabelTextView = view.findViewById(R.id.tx_fee_radiogroup_label_textview) + inflater = activity?.layoutInflater + bindListeners() + bindObservers() + init() + } + + private fun init() { + addOutput(true) + } + + private fun bindListeners() { + feeRadioGroup?.check(R.id.low_fee_radiobutton) + priority = PendingTransaction.Priority.Priority_Low + feeRadioGroup?.setOnCheckedChangeListener { _: RadioGroup?, i: Int -> + when (i) { + R.id.low_fee_radiobutton -> priority = PendingTransaction.Priority.Priority_Low + R.id.med_fee_radiobutton -> priority = PendingTransaction.Priority.Priority_Medium + R.id.high_fee_radiobutton -> priority = PendingTransaction.Priority.Priority_High + } + } + addOutputImageView?.setOnClickListener { + sendMaxButton?.visibility = View.GONE + val outputCount = destCount if (outputCount < 8) { - addOutput(false); + addOutput(false) } else { - Toast.makeText(getActivity(), getString(R.string.max_outputs_allowed), Toast.LENGTH_SHORT).show(); + Toast.makeText( + activity, + getString(R.string.max_outputs_allowed), + Toast.LENGTH_SHORT + ).show() } - }); - sendMaxButton.setOnClickListener(view1 -> { - mViewModel.setSendingMax(!isSendAll()); - }); - createButton.setOnClickListener(view1 -> { - boolean outputsValid = checkDestsValidity(isSendAll()); - + } + sendMaxButton?.setOnClickListener { mViewModel?.setSendingMax(!isSendAll) } + createButton?.setOnClickListener { + val outputsValid = checkDestsValidity(isSendAll) if (outputsValid) { - Toast.makeText(getActivity(), getString(R.string.creating_tx), Toast.LENGTH_SHORT).show(); - createButton.setEnabled(false); - sendMaxButton.setEnabled(false); - createTx(getRawDests(), isSendAll(), priority); + Toast.makeText(activity, getString(R.string.creating_tx), Toast.LENGTH_SHORT).show() + createButton?.isEnabled = false + sendMaxButton?.isEnabled = false + createTx(rawDests, isSendAll, priority) } else { - Toast.makeText(getActivity(), getString(R.string.creating_tx_failed_invalid_outputs), Toast.LENGTH_SHORT).show(); + Toast.makeText( + activity, + getString(R.string.creating_tx_failed_invalid_outputs), + Toast.LENGTH_SHORT + ).show() } - }); - - sendTxSlider.setOnSlideCompleteListener(slideToActView -> { - PendingTransaction pendingTx = mViewModel.pendingTransaction.getValue(); - if (pendingTx != null) { - Toast.makeText(getActivity(), getString(R.string.sending_tx), Toast.LENGTH_SHORT).show(); - sendTx(pendingTx); + } + sendTxSlider?.onSlideCompleteListener = + object : OnSlideCompleteListener { + override fun onSlideComplete(view: SlideToActView) { + val pendingTx = mViewModel?.pendingTransaction?.value ?: return + Toast.makeText(activity, getString(R.string.sending_tx), Toast.LENGTH_SHORT) + .show() + sendTx(pendingTx) + } } - }); } - private boolean checkDestsValidity(boolean sendAll) { - List> dests = getRawDests(); - for (Pair dest : dests) { - String address = dest.component1(); - String amount = dest.component2(); + private fun checkDestsValidity(sendAll: Boolean): Boolean { + val dests = rawDests + for (dest in dests) { + val address = dest.component1() + val amount = dest.component2() if (!sendAll) { if (amount.isEmpty()) { - Toast.makeText(getActivity(), getString(R.string.send_amount_empty), Toast.LENGTH_SHORT).show(); - return false; + Toast.makeText( + activity, + getString(R.string.send_amount_empty), + Toast.LENGTH_SHORT + ).show() + return false } - - long amountRaw = Wallet.getAmountFromString(amount); - long balance = BalanceService.instance.getUnlockedBalanceRaw(); + val amountRaw = Wallet.getAmountFromString(amount) + val balance = BalanceService.instance?.unlockedBalanceRaw ?: 0 if (amountRaw >= balance || amountRaw <= 0) { - Toast.makeText(getActivity(), getString(R.string.send_amount_invalid), Toast.LENGTH_SHORT).show(); - return false; + Toast.makeText( + activity, + getString(R.string.send_amount_invalid), + Toast.LENGTH_SHORT + ).show() + return false } - } else if (dests.size() > 1) { - Toast.makeText(getActivity(), getString(R.string.send_amount_invalid_sendall_paytomany), Toast.LENGTH_SHORT).show(); - return false; + } else if (dests.size > 1) { + Toast.makeText( + activity, + getString(R.string.send_amount_invalid_sendall_paytomany), + Toast.LENGTH_SHORT + ).show() + return false } - - UriData uriData = UriData.parse(address); - boolean isValidAddress = uriData != null; + val uriData = UriData.parse(address) + val isValidAddress = uriData != null if (!isValidAddress) { - Toast.makeText(getActivity(), getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show(); - return false; + Toast.makeText( + activity, + getString(R.string.send_address_invalid), + Toast.LENGTH_SHORT + ).show() + return false } - - if (dests.size() > 1 && uriData.hasPaymentId()) { - Toast.makeText(getActivity(), getString(R.string.paymentid_paytomany), Toast.LENGTH_SHORT).show(); - return false; + if (dests.size > 1 && uriData?.hasPaymentId() == true) { + Toast.makeText( + activity, + getString(R.string.paymentid_paytomany), + Toast.LENGTH_SHORT + ).show() + return false } } - - return true; + return true } - private boolean destsHasPaymentId() { - List> dests = getRawDests(); - for (Pair dest : dests) { - String address = dest.component1(); - UriData uriData = UriData.parse(address); - if (uriData == null) return false; - if (uriData.hasPaymentId()) return true; + private fun destsHasPaymentId(): Boolean { + val dests = rawDests + for (dest in dests) { + val address = dest.component1() + val uriData = UriData.parse(address) ?: return false + if (uriData.hasPaymentId()) return true } - return false; + return false } - private void bindObservers() { - mViewModel.sendingMax.observe(getViewLifecycleOwner(), sendingMax -> { - if (mViewModel.pendingTransaction.getValue() == null) { - if (sendingMax) { - prepareOutputsForMaxSend(); - sendMaxButton.setText(getText(R.string.undo)); + private fun bindObservers() { + mViewModel?.sendingMax?.observe(viewLifecycleOwner) { sendingMax: Boolean? -> + if (mViewModel?.pendingTransaction?.value == null) { + if (sendingMax == true) { + prepareOutputsForMaxSend() + sendMaxButton?.text = getText(R.string.undo) } else { - unprepareMaxSend(); - sendMaxButton.setText(getText(R.string.send_max)); + unprepareMaxSend() + sendMaxButton?.text = getText(R.string.send_max) } } - }); - - mViewModel.showAddOutputButton.observe(getViewLifecycleOwner(), show -> { - setAddOutputButtonVisibility((show && !destsHasPaymentId()) ? View.VISIBLE : View.INVISIBLE); - }); - - mViewModel.pendingTransaction.observe(getViewLifecycleOwner(), pendingTx -> { - showConfirmationLayout(pendingTx != null); - + } + mViewModel?.showAddOutputButton?.observe(viewLifecycleOwner) { show: Boolean? -> + setAddOutputButtonVisibility( + if (show == true && !destsHasPaymentId()) View.VISIBLE else View.INVISIBLE + ) + } + mViewModel?.pendingTransaction?.observe(viewLifecycleOwner) { pendingTx: PendingTransaction? -> + showConfirmationLayout(pendingTx != null) if (pendingTx != null) { - String address = getDestCount() == 1 ? getAddressField(0).getText().toString() : "Multiple"; - addressTextView.setText(getString(R.string.tx_address_text, address)); - amountTextView.setText(getString(R.string.tx_amount_text, Helper.getDisplayAmount(pendingTx.getAmount()))); - feeTextView.setText(getString(R.string.tx_fee_text, Helper.getDisplayAmount(pendingTx.getFee()))); + val address = if (destCount == 1) getAddressField(0).text.toString() else "Multiple" + addressTextView?.text = getString(R.string.tx_address_text, address) + amountTextView?.text = + getString(R.string.tx_amount_text, Helper.getDisplayAmount(pendingTx.getAmount())) + feeTextView?.text = + getString(R.string.tx_fee_text, Helper.getDisplayAmount(pendingTx.getFee())) } - }); + } } - private void addOutput(boolean initial) { + private fun addOutput(initial: Boolean) { if (inflater != null) { - int index = getDestCount(); - ConstraintLayout entryView = (ConstraintLayout) inflater.inflate(R.layout.transaction_output_item, null); - ImageButton removeOutputImageButton = entryView.findViewById(R.id.remove_output_imagebutton); - EditText addressField = entryView.findViewById(R.id.address_edittext); - addressField.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + val index = destCount + val entryView = + inflater?.inflate(R.layout.transaction_output_item, null) as ConstraintLayout + val removeOutputImageButton = + entryView.findViewById(R.id.remove_output_imagebutton) + val addressField = entryView.findViewById(R.id.address_edittext) + addressField.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged( + charSequence: CharSequence, + i: Int, + i1: Int, + i2: Int + ) { } - @Override - public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { - } - - @Override - public void afterTextChanged(Editable editable) { - int currentOutputs = getDestCount(); - UriData uriData = UriData.parse(editable.toString()); + override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} + override fun afterTextChanged(editable: Editable) { + val currentOutputs: Int = destCount + val uriData = UriData.parse(editable.toString()) if (uriData != null) { // we have valid address - boolean hasPaymentId = uriData.hasPaymentId(); + val hasPaymentId = uriData.hasPaymentId() if (currentOutputs > 1 && hasPaymentId) { // multiple outputs when pasting/editing in integrated address. this is not allowed - Toast.makeText(getActivity(), getString(R.string.paymentid_paytomany), Toast.LENGTH_SHORT).show(); - addressField.setText(null); + Toast.makeText( + activity, + getString(R.string.paymentid_paytomany), + Toast.LENGTH_SHORT + ).show() + addressField.text = null } else if (currentOutputs == 1 && hasPaymentId) { // show add output button: we are sending to integrated address - mViewModel.setShowAddOutputButton(false); + mViewModel?.setShowAddOutputButton(false) } - } else if (currentOutputs == 1 && !isSendAll()) { + } else if (currentOutputs == 1 && !isSendAll) { // when send-all is false and this is our only dest and address is invalid, then show add output button - mViewModel.setShowAddOutputButton(true); + mViewModel?.setShowAddOutputButton(true) } } - }); - entryView.findViewById(R.id.paste_amount_imagebutton).setOnClickListener(view1 -> { - Context ctx = getContext(); - if (ctx != null) { - String clipboard = Helper.getClipBoardText(ctx); - if (clipboard != null) { - pasteAddress(entryView, clipboard, true); + }) + entryView.findViewById(R.id.paste_amount_imagebutton) + .setOnClickListener { + val ctx = context + if (ctx != null) { + val clipboard = Helper.getClipBoardText(ctx) + if (clipboard != null) { + pasteAddress(entryView, clipboard, true) + } } } - }); - entryView.findViewById(R.id.paste_address_imagebutton).setOnClickListener(view1 -> { - Context ctx = getContext(); - if (ctx != null) { - String clipboard = Helper.getClipBoardText(ctx); - if (clipboard != null) { - pasteAddress(entryView, clipboard, false); + entryView.findViewById(R.id.paste_address_imagebutton) + .setOnClickListener { + val ctx = context + if (ctx != null) { + val clipboard = Helper.getClipBoardText(ctx) + if (clipboard != null) { + pasteAddress(entryView, clipboard, false) + } } } - }); - entryView.findViewById(R.id.scan_address_imagebutton).setOnClickListener(view -> onScan(index)); + entryView.findViewById(R.id.scan_address_imagebutton) + .setOnClickListener { onScan(index) } if (initial) { - removeOutputImageButton.setVisibility(View.INVISIBLE); + removeOutputImageButton.visibility = View.INVISIBLE } else { - removeOutputImageButton.setOnClickListener(view -> { - int currentCount = getDestCount(); + removeOutputImageButton.setOnClickListener { + val currentCount = destCount if (currentCount > 1) { if (currentCount == 2) { - sendMaxButton.setVisibility(View.VISIBLE); + sendMaxButton?.visibility = View.VISIBLE } - destList.removeView(entryView); + destList?.removeView(entryView) } - }); + } } - destList.addView(entryView); + destList?.addView(entryView) } } - private int getDestCount() { - return destList.getChildCount(); - } - - private List> getRawDests() { - ArrayList> dests = new ArrayList<>(); - for (int i = 0; i < getDestCount(); i++) { - ConstraintLayout entryView = getDestView(i); - EditText amountField = entryView.findViewById(R.id.amount_edittext); - EditText addressField = entryView.findViewById(R.id.address_edittext); - String amount = amountField.getText().toString().trim(); - String address = addressField.getText().toString().trim(); - dests.add(new Pair<>(address, amount)); + private val destCount: Int + get() = destList?.childCount ?: -1 + private val rawDests: List> + get() { + val dests = ArrayList>() + for (i in 0 until destCount) { + val entryView = getDestView(i) + val amountField = entryView.findViewById(R.id.amount_edittext) + val addressField = entryView.findViewById(R.id.address_edittext) + val amount = amountField.text.toString().trim { it <= ' ' } + val address = addressField.text.toString().trim { it <= ' ' } + dests.add(Pair(address, amount)) + } + return dests } + private val isSendAll: Boolean + get() = mViewModel?.sendingMax?.value ?: false - return dests; + private fun getDestView(pos: Int): ConstraintLayout { + return destList?.getChildAt(pos) as ConstraintLayout } - private boolean isSendAll() { - return mViewModel.sendingMax.getValue() != null ? mViewModel.sendingMax.getValue() : false; + private fun getAddressField(pos: Int): EditText { + return getDestView(pos).findViewById(R.id.address_edittext) as EditText } - private ConstraintLayout getDestView(int pos) { - return (ConstraintLayout) destList.getChildAt(pos); + private fun unprepareMaxSend() { + val entryView = getDestView(0) + entryView.findViewById(R.id.sending_all_textview).visibility = View.INVISIBLE + entryView.findViewById(R.id.amount_edittext).visibility = + View.VISIBLE } - private EditText getAddressField(int pos) { - return (EditText) getDestView(pos).findViewById(R.id.address_edittext); + private fun prepareOutputsForMaxSend() { + val entryView = getDestView(0) + entryView.findViewById(R.id.sending_all_textview).visibility = View.VISIBLE + entryView.findViewById(R.id.amount_edittext).visibility = + View.INVISIBLE } - private void unprepareMaxSend() { - ConstraintLayout entryView = getDestView(0); - entryView.findViewById(R.id.sending_all_textview).setVisibility(View.INVISIBLE); - entryView.findViewById(R.id.amount_edittext).setVisibility(View.VISIBLE); - } - - private void prepareOutputsForMaxSend() { - ConstraintLayout entryView = getDestView(0); - entryView.findViewById(R.id.sending_all_textview).setVisibility(View.VISIBLE); - entryView.findViewById(R.id.amount_edittext).setVisibility(View.INVISIBLE); - } - - private void showConfirmationLayout(boolean show) { + private fun showConfirmationLayout(show: Boolean) { if (show) { - destList.setVisibility(View.GONE); - setAddOutputButtonVisibility(View.GONE); - sendMaxButton.setVisibility(View.GONE); - createButton.setVisibility(View.GONE); - feeRadioGroup.setVisibility(View.GONE); - feeRadioGroupLabelTextView.setVisibility(View.GONE); - - sendTxSlider.setVisibility(View.VISIBLE); - feeTextView.setVisibility(View.VISIBLE); - addressTextView.setVisibility(View.VISIBLE); - amountTextView.setVisibility(View.VISIBLE); + destList?.visibility = View.GONE + setAddOutputButtonVisibility(View.GONE) + sendMaxButton?.visibility = View.GONE + createButton?.visibility = View.GONE + feeRadioGroup?.visibility = View.GONE + feeRadioGroupLabelTextView?.visibility = View.GONE + sendTxSlider?.visibility = View.VISIBLE + feeTextView?.visibility = View.VISIBLE + addressTextView?.visibility = View.VISIBLE + amountTextView?.visibility = View.VISIBLE } else { - destList.setVisibility(View.VISIBLE); - setAddOutputButtonVisibility(View.VISIBLE); - sendMaxButton.setVisibility(View.VISIBLE); - createButton.setVisibility(View.VISIBLE); - feeRadioGroup.setVisibility(View.VISIBLE); - feeRadioGroupLabelTextView.setVisibility(View.VISIBLE); - - sendTxSlider.setVisibility(View.GONE); - feeTextView.setVisibility(View.GONE); - addressTextView.setVisibility(View.GONE); - amountTextView.setVisibility(View.GONE); + destList?.visibility = View.VISIBLE + setAddOutputButtonVisibility(View.VISIBLE) + sendMaxButton?.visibility = View.VISIBLE + createButton?.visibility = View.VISIBLE + feeRadioGroup?.visibility = View.VISIBLE + feeRadioGroupLabelTextView?.visibility = View.VISIBLE + sendTxSlider?.visibility = View.GONE + feeTextView?.visibility = View.GONE + addressTextView?.visibility = View.GONE + amountTextView?.visibility = View.GONE } } - private void onScan(int index) { - currentEntryIndex = index; - if (Helper.getCameraPermission(getActivity(), cameraPermissionsLauncher)) { - ScanOptions options = new ScanOptions(); - options.setBeepEnabled(false); - options.setOrientationLocked(true); - options.setDesiredBarcodeFormats(List.of(Intents.Scan.QR_CODE_MODE)); - options.addExtra(Intents.Scan.SCAN_TYPE, Intents.Scan.MIXED_SCAN); - qrCodeLauncher.launch(options); + private fun onScan(index: Int) { + currentEntryIndex = index + if (activity?.let { Helper.getCameraPermission(it, cameraPermissionsLauncher) } == true) { + val options = ScanOptions() + options.setBeepEnabled(false) + options.setOrientationLocked(true) + options.setDesiredBarcodeFormats(listOf(Intents.Scan.QR_CODE_MODE)) + options.addExtra(Intents.Scan.SCAN_TYPE, Intents.Scan.MIXED_SCAN) + qrCodeLauncher.launch(options) } } - private void pasteAddress(ConstraintLayout entryView, String clipboard, boolean pastingAmount) { + private fun pasteAddress( + entryView: ConstraintLayout, + clipboard: String, + pastingAmount: Boolean + ) { if (pastingAmount) { try { - Double.parseDouble(clipboard); - setAmount(entryView, clipboard); - } catch (Exception e) { - Toast.makeText(getActivity(), getString(R.string.send_amount_invalid), Toast.LENGTH_SHORT).show(); - return; + clipboard.toDouble() + setAmount(entryView, clipboard) + } catch (e: Exception) { + Toast.makeText( + activity, + getString(R.string.send_amount_invalid), + Toast.LENGTH_SHORT + ).show() + return } } - - UriData uriData = UriData.parse(clipboard); + val uriData = UriData.parse(clipboard) if (uriData != null) { - int currentOutputs = getDestCount(); + val currentOutputs = destCount if (currentOutputs > 1 && uriData.hasPaymentId()) { - Toast.makeText(getActivity(), getString(R.string.paymentid_paytomany), Toast.LENGTH_SHORT).show(); - return; + Toast.makeText( + activity, + getString(R.string.paymentid_paytomany), + Toast.LENGTH_SHORT + ).show() + return } else if (currentOutputs == 1 && uriData.hasPaymentId()) { - mViewModel.setShowAddOutputButton(false); + mViewModel?.setShowAddOutputButton(false) } - EditText addressField = entryView.findViewById(R.id.address_edittext); - addressField.setText(uriData.address); + val addressField = entryView.findViewById(R.id.address_edittext) + addressField.setText(uriData.address) if (uriData.hasAmount()) { - setAmount(entryView, uriData.getAmount()); + setAmount(entryView, uriData.amount) } } else { - Toast.makeText(getActivity(), getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show(); + Toast.makeText(activity, getString(R.string.send_address_invalid), Toast.LENGTH_SHORT) + .show() } } - private void setAmount(ConstraintLayout entryView, String amount) { - sendMaxButton.setEnabled(false); - EditText amountField = entryView.findViewById(R.id.amount_edittext); - amountField.setText(amount); + private fun setAmount(entryView: ConstraintLayout, amount: String?) { + sendMaxButton?.isEnabled = false + val amountField = entryView.findViewById(R.id.amount_edittext) + amountField.setText(amount) } - private void createTx(List> dests, boolean sendAll, PendingTransaction.Priority feePriority) { - ((MoneroApplication) getActivity().getApplication()).getExecutor().execute(() -> { + private fun createTx( + dests: List>, + sendAll: Boolean, + feePriority: PendingTransaction.Priority + ) { + (activity?.application as MoneroApplication).executor?.execute { try { - PendingTransaction pendingTx = TxService.instance.createTx(dests, sendAll, feePriority, new ArrayList<>()); - if (pendingTx != null && pendingTx.getStatus() == PendingTransaction.Status.Status_Ok) { - mViewModel.setPendingTransaction(pendingTx); + val pendingTx = + TxService.instance?.createTx(dests, sendAll, feePriority, ArrayList()) + if (pendingTx != null && pendingTx.status === PendingTransaction.Status.Status_Ok) { + mViewModel?.setPendingTransaction(pendingTx) } else { - Activity activity = getActivity(); + val activity: Activity? = activity if (activity != null && pendingTx != null) { - activity.runOnUiThread(() -> { - createButton.setEnabled(true); - sendMaxButton.setEnabled(true); - if (pendingTx.getErrorString() != null) - Toast.makeText(activity, getString(R.string.error_creating_tx, pendingTx.getErrorString()), Toast.LENGTH_SHORT).show(); - }); + activity.runOnUiThread(Runnable { + createButton?.isEnabled = true + sendMaxButton?.isEnabled = true + if (pendingTx.getErrorString() != null) Toast.makeText( + activity, + getString(R.string.error_creating_tx, pendingTx.getErrorString()), + Toast.LENGTH_SHORT + ).show() + }) } } - } catch (Exception e) { - e.printStackTrace(); - Activity activity = getActivity(); - if (activity != null) { - activity.runOnUiThread(() -> { - createButton.setEnabled(true); - sendMaxButton.setEnabled(true); - Toast.makeText(activity, getString(R.string.error_creating_tx, e.getMessage()), Toast.LENGTH_SHORT).show(); - }); + } catch (e: Exception) { + e.printStackTrace() + val activity: Activity? = activity + activity?.runOnUiThread { + createButton?.isEnabled = true + sendMaxButton?.isEnabled = true + Toast.makeText( + activity, + getString(R.string.error_creating_tx, e.message), + Toast.LENGTH_SHORT + ).show() } } - }); + } } - private void sendTx(PendingTransaction pendingTx) { - ((MoneroApplication) getActivity().getApplication()).getExecutor().execute(() -> { - boolean success = TxService.instance.sendTx(pendingTx); - Activity activity = getActivity(); - if (activity != null) { - activity.runOnUiThread(() -> { - if (success) { - Toast.makeText(getActivity(), getString(R.string.sent_tx), Toast.LENGTH_SHORT).show(); - getActivity().onBackPressed(); - } else { - sendTxSlider.resetSlider(); - Toast.makeText(getActivity(), getString(R.string.error_sending_tx), Toast.LENGTH_SHORT).show(); - } - }); + private fun sendTx(pendingTx: PendingTransaction) { + (activity?.application as MoneroApplication).executor?.execute { + val success = TxService.instance?.sendTx(pendingTx) + val activity: Activity? = activity + activity?.runOnUiThread { + if (success == true) { + Toast.makeText(getActivity(), getString(R.string.sent_tx), Toast.LENGTH_SHORT) + .show() + activity.onBackPressed() + } else { + sendTxSlider?.resetSlider() + Toast.makeText( + getActivity(), + getString(R.string.error_sending_tx), + Toast.LENGTH_SHORT + ) + .show() + } } - }); + } } - private void setAddOutputButtonVisibility(int visibility) { - addOutputImageView.setVisibility(visibility); + private fun setAddOutputButtonVisibility(visibility: Int) { + addOutputImageView?.visibility = visibility } - - - - } \ No newline at end of file diff --git a/app/src/main/java/net/mynero/wallet/fragment/send/SendViewModel.kt b/app/src/main/java/net/mynero/wallet/fragment/send/SendViewModel.kt index 2b33a31..16777bb 100644 --- a/app/src/main/java/net/mynero/wallet/fragment/send/SendViewModel.kt +++ b/app/src/main/java/net/mynero/wallet/fragment/send/SendViewModel.kt @@ -1,29 +1,27 @@ -package net.mynero.wallet.fragment.send; +package net.mynero.wallet.fragment.send -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import net.mynero.wallet.model.PendingTransaction -import net.mynero.wallet.model.PendingTransaction; - -public class SendViewModel extends ViewModel { - private final MutableLiveData _sendingMax = new MutableLiveData<>(false); - private final MutableLiveData _showAddOutputButton = new MutableLiveData<>(true); - private final MutableLiveData _pendingTransaction = new MutableLiveData<>(null); - public LiveData sendingMax = _sendingMax; - public LiveData showAddOutputButton = _showAddOutputButton; - public LiveData pendingTransaction = _pendingTransaction; - - public void setSendingMax(boolean value) { - _sendingMax.setValue(value); - setShowAddOutputButton(!value); +class SendViewModel : ViewModel() { + private val _sendingMax = MutableLiveData(false) + private val _showAddOutputButton = MutableLiveData(true) + private val _pendingTransaction = MutableLiveData(null) + var sendingMax: LiveData = _sendingMax + var showAddOutputButton: LiveData = _showAddOutputButton + var pendingTransaction: LiveData = _pendingTransaction + fun setSendingMax(value: Boolean) { + _sendingMax.value = value + setShowAddOutputButton(!value) } - public void setShowAddOutputButton(boolean value) { - _showAddOutputButton.setValue(value); + fun setShowAddOutputButton(value: Boolean) { + _showAddOutputButton.value = value } - public void setPendingTransaction(PendingTransaction pendingTx) { - _pendingTransaction.postValue(pendingTx); + fun setPendingTransaction(pendingTx: PendingTransaction?) { + _pendingTransaction.postValue(pendingTx) } } \ No newline at end of file diff --git a/app/src/main/java/net/mynero/wallet/model/TransactionInfo.kt b/app/src/main/java/net/mynero/wallet/model/TransactionInfo.kt index 06e28e0..0766f5d 100644 --- a/app/src/main/java/net/mynero/wallet/model/TransactionInfo.kt +++ b/app/src/main/java/net/mynero/wallet/model/TransactionInfo.kt @@ -13,177 +13,188 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package net.mynero.wallet.model -package net.mynero.wallet.model; +import android.os.Build +import android.os.Parcel +import android.os.Parcelable +import android.os.Parcelable.Creator +import net.mynero.wallet.data.Subaddress -import android.os.Parcel; -import android.os.Parcelable; +class TransactionInfo : Parcelable, Comparable { + @JvmField + var direction: Direction + var isPending: Boolean + var isFailed: Boolean -import androidx.annotation.NonNull; + @JvmField + var amount: Long + var fee: Long -import net.mynero.wallet.data.Subaddress; + @JvmField + var blockheight: Long -import java.util.List; + @JvmField + var hash: String? -// this is not the TransactionInfo from the API as that is owned by the TransactionHistory -// this is a POJO for the TransactionInfoAdapter -public class TransactionInfo implements Parcelable, Comparable { - public static final int CONFIRMATION = 10; // blocks - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public TransactionInfo createFromParcel(Parcel in) { - return new TransactionInfo(in); + @JvmField + var timestamp: Long + var paymentId: String? + + @JvmField + var accountIndex: Int + + @JvmField + var addressIndex: Int + + @JvmField + var confirmations: Long + var subaddressLabel: String? + + @JvmField + var transfers: List? = 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? + ) { + this.direction = Direction.values()[direction] + this.isPending = isPending + this.isFailed = isFailed + this.amount = amount + this.fee = fee + this.blockheight = blockheight + this.hash = hash + this.timestamp = timestamp + this.paymentId = paymentId + this.accountIndex = accountIndex + this.addressIndex = addressIndex + this.confirmations = confirmations + this.subaddressLabel = subaddressLabel + this.transfers = transfers + } + + private constructor(`in`: Parcel) { + direction = Direction.fromInteger(`in`.readInt()) + isPending = `in`.readByte().toInt() != 0 + isFailed = `in`.readByte().toInt() != 0 + amount = `in`.readLong() + fee = `in`.readLong() + blockheight = `in`.readLong() + hash = `in`.readString() + timestamp = `in`.readLong() + paymentId = `in`.readString() + accountIndex = `in`.readInt() + addressIndex = `in`.readInt() + confirmations = `in`.readLong() + subaddressLabel = `in`.readString() + transfers?.toMutableList()?.let { transfers -> + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + `in`.readList(transfers, Transfer::class.java.classLoader, Transfer::class.java) + } else { + `in`.readList(transfers, Transfer::class.java.classLoader) + } } - public TransactionInfo[] newArray(int size) { - return new TransactionInfo[size]; + txKey = `in`.readString() + notes = `in`.readString() + address = `in`.readString() + } + + val isConfirmed: Boolean + get() = confirmations >= CONFIRMATION + val displayLabel: String? + get() = if (subaddressLabel?.isEmpty() == true || Subaddress.DEFAULT_LABEL_FORMATTER.matcher( + subaddressLabel.toString() + ).matches() + ) "#$addressIndex" else subaddressLabel + + override fun toString(): String { + return "$direction@$blockheight $amount" + } + + override fun writeToParcel(out: Parcel, flags: Int) { + out.writeInt(direction.value) + out.writeByte((if (isPending) 1 else 0).toByte()) + out.writeByte((if (isFailed) 1 else 0).toByte()) + out.writeLong(amount) + out.writeLong(fee) + out.writeLong(blockheight) + out.writeString(hash) + out.writeLong(timestamp) + out.writeString(paymentId) + out.writeInt(accountIndex) + out.writeInt(addressIndex) + out.writeLong(confirmations) + out.writeString(subaddressLabel) + transfers?.let { + out.writeList(transfers) } - }; - public Direction direction; - public boolean isPending; - public boolean isFailed; - public long amount; - public long fee; - public long blockheight; - public String hash; - public long timestamp; - public String paymentId; - public int accountIndex; - public int addressIndex; - public long confirmations; - public String subaddressLabel; - public List 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 transfers) { - this.direction = Direction.values()[direction]; - this.isPending = isPending; - this.isFailed = isFailed; - this.amount = amount; - this.fee = fee; - this.blockheight = blockheight; - this.hash = hash; - this.timestamp = timestamp; - this.paymentId = paymentId; - this.accountIndex = accountIndex; - this.addressIndex = addressIndex; - this.confirmations = confirmations; - this.subaddressLabel = subaddressLabel; - this.transfers = transfers; + out.writeString(txKey) + out.writeString(notes) + out.writeString(address) } - private TransactionInfo(Parcel in) { - direction = Direction.fromInteger(in.readInt()); - isPending = in.readByte() != 0; - isFailed = in.readByte() != 0; - amount = in.readLong(); - fee = in.readLong(); - blockheight = in.readLong(); - hash = in.readString(); - timestamp = in.readLong(); - paymentId = in.readString(); - accountIndex = in.readInt(); - addressIndex = in.readInt(); - confirmations = in.readLong(); - subaddressLabel = in.readString(); - in.readList(transfers, Transfer.class.getClassLoader()); - txKey = in.readString(); - notes = in.readString(); - address = in.readString(); + override fun describeContents(): Int { + return 0 } - public boolean isConfirmed() { - return confirmations >= CONFIRMATION; - } - - public String getDisplayLabel() { - if (subaddressLabel.isEmpty() || (Subaddress.DEFAULT_LABEL_FORMATTER.matcher(subaddressLabel).matches())) - return ("#" + addressIndex); - else - return subaddressLabel; - } - - @Override - @NonNull - public String toString() { - return direction + "@" + blockheight + " " + amount; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(direction.getValue()); - out.writeByte((byte) (isPending ? 1 : 0)); - out.writeByte((byte) (isFailed ? 1 : 0)); - out.writeLong(amount); - out.writeLong(fee); - out.writeLong(blockheight); - out.writeString(hash); - out.writeLong(timestamp); - out.writeString(paymentId); - out.writeInt(accountIndex); - out.writeInt(addressIndex); - out.writeLong(confirmations); - out.writeString(subaddressLabel); - out.writeList(transfers); - out.writeString(txKey); - out.writeString(notes); - out.writeString(address); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public int compareTo(TransactionInfo another) { - long b1 = this.timestamp; - long b2 = another.timestamp; - if (b1 > b2) { - return -1; + override fun compareTo(other: TransactionInfo): Int { + val b1 = timestamp + val b2 = other.timestamp + return if (b1 > b2) { + -1 } else if (b1 < b2) { - return 1; + 1 } else { - return this.hash.compareTo(another.hash); + hash?.let { other.hash?.compareTo(it) } ?: 0 } } - public enum Direction { - Direction_In(0), - Direction_Out(1); + enum class Direction(val value: Int) { + Direction_In(0), Direction_Out(1); - private final int value; - - Direction(int value) { - this.value = value; + companion object { + fun fromInteger(n: Int): Direction { + return when (n) { + 0 -> Direction_In + else -> Direction_Out + } + } } + } - public static Direction fromInteger(int n) { - return switch (n) { - case 0 -> Direction_In; - case 1 -> Direction_Out; - default -> null; - }; - } + companion object { + const val CONFIRMATION = 10 // blocks - public int getValue() { - return value; + @JvmField + val CREATOR: Creator = object : Creator { + override fun createFromParcel(`in`: Parcel): TransactionInfo { + return TransactionInfo(`in`) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } } } } \ No newline at end of file diff --git a/app/src/main/java/net/mynero/wallet/model/Wallet.kt b/app/src/main/java/net/mynero/wallet/model/Wallet.kt index 3fe7f5c..958ec7c 100644 --- a/app/src/main/java/net/mynero/wallet/model/Wallet.kt +++ b/app/src/main/java/net/mynero/wallet/model/Wallet.kt @@ -430,8 +430,6 @@ class Wallet { class Status internal constructor(status: Int, @JvmField val errorString: String) { val status: StatusEnum - - @JvmField var connectionStatus: ConnectionStatus? = null // optional init { @@ -470,7 +468,6 @@ class Wallet { @JvmStatic external fun isPaymentIdValid(payment_id: String): Boolean - @JvmStatic fun isAddressValid(address: String): Boolean { return WalletManager.instance?.networkType?.value?.let { isAddressValid( diff --git a/app/src/main/java/net/mynero/wallet/service/BalanceService.kt b/app/src/main/java/net/mynero/wallet/service/BalanceService.kt index 9927a9b..034aca1 100644 --- a/app/src/main/java/net/mynero/wallet/service/BalanceService.kt +++ b/app/src/main/java/net/mynero/wallet/service/BalanceService.kt @@ -6,8 +6,6 @@ import net.mynero.wallet.model.BalanceInfo class BalanceService(thread: MoneroHandlerThread) : ServiceBase(thread) { private val _balanceInfo = MutableLiveData(null) - - @JvmField var balanceInfo: LiveData = _balanceInfo init { diff --git a/app/src/main/java/net/mynero/wallet/service/BlockchainService.kt b/app/src/main/java/net/mynero/wallet/service/BlockchainService.kt index c791f7c..8d8541d 100644 --- a/app/src/main/java/net/mynero/wallet/service/BlockchainService.kt +++ b/app/src/main/java/net/mynero/wallet/service/BlockchainService.kt @@ -9,6 +9,7 @@ class BlockchainService(thread: MoneroHandlerThread) : ServiceBase(thread) { private val _currentHeight = MutableLiveData(0L) private val _connectionStatus = MutableLiveData(ConnectionStatus.ConnectionStatus_Disconnected) var height: LiveData = _currentHeight + @JvmField var connectionStatus: LiveData = _connectionStatus var daemonHeight: Long = 0 diff --git a/app/src/main/java/net/mynero/wallet/service/TxService.kt b/app/src/main/java/net/mynero/wallet/service/TxService.kt index 2d8c9d9..23dc986 100644 --- a/app/src/main/java/net/mynero/wallet/service/TxService.kt +++ b/app/src/main/java/net/mynero/wallet/service/TxService.kt @@ -33,7 +33,6 @@ class TxService(thread: MoneroHandlerThread) : ServiceBase(thread) { } companion object { - @JvmField var instance: TxService? = null } } \ No newline at end of file diff --git a/app/src/main/java/net/mynero/wallet/service/UTXOService.kt b/app/src/main/java/net/mynero/wallet/service/UTXOService.kt index 98242a8..2e038dc 100644 --- a/app/src/main/java/net/mynero/wallet/service/UTXOService.kt +++ b/app/src/main/java/net/mynero/wallet/service/UTXOService.kt @@ -96,9 +96,9 @@ class UTXOService(thread: MoneroHandlerThread?) : ServiceBase(thread) { val seenTxs = ArrayList() val utxos: List = ArrayList(getUtxos()) var amountSelected: Long = 0 - utxos.sorted() + val sortedUtxos = utxos.sorted() //loop through each utxo - for (coinsInfo in utxos) { + for (coinsInfo in sortedUtxos) { if (!coinsInfo.isSpent && coinsInfo.isUnlocked && !coinsInfo.isFrozen && !frozenCoins.contains( coinsInfo.pubKey ) diff --git a/app/src/main/java/net/mynero/wallet/util/Helper.kt b/app/src/main/java/net/mynero/wallet/util/Helper.kt index 3f3d120..730f409 100644 --- a/app/src/main/java/net/mynero/wallet/util/Helper.kt +++ b/app/src/main/java/net/mynero/wallet/util/Helper.kt @@ -38,8 +38,6 @@ import androidx.activity.result.ActivityResultLauncher import androidx.core.content.ContextCompat import net.mynero.wallet.R import net.mynero.wallet.model.WalletManager -import net.mynero.wallet.model.WalletManager.Companion.initLogger -import net.mynero.wallet.model.WalletManager.Companion.setLogLevel import timber.log.Timber import java.io.File import java.io.IOException @@ -62,8 +60,6 @@ object Helper { private const val MONERO_DIR = "monero" private val HexArray = "0123456789ABCDEF".toCharArray() var ALLOW_SHIFT = false - - @JvmField var DISPLAY_DIGITS_INFO = 5 private var ShakeAnimation: Animation? = null fun getWalletRoot(context: Context): File { @@ -104,7 +100,6 @@ object Helper { } } - @JvmStatic fun getCameraPermission(context: Activity, launcher: ActivityResultLauncher): Boolean { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (context.checkSelfPermission(Manifest.permission.CAMERA) @@ -161,7 +156,6 @@ object Helper { return BigDecimal(amount).scaleByPowerOfTen(-XMR_DECIMALS) } - @JvmStatic fun getDisplayAmount(amount: Long): String { return getDisplayAmount(amount, XMR_DECIMALS) } @@ -203,13 +197,10 @@ object Helper { } fun getBitmap(context: Context, drawableId: Int): Bitmap { - val drawable = ContextCompat.getDrawable(context, drawableId) - return if (drawable is BitmapDrawable) { - BitmapFactory.decodeResource(context.resources, drawableId) - } else if (drawable is VectorDrawable) { - getBitmap(drawable) - } else { - throw IllegalArgumentException("unsupported drawable type") + return when (val drawable = ContextCompat.getDrawable(context, drawableId)) { + is BitmapDrawable -> BitmapFactory.decodeResource(context.resources, drawableId) + is VectorDrawable -> getBitmap(drawable) + else -> throw IllegalArgumentException("unsupported drawable type") } } @@ -268,7 +259,6 @@ object Helper { } } - @JvmStatic fun getClipBoardText(context: Context): String? { val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager @@ -300,7 +290,7 @@ object Helper { // TODO make the log levels refer to the WalletManagerFactory::LogLevel enum ? fun initLogger(context: Context, level: Int) { val home = getStorage(context, MONERO_DIR).absolutePath - initLogger("$home/monerujo", "monerujo.log") - if (level >= WalletManager.LOGLEVEL_SILENT) setLogLevel(level) + WalletManager.initLogger("$home/monerujo", "monerujo.log") + if (level >= WalletManager.LOGLEVEL_SILENT) WalletManager.setLogLevel(level) } } \ No newline at end of file diff --git a/app/src/main/java/net/mynero/wallet/util/ThemeHelper.kt b/app/src/main/java/net/mynero/wallet/util/ThemeHelper.kt index 4d0a580..27d3977 100644 --- a/app/src/main/java/net/mynero/wallet/util/ThemeHelper.kt +++ b/app/src/main/java/net/mynero/wallet/util/ThemeHelper.kt @@ -34,7 +34,6 @@ object ThemeHelper { ) typedValue.resourceId else 0 } - @JvmStatic @ColorInt fun getThemedColor(ctx: Context, attrId: Int): Int { val typedValue = TypedValue() diff --git a/app/src/test/java/net/mynero/wallet/util/HelperTest.java b/app/src/test/java/net/mynero/wallet/util/HelperTest.java deleted file mode 100644 index bc1b333..0000000 --- a/app/src/test/java/net/mynero/wallet/util/HelperTest.java +++ /dev/null @@ -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))); - } -} diff --git a/app/src/test/java/net/mynero/wallet/util/RestoreHeightTest.java b/app/src/test/java/net/mynero/wallet/util/RestoreHeightTest.java deleted file mode 100644 index e152978..0000000 --- a/app/src/test/java/net/mynero/wallet/util/RestoreHeightTest.java +++ /dev/null @@ -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); - } -} diff --git a/app/src/test/java/net/mynero/wallet/util/UserNoteTest.java b/app/src/test/java/net/mynero/wallet/util/UserNoteTest.java deleted file mode 100644 index 207ba1d..0000000 --- a/app/src/test/java/net/mynero/wallet/util/UserNoteTest.java +++ /dev/null @@ -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()); - } -}