mirror of
https://codeberg.org/r4v3r23/mysu.git
synced 2024-11-25 15:22:28 +00:00
Cleanup some Kotlin code, convert HomeFragment to Kotlin, and dialogs
This commit is contained in:
parent
97d39a3cb4
commit
d38a2e1306
36 changed files with 1195 additions and 1367 deletions
|
@ -245,7 +245,6 @@ class Node {
|
|||
private var DEFAULT_LEVIN_PORT = 0
|
||||
private var DEFAULT_RPC_PORT = 0
|
||||
|
||||
@JvmStatic
|
||||
fun fromString(nodeString: String?): Node? {
|
||||
return try {
|
||||
Node(nodeString)
|
||||
|
|
|
@ -23,7 +23,6 @@ class Subaddress(
|
|||
@JvmField val address: String,
|
||||
val label: String
|
||||
) : Comparable<Subaddress> {
|
||||
@JvmField
|
||||
var amount: Long = 0
|
||||
|
||||
override fun compareTo(other: Subaddress): Int { // newer is <
|
||||
|
|
|
@ -1,106 +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 android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.os.Parcelable.Creator
|
||||
import net.mynero.wallet.model.PendingTransaction
|
||||
import net.mynero.wallet.model.Wallet
|
||||
import net.mynero.wallet.util.Helper
|
||||
|
||||
// https://stackoverflow.com/questions/2139134/how-to-send-an-object-from-one-android-activity-to-another-using-intents
|
||||
class TxData : Parcelable {
|
||||
var destinationAddress: String? = null
|
||||
var amount: Long = 0
|
||||
var mixin = 0
|
||||
var priority: PendingTransaction.Priority? = null
|
||||
var userNotes: UserNotes? = null
|
||||
var preferredInputs: ArrayList<String>? = null
|
||||
private set
|
||||
|
||||
constructor()
|
||||
constructor(txData: TxData) {
|
||||
destinationAddress = txData.destinationAddress
|
||||
amount = txData.amount
|
||||
mixin = txData.mixin
|
||||
priority = txData.priority
|
||||
preferredInputs = txData.preferredInputs
|
||||
}
|
||||
|
||||
constructor(
|
||||
dstAddr: String?,
|
||||
amount: Long,
|
||||
mixin: Int,
|
||||
priority: PendingTransaction.Priority?,
|
||||
preferredInputs: ArrayList<String>?
|
||||
) {
|
||||
destinationAddress = dstAddr
|
||||
this.amount = amount
|
||||
this.mixin = mixin
|
||||
this.priority = priority
|
||||
this.preferredInputs = preferredInputs
|
||||
}
|
||||
|
||||
protected constructor(`in`: Parcel) {
|
||||
destinationAddress = `in`.readString()
|
||||
amount = `in`.readLong()
|
||||
mixin = `in`.readInt()
|
||||
priority = PendingTransaction.Priority.fromInteger(`in`.readInt())
|
||||
`in`.readStringList(preferredInputs!!)
|
||||
}
|
||||
|
||||
fun setAmount(amount: Double) {
|
||||
this.amount = Wallet.getAmountFromDouble(amount)
|
||||
}
|
||||
|
||||
val amountAsDouble: Double
|
||||
get() = 1.0 * amount / Helper.ONE_XMR
|
||||
|
||||
override fun writeToParcel(out: Parcel, flags: Int) {
|
||||
out.writeString(destinationAddress)
|
||||
out.writeLong(amount)
|
||||
out.writeInt(mixin)
|
||||
out.writeInt(priority!!.value)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val sb = StringBuffer()
|
||||
sb.append("dstAddr:")
|
||||
sb.append(destinationAddress)
|
||||
sb.append(",amount:")
|
||||
sb.append(amount)
|
||||
sb.append(",mixin:")
|
||||
sb.append(mixin)
|
||||
sb.append(",priority:")
|
||||
sb.append(priority)
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
companion object CREATOR : Creator<TxData> {
|
||||
override fun createFromParcel(parcel: Parcel): TxData {
|
||||
return TxData(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<TxData?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -71,7 +71,7 @@ class UserNotes(txNotes: String?) {
|
|||
sb.append(",")
|
||||
sb.append(xmrtoDestination)
|
||||
sb.append("}")
|
||||
if (note != null && !note!!.isEmpty()) sb.append(" ")
|
||||
if (note != null && note?.isNotEmpty() == true) sb.append(" ")
|
||||
}
|
||||
sb.append(note)
|
||||
return sb.toString()
|
||||
|
|
|
@ -1,155 +0,0 @@
|
|||
package net.mynero.wallet.fragment.dialog;
|
||||
|
||||
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.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import net.mynero.wallet.R;
|
||||
import net.mynero.wallet.data.Node;
|
||||
import net.mynero.wallet.service.PrefService;
|
||||
import net.mynero.wallet.util.Constants;
|
||||
import net.mynero.wallet.util.Helper;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class AddNodeBottomSheetDialog extends BottomSheetDialogFragment {
|
||||
public AddNodeListener listener = null;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.add_node_bottom_sheet_dialog, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
Button addNodeButton = view.findViewById(R.id.add_node_button);
|
||||
EditText addressEditText = view.findViewById(R.id.address_edittext);
|
||||
EditText portEditText = view.findViewById(R.id.node_port_edittext);
|
||||
EditText nodeNameEditText = view.findViewById(R.id.node_name_edittext);
|
||||
EditText usernameEditText = view.findViewById(R.id.username_edittext);
|
||||
EditText passwordEditText = view.findViewById(R.id.password_edittext);
|
||||
ImageButton pastePasswordImageButton = view.findViewById(R.id.paste_password_imagebutton);
|
||||
|
||||
usernameEditText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
if (editable.toString().isEmpty()) {
|
||||
passwordEditText.setText(null);
|
||||
passwordEditText.setVisibility(View.GONE);
|
||||
pastePasswordImageButton.setVisibility(View.GONE);
|
||||
} else {
|
||||
passwordEditText.setVisibility(View.VISIBLE);
|
||||
pastePasswordImageButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
addPasteListener(view, addressEditText, R.id.paste_address_imagebutton);
|
||||
addPasteListener(view, usernameEditText, R.id.paste_username_imagebutton);
|
||||
addPasteListener(view, passwordEditText, R.id.paste_password_imagebutton);
|
||||
|
||||
addNodeButton.setOnClickListener(view1 -> {
|
||||
String nodeAddr = addressEditText.getText().toString();
|
||||
String portString = portEditText.getText().toString();
|
||||
String name = nodeNameEditText.getText().toString();
|
||||
String user = usernameEditText.getText().toString();
|
||||
String pass = passwordEditText.getText().toString();
|
||||
if (name.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Enter node name", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
} else if (nodeAddr.isEmpty() || portString.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Enter node address", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
} else if (!user.isEmpty() && pass.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Enter password", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
try {
|
||||
if (!user.isEmpty()) {
|
||||
jsonObject.put("username", user);
|
||||
jsonObject.put("password", pass);
|
||||
}
|
||||
|
||||
if (nodeAddr.contains(":") && !nodeAddr.startsWith("[") && !nodeAddr.endsWith("]"))
|
||||
nodeAddr = "[" + nodeAddr + "]";
|
||||
|
||||
jsonObject.put("host", nodeAddr);
|
||||
jsonObject.put("rpcPort", Integer.parseInt(portString));
|
||||
jsonObject.put("network", "mainnet");
|
||||
jsonObject.put("name", name);
|
||||
|
||||
addNodeToSaved(jsonObject);
|
||||
|
||||
if (listener != null) {
|
||||
listener.onNodeAdded();
|
||||
dismiss();
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addNodeToSaved(JSONObject newNodeJsonObject) throws JSONException {
|
||||
Node newNode = Node.fromJson(newNodeJsonObject);
|
||||
|
||||
String nodesArray = PrefService.getInstance().getString(Constants.PREF_CUSTOM_NODES, "[]");
|
||||
JSONArray jsonArray = new JSONArray(nodesArray);
|
||||
boolean exists = false;
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
JSONObject nodeJsonObject = jsonArray.getJSONObject(i);
|
||||
Node node = Node.fromJson(nodeJsonObject);
|
||||
if (node.toNodeString().equals(newNode.toNodeString()))
|
||||
exists = true;
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
jsonArray.put(newNodeJsonObject);
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Node already exists", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
PrefService.getInstance().edit().putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString()).apply();
|
||||
}
|
||||
|
||||
private void addPasteListener(View root, EditText editText, int layoutId) {
|
||||
ImageButton pasteImageButton = root.findViewById(layoutId);
|
||||
pasteImageButton.setOnClickListener(view1 -> {
|
||||
Context ctx = getContext();
|
||||
if (ctx != null) {
|
||||
editText.setText(Helper.getClipBoardText(ctx));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface AddNodeListener {
|
||||
void onNodeAdded();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package net.mynero.wallet.fragment.dialog
|
||||
|
||||
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.Toast
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import net.mynero.wallet.R
|
||||
import net.mynero.wallet.data.Node.Companion.fromJson
|
||||
import net.mynero.wallet.service.PrefService
|
||||
import net.mynero.wallet.util.Constants
|
||||
import net.mynero.wallet.util.Helper.getClipBoardText
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
class AddNodeBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
@JvmField
|
||||
var listener: AddNodeListener? = null
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.add_node_bottom_sheet_dialog, null)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val addNodeButton = view.findViewById<Button>(R.id.add_node_button)
|
||||
val addressEditText = view.findViewById<EditText>(R.id.address_edittext)
|
||||
val portEditText = view.findViewById<EditText>(R.id.node_port_edittext)
|
||||
val nodeNameEditText = view.findViewById<EditText>(R.id.node_name_edittext)
|
||||
val usernameEditText = view.findViewById<EditText>(R.id.username_edittext)
|
||||
val passwordEditText = view.findViewById<EditText>(R.id.password_edittext)
|
||||
val pastePasswordImageButton =
|
||||
view.findViewById<ImageButton>(R.id.paste_password_imagebutton)
|
||||
usernameEditText.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||
override fun afterTextChanged(editable: Editable) {
|
||||
if (editable.toString().isEmpty()) {
|
||||
passwordEditText.text = null
|
||||
passwordEditText.visibility = View.GONE
|
||||
pastePasswordImageButton.visibility = View.GONE
|
||||
} else {
|
||||
passwordEditText.visibility = View.VISIBLE
|
||||
pastePasswordImageButton.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
})
|
||||
addPasteListener(view, addressEditText, R.id.paste_address_imagebutton)
|
||||
addPasteListener(view, usernameEditText, R.id.paste_username_imagebutton)
|
||||
addPasteListener(view, passwordEditText, R.id.paste_password_imagebutton)
|
||||
addNodeButton.setOnClickListener {
|
||||
var nodeAddr = addressEditText.text.toString()
|
||||
val portString = portEditText.text.toString()
|
||||
val name = nodeNameEditText.text.toString()
|
||||
val user = usernameEditText.text.toString()
|
||||
val pass = passwordEditText.text.toString()
|
||||
if (name.isEmpty()) {
|
||||
Toast.makeText(context, "Enter node name", Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
} else if (nodeAddr.isEmpty() || portString.isEmpty()) {
|
||||
Toast.makeText(context, "Enter node address", Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
} else if (user.isNotEmpty() && pass.isEmpty()) {
|
||||
Toast.makeText(context, "Enter password", Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
val jsonObject = JSONObject()
|
||||
try {
|
||||
if (user.isNotEmpty()) {
|
||||
jsonObject.put("username", user)
|
||||
jsonObject.put("password", pass)
|
||||
}
|
||||
if (nodeAddr.contains(":") && !nodeAddr.startsWith("[") && !nodeAddr.endsWith("]")) nodeAddr =
|
||||
"[$nodeAddr]"
|
||||
jsonObject.put("host", nodeAddr)
|
||||
jsonObject.put("rpcPort", portString.toInt())
|
||||
jsonObject.put("network", "mainnet")
|
||||
jsonObject.put("name", name)
|
||||
addNodeToSaved(jsonObject)
|
||||
if (listener != null) {
|
||||
listener?.onNodeAdded()
|
||||
dismiss()
|
||||
}
|
||||
} catch (e: JSONException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(JSONException::class)
|
||||
private fun addNodeToSaved(newNodeJsonObject: JSONObject) {
|
||||
val newNode = fromJson(newNodeJsonObject)
|
||||
val nodesArray = PrefService.instance?.getString(Constants.PREF_CUSTOM_NODES, "[]")
|
||||
val jsonArray = JSONArray(nodesArray)
|
||||
var exists = false
|
||||
for (i in 0 until jsonArray.length()) {
|
||||
val nodeJsonObject = jsonArray.getJSONObject(i)
|
||||
val node = fromJson(nodeJsonObject)
|
||||
if (node?.toNodeString() == newNode?.toNodeString()) exists = true
|
||||
}
|
||||
if (!exists) {
|
||||
jsonArray.put(newNodeJsonObject)
|
||||
} else {
|
||||
Toast.makeText(context, "Node already exists", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
PrefService.instance?.edit()?.putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString())?.apply()
|
||||
}
|
||||
|
||||
private fun addPasteListener(root: View, editText: EditText, layoutId: Int) {
|
||||
val pasteImageButton = root.findViewById<ImageButton>(layoutId)
|
||||
pasteImageButton.setOnClickListener {
|
||||
val ctx = context
|
||||
if (ctx != null) {
|
||||
editText.setText(getClipBoardText(ctx))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface AddNodeListener {
|
||||
fun onNodeAdded()
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
package net.mynero.wallet.fragment.dialog;
|
||||
|
||||
import android.os.Bundle;
|
||||
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 androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import net.mynero.wallet.R;
|
||||
import net.mynero.wallet.model.Wallet;
|
||||
import net.mynero.wallet.model.WalletManager;
|
||||
import net.mynero.wallet.service.AddressService;
|
||||
import net.mynero.wallet.util.Helper;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
public class EditAddressLabelBottomSheetDialog extends BottomSheetDialogFragment {
|
||||
public LabelListener listener = null;
|
||||
public int addressIndex = 0;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.bottom_sheet_dialog_edit_address_label, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
Wallet wallet = WalletManager.getInstance().getWallet();
|
||||
AddressService addressService = AddressService.instance;
|
||||
ImageButton pasteButton = view.findViewById(R.id.paste_password_imagebutton);
|
||||
EditText labelEditText = view.findViewById(R.id.wallet_password_edittext);
|
||||
Button saveLabelButton = view.findViewById(R.id.unlock_wallet_button);
|
||||
|
||||
boolean isDate = false;
|
||||
try {
|
||||
new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss", Locale.US).parse(wallet.getSubaddressLabel(addressIndex));
|
||||
isDate = true;
|
||||
} catch (ParseException ignored) {
|
||||
}
|
||||
|
||||
labelEditText.setText(isDate ? null : wallet.getSubaddressLabel(addressIndex));
|
||||
pasteButton.setOnClickListener(view1 -> labelEditText.setText(Helper.getClipBoardText(view.getContext())));
|
||||
saveLabelButton.setOnClickListener(view1 -> {
|
||||
String label = labelEditText.getText().toString();
|
||||
if (addressService.getLatestAddressIndex() == addressIndex) {
|
||||
addressService.freshSubaddress();
|
||||
}
|
||||
wallet.setSubaddressLabel(addressIndex, label);
|
||||
wallet.store();
|
||||
if (listener != null) {
|
||||
listener.onDismiss();
|
||||
}
|
||||
dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
public interface LabelListener {
|
||||
void onDismiss();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package net.mynero.wallet.fragment.dialog
|
||||
|
||||
import android.os.Bundle
|
||||
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 com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import net.mynero.wallet.R
|
||||
import net.mynero.wallet.model.WalletManager
|
||||
import net.mynero.wallet.service.AddressService
|
||||
import net.mynero.wallet.util.Helper.getClipBoardText
|
||||
import java.text.ParseException
|
||||
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,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.bottom_sheet_dialog_edit_address_label, null)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val wallet = WalletManager.instance?.wallet
|
||||
val addressService = AddressService.instance
|
||||
val pasteButton = view.findViewById<ImageButton>(R.id.paste_password_imagebutton)
|
||||
val labelEditText = view.findViewById<EditText>(R.id.wallet_password_edittext)
|
||||
val saveLabelButton = view.findViewById<Button>(R.id.unlock_wallet_button)
|
||||
var isDate = false
|
||||
try {
|
||||
wallet?.getSubaddressLabel(
|
||||
addressIndex
|
||||
)?.let {
|
||||
SimpleDateFormat("yyyy-MM-dd-HH:mm:ss", Locale.US).parse(
|
||||
it
|
||||
)
|
||||
isDate = true
|
||||
}
|
||||
} catch (ignored: ParseException) {
|
||||
}
|
||||
labelEditText.setText(if (isDate) null else wallet?.getSubaddressLabel(addressIndex))
|
||||
pasteButton.setOnClickListener { labelEditText.setText(getClipBoardText(view.context)) }
|
||||
saveLabelButton.setOnClickListener {
|
||||
val label = labelEditText.text.toString()
|
||||
if (addressService?.latestAddressIndex == addressIndex) {
|
||||
addressService.freshSubaddress()
|
||||
}
|
||||
wallet?.setSubaddressLabel(addressIndex, label)
|
||||
wallet?.store()
|
||||
if (listener != null) {
|
||||
listener?.onDismiss()
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
interface LabelListener {
|
||||
fun onDismiss()
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
package net.mynero.wallet.fragment.dialog;
|
||||
|
||||
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.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import net.mynero.wallet.R;
|
||||
import net.mynero.wallet.data.Node;
|
||||
import net.mynero.wallet.util.Helper;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class EditNodeBottomSheetDialog extends BottomSheetDialogFragment {
|
||||
public EditNodeListener listener = null;
|
||||
public Node node = null;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.edit_node_bottom_sheet_dialog, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
Button deleteNodeButton = view.findViewById(R.id.delete_node_button);
|
||||
Button doneEditingButton = view.findViewById(R.id.done_editing_button);
|
||||
EditText addressEditText = view.findViewById(R.id.address_edittext);
|
||||
EditText portEditText = view.findViewById(R.id.node_port_edittext);
|
||||
EditText nodeNameEditText = view.findViewById(R.id.node_name_edittext);
|
||||
EditText usernameEditText = view.findViewById(R.id.username_edittext);
|
||||
EditText passwordEditText = view.findViewById(R.id.password_edittext);
|
||||
ImageButton pastePasswordImageButton = view.findViewById(R.id.paste_password_imagebutton);
|
||||
|
||||
if (node == null) return;
|
||||
addressEditText.setText(node.getHost());
|
||||
portEditText.setText("" + node.getRpcPort());
|
||||
nodeNameEditText.setText(node.getName());
|
||||
usernameEditText.setText(node.getUsername());
|
||||
if (!node.getPassword().isEmpty()) {
|
||||
passwordEditText.setText(node.getPassword());
|
||||
passwordEditText.setVisibility(View.VISIBLE);
|
||||
pastePasswordImageButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
usernameEditText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
if (editable.toString().isEmpty()) {
|
||||
passwordEditText.setText(null);
|
||||
passwordEditText.setVisibility(View.GONE);
|
||||
pastePasswordImageButton.setVisibility(View.GONE);
|
||||
} else {
|
||||
passwordEditText.setVisibility(View.VISIBLE);
|
||||
pastePasswordImageButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
addPasteListener(view, addressEditText, R.id.paste_address_imagebutton);
|
||||
addPasteListener(view, usernameEditText, R.id.paste_username_imagebutton);
|
||||
addPasteListener(view, passwordEditText, R.id.paste_password_imagebutton);
|
||||
|
||||
deleteNodeButton.setOnClickListener(view1 -> {
|
||||
listener.onNodeDeleted(node);
|
||||
dismiss();
|
||||
});
|
||||
doneEditingButton.setOnClickListener(view1 -> {
|
||||
String nodeAddr = addressEditText.getText().toString();
|
||||
String portString = portEditText.getText().toString();
|
||||
String nodeName = nodeNameEditText.getText().toString();
|
||||
String user = usernameEditText.getText().toString();
|
||||
String pass = passwordEditText.getText().toString();
|
||||
|
||||
if (nodeName.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Enter node name", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
} else if (nodeAddr.isEmpty() || portString.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Enter node address", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
} else if (!user.isEmpty() && pass.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Enter password", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
try {
|
||||
if (!user.isEmpty()) {
|
||||
jsonObject.put("username", user);
|
||||
jsonObject.put("password", pass);
|
||||
}
|
||||
|
||||
if (nodeAddr.contains(":") && !nodeAddr.startsWith("[") && !nodeAddr.endsWith("]"))
|
||||
nodeAddr = "[" + nodeAddr + "]";
|
||||
|
||||
jsonObject.put("host", nodeAddr);
|
||||
jsonObject.put("rpcPort", Integer.parseInt(portString));
|
||||
jsonObject.put("network", "mainnet");
|
||||
jsonObject.put("name", nodeName);
|
||||
|
||||
listener.onNodeEdited(node, Node.fromJson(jsonObject));
|
||||
dismiss();
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addPasteListener(View root, EditText editText, int layoutId) {
|
||||
ImageButton pasteImageButton = root.findViewById(layoutId);
|
||||
pasteImageButton.setOnClickListener(view1 -> {
|
||||
Context ctx = getContext();
|
||||
if (ctx != null) {
|
||||
editText.setText(Helper.getClipBoardText(ctx));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface EditNodeListener {
|
||||
void onNodeDeleted(Node node);
|
||||
|
||||
void onNodeEdited(Node oldNode, Node newNode);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package net.mynero.wallet.fragment.dialog
|
||||
|
||||
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.Toast
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import net.mynero.wallet.R
|
||||
import net.mynero.wallet.data.Node
|
||||
import net.mynero.wallet.data.Node.Companion.fromJson
|
||||
import net.mynero.wallet.util.Helper.getClipBoardText
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
class EditNodeBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
@JvmField
|
||||
var listener: EditNodeListener? = null
|
||||
@JvmField
|
||||
var node: Node? = null
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.edit_node_bottom_sheet_dialog, null)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val deleteNodeButton = view.findViewById<Button>(R.id.delete_node_button)
|
||||
val doneEditingButton = view.findViewById<Button>(R.id.done_editing_button)
|
||||
val addressEditText = view.findViewById<EditText>(R.id.address_edittext)
|
||||
val portEditText = view.findViewById<EditText>(R.id.node_port_edittext)
|
||||
val nodeNameEditText = view.findViewById<EditText>(R.id.node_name_edittext)
|
||||
val usernameEditText = view.findViewById<EditText>(R.id.username_edittext)
|
||||
val passwordEditText = view.findViewById<EditText>(R.id.password_edittext)
|
||||
val pastePasswordImageButton =
|
||||
view.findViewById<ImageButton>(R.id.paste_password_imagebutton)
|
||||
if (node == null) return
|
||||
addressEditText.setText(node?.host)
|
||||
portEditText.setText("${node?.rpcPort}")
|
||||
nodeNameEditText.setText(node?.name)
|
||||
usernameEditText.setText(node?.username)
|
||||
if (node?.password?.isNotEmpty() == true) {
|
||||
passwordEditText.setText(node?.password)
|
||||
passwordEditText.visibility = View.VISIBLE
|
||||
pastePasswordImageButton.visibility = View.VISIBLE
|
||||
}
|
||||
usernameEditText.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||
override fun afterTextChanged(editable: Editable) {
|
||||
if (editable.toString().isEmpty()) {
|
||||
passwordEditText.text = null
|
||||
passwordEditText.visibility = View.GONE
|
||||
pastePasswordImageButton.visibility = View.GONE
|
||||
} else {
|
||||
passwordEditText.visibility = View.VISIBLE
|
||||
pastePasswordImageButton.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
})
|
||||
addPasteListener(view, addressEditText, R.id.paste_address_imagebutton)
|
||||
addPasteListener(view, usernameEditText, R.id.paste_username_imagebutton)
|
||||
addPasteListener(view, passwordEditText, R.id.paste_password_imagebutton)
|
||||
deleteNodeButton.setOnClickListener {
|
||||
listener?.onNodeDeleted(node)
|
||||
dismiss()
|
||||
}
|
||||
doneEditingButton.setOnClickListener {
|
||||
var nodeAddr = addressEditText.text.toString()
|
||||
val portString = portEditText.text.toString()
|
||||
val nodeName = nodeNameEditText.text.toString()
|
||||
val user = usernameEditText.text.toString()
|
||||
val pass = passwordEditText.text.toString()
|
||||
if (nodeName.isEmpty()) {
|
||||
Toast.makeText(context, "Enter node name", Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
} else if (nodeAddr.isEmpty() || portString.isEmpty()) {
|
||||
Toast.makeText(context, "Enter node address", Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
} else if (user.isNotEmpty() && pass.isEmpty()) {
|
||||
Toast.makeText(context, "Enter password", Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
val jsonObject = JSONObject()
|
||||
try {
|
||||
if (user.isNotEmpty()) {
|
||||
jsonObject.put("username", user)
|
||||
jsonObject.put("password", pass)
|
||||
}
|
||||
if (nodeAddr.contains(":") && !nodeAddr.startsWith("[") && !nodeAddr.endsWith("]")) nodeAddr =
|
||||
"[$nodeAddr]"
|
||||
jsonObject.put("host", nodeAddr)
|
||||
jsonObject.put("rpcPort", portString.toInt())
|
||||
jsonObject.put("network", "mainnet")
|
||||
jsonObject.put("name", nodeName)
|
||||
listener?.onNodeEdited(node, fromJson(jsonObject))
|
||||
dismiss()
|
||||
} catch (e: JSONException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addPasteListener(root: View, editText: EditText, layoutId: Int) {
|
||||
val pasteImageButton = root.findViewById<ImageButton>(layoutId)
|
||||
pasteImageButton.setOnClickListener {
|
||||
val ctx = context
|
||||
if (ctx != null) {
|
||||
editText.setText(getClipBoardText(ctx))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface EditNodeListener {
|
||||
fun onNodeDeleted(node: Node?)
|
||||
fun onNodeEdited(oldNode: Node?, newNode: Node?)
|
||||
}
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
package net.mynero.wallet.fragment.dialog;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import net.mynero.wallet.R;
|
||||
import net.mynero.wallet.adapter.NodeSelectionAdapter;
|
||||
import net.mynero.wallet.data.DefaultNodes;
|
||||
import net.mynero.wallet.data.Node;
|
||||
import net.mynero.wallet.model.WalletManager;
|
||||
import net.mynero.wallet.service.PrefService;
|
||||
import net.mynero.wallet.util.Constants;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class NodeSelectionBottomSheetDialog extends BottomSheetDialogFragment implements NodeSelectionAdapter.NodeSelectionAdapterListener {
|
||||
public NodeSelectionDialogListener listener = null;
|
||||
private NodeSelectionAdapter adapter = null;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.node_selection_bottom_sheet_dialog, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
ArrayList<Node> nodes = new ArrayList<>();
|
||||
adapter = new NodeSelectionAdapter(this);
|
||||
|
||||
RecyclerView recyclerView = view.findViewById(R.id.node_selection_recyclerview);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
||||
Button addNodeButton = view.findViewById(R.id.add_node_button);
|
||||
addNodeButton.setOnClickListener(view1 -> {
|
||||
if (listener != null) {
|
||||
listener.onClickedAddNode();
|
||||
}
|
||||
dismiss();
|
||||
});
|
||||
|
||||
String nodesArray = PrefService.getInstance().getString(Constants.PREF_CUSTOM_NODES, "[]");
|
||||
JSONArray jsonArray = null;
|
||||
try {
|
||||
jsonArray = new JSONArray(nodesArray);
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
JSONObject nodeJsonObject = null;
|
||||
try {
|
||||
nodeJsonObject = jsonArray.getJSONObject(i);
|
||||
if (nodeJsonObject != null) {
|
||||
Node node = Node.fromJson(nodeJsonObject);
|
||||
if (node != null) {
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
// if stored node is old string format, try parse and upgrade, if fail then remove
|
||||
try {
|
||||
String nodeString = jsonArray.getString(i);
|
||||
Node node = Node.fromString(nodeString);
|
||||
if (node != null) {
|
||||
nodes.add(node);
|
||||
jsonArray.put(i, node.toJson());
|
||||
} else {
|
||||
jsonArray.remove(i);
|
||||
}
|
||||
} catch (JSONException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrefService.getInstance().edit().putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString()).apply();
|
||||
|
||||
for (DefaultNodes defaultNode : DefaultNodes.values()) {
|
||||
nodes.add(Node.fromJson(defaultNode.getJson()));
|
||||
}
|
||||
adapter.submitList(nodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectNode(Node node) {
|
||||
Activity activity = getActivity();
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(() -> {
|
||||
Toast.makeText(activity, getString(R.string.node_selected), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
}
|
||||
PrefService.getInstance().edit().putString(Constants.PREF_NODE_2, node.toJson().toString()).apply();
|
||||
WalletManager.getInstance().setDaemon(node);
|
||||
adapter.updateSelectedNode();
|
||||
listener.onNodeSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSelectEditNode(Node node) {
|
||||
if (listener != null) {
|
||||
listener.onClickedEditNode(node);
|
||||
}
|
||||
dismiss();
|
||||
return true;
|
||||
}
|
||||
|
||||
public interface NodeSelectionDialogListener {
|
||||
void onNodeSelected();
|
||||
|
||||
void onClickedEditNode(Node node);
|
||||
|
||||
void onClickedAddNode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
package net.mynero.wallet.fragment.dialog
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.Toast
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import net.mynero.wallet.R
|
||||
import net.mynero.wallet.adapter.NodeSelectionAdapter
|
||||
import net.mynero.wallet.adapter.NodeSelectionAdapter.NodeSelectionAdapterListener
|
||||
import net.mynero.wallet.data.DefaultNodes
|
||||
import net.mynero.wallet.data.Node
|
||||
import net.mynero.wallet.data.Node.Companion.fromJson
|
||||
import net.mynero.wallet.data.Node.Companion.fromString
|
||||
import net.mynero.wallet.model.WalletManager
|
||||
import net.mynero.wallet.service.PrefService
|
||||
import net.mynero.wallet.util.Constants
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
class NodeSelectionBottomSheetDialog : BottomSheetDialogFragment(), NodeSelectionAdapterListener {
|
||||
@JvmField
|
||||
var listener: NodeSelectionDialogListener? = null
|
||||
private var adapter: NodeSelectionAdapter? = null
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.node_selection_bottom_sheet_dialog, null)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val nodes = ArrayList<Node>()
|
||||
adapter = NodeSelectionAdapter(this)
|
||||
val recyclerView = view.findViewById<RecyclerView>(R.id.node_selection_recyclerview)
|
||||
recyclerView.layoutManager = LinearLayoutManager(activity)
|
||||
recyclerView.adapter = adapter
|
||||
val addNodeButton = view.findViewById<Button>(R.id.add_node_button)
|
||||
addNodeButton.setOnClickListener {
|
||||
if (listener != null) {
|
||||
listener?.onClickedAddNode()
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
val nodesArray = PrefService.instance?.getString(Constants.PREF_CUSTOM_NODES, "[]")
|
||||
val jsonArray: JSONArray = try {
|
||||
JSONArray(nodesArray)
|
||||
} catch (e: JSONException) {
|
||||
return
|
||||
}
|
||||
for (i in 0 until jsonArray.length()) {
|
||||
var nodeJsonObject: JSONObject?
|
||||
try {
|
||||
nodeJsonObject = jsonArray.getJSONObject(i)
|
||||
if (nodeJsonObject != null) {
|
||||
val node = fromJson(nodeJsonObject)
|
||||
if (node != null) {
|
||||
nodes.add(node)
|
||||
}
|
||||
}
|
||||
} catch (e: JSONException) {
|
||||
// if stored node is old string format, try parse and upgrade, if fail then remove
|
||||
try {
|
||||
val nodeString = jsonArray.getString(i)
|
||||
val node = fromString(nodeString)
|
||||
if (node != null) {
|
||||
nodes.add(node)
|
||||
jsonArray.put(i, node.toJson())
|
||||
} else {
|
||||
jsonArray.remove(i)
|
||||
}
|
||||
} catch (ex: JSONException) {
|
||||
throw RuntimeException(ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
PrefService.instance?.edit()?.putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString())?.apply()
|
||||
for (defaultNode in DefaultNodes.values()) {
|
||||
fromJson(defaultNode.json)?.let { nodes.add(it) }
|
||||
}
|
||||
adapter?.submitList(nodes)
|
||||
}
|
||||
|
||||
override fun onSelectNode(node: Node?) {
|
||||
val activity: Activity? = activity
|
||||
activity?.runOnUiThread {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.node_selected),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
PrefService.instance?.edit()?.putString(Constants.PREF_NODE_2, node?.toJson().toString())?.apply()
|
||||
WalletManager.instance?.setDaemon(node)
|
||||
adapter?.updateSelectedNode()
|
||||
listener?.onNodeSelected()
|
||||
}
|
||||
|
||||
override fun onSelectEditNode(node: Node?): Boolean {
|
||||
if (listener != null) {
|
||||
listener?.onClickedEditNode(node)
|
||||
}
|
||||
dismiss()
|
||||
return true
|
||||
}
|
||||
|
||||
interface NodeSelectionDialogListener {
|
||||
fun onNodeSelected()
|
||||
fun onClickedEditNode(node: Node?)
|
||||
fun onClickedAddNode()
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package net.mynero.wallet.fragment.dialog;
|
||||
|
||||
import android.os.Bundle;
|
||||
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 androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import net.mynero.wallet.R;
|
||||
import net.mynero.wallet.model.WalletManager;
|
||||
import net.mynero.wallet.util.Constants;
|
||||
import net.mynero.wallet.util.Helper;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class PasswordBottomSheetDialog extends BottomSheetDialogFragment {
|
||||
public PasswordListener listener = null;
|
||||
public boolean cancelable = false;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.password_bottom_sheet_dialog, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setCancelable(cancelable);
|
||||
File walletFile = new File(getActivity().getApplicationInfo().dataDir, Constants.WALLET_NAME);
|
||||
|
||||
ImageButton pastePasswordImageButton = view.findViewById(R.id.paste_password_imagebutton);
|
||||
EditText passwordEditText = view.findViewById(R.id.wallet_password_edittext);
|
||||
Button unlockWalletButton = view.findViewById(R.id.unlock_wallet_button);
|
||||
|
||||
pastePasswordImageButton.setOnClickListener(view1 -> {
|
||||
passwordEditText.setText(Helper.getClipBoardText(view.getContext()));
|
||||
});
|
||||
|
||||
unlockWalletButton.setOnClickListener(view1 -> {
|
||||
String password = passwordEditText.getText().toString();
|
||||
boolean success = checkPassword(walletFile, password);
|
||||
if (success) {
|
||||
listener.onPasswordSuccess(password);
|
||||
dismiss();
|
||||
} else {
|
||||
listener.onPasswordFail();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean checkPassword(File walletFile, String password) {
|
||||
return WalletManager.getInstance().verifyWalletPasswordOnly(walletFile.getAbsolutePath() + ".keys", password);
|
||||
}
|
||||
|
||||
public interface PasswordListener {
|
||||
void onPasswordSuccess(String password);
|
||||
|
||||
void onPasswordFail();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package net.mynero.wallet.fragment.dialog
|
||||
|
||||
import android.os.Bundle
|
||||
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 com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import net.mynero.wallet.R
|
||||
import net.mynero.wallet.model.WalletManager
|
||||
import net.mynero.wallet.util.Constants
|
||||
import net.mynero.wallet.util.Helper.getClipBoardText
|
||||
import java.io.File
|
||||
|
||||
class PasswordBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
@JvmField
|
||||
var listener: PasswordListener? = null
|
||||
@JvmField
|
||||
var cancelable = false
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.password_bottom_sheet_dialog, null)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
isCancelable = cancelable
|
||||
val walletFile = File(activity?.applicationInfo?.dataDir, Constants.WALLET_NAME)
|
||||
val pastePasswordImageButton =
|
||||
view.findViewById<ImageButton>(R.id.paste_password_imagebutton)
|
||||
val passwordEditText = view.findViewById<EditText>(R.id.wallet_password_edittext)
|
||||
val unlockWalletButton = view.findViewById<Button>(R.id.unlock_wallet_button)
|
||||
pastePasswordImageButton.setOnClickListener {
|
||||
passwordEditText.setText(
|
||||
getClipBoardText(view.context)
|
||||
)
|
||||
}
|
||||
unlockWalletButton.setOnClickListener {
|
||||
val password = passwordEditText.text.toString()
|
||||
val success = checkPassword(walletFile, password)
|
||||
if (success) {
|
||||
listener?.onPasswordSuccess(password)
|
||||
dismiss()
|
||||
} else {
|
||||
listener?.onPasswordFail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPassword(walletFile: File, password: String): Boolean {
|
||||
return WalletManager.instance?.verifyWalletPasswordOnly(
|
||||
walletFile.absolutePath + ".keys",
|
||||
password
|
||||
) == true
|
||||
}
|
||||
|
||||
interface PasswordListener {
|
||||
fun onPasswordSuccess(password: String)
|
||||
fun onPasswordFail()
|
||||
}
|
||||
}
|
|
@ -1,356 +0,0 @@
|
|||
package net.mynero.wallet.fragment.dialog;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
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.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.google.zxing.client.android.Intents;
|
||||
import com.journeyapps.barcodescanner.ScanContract;
|
||||
import com.journeyapps.barcodescanner.ScanOptions;
|
||||
|
||||
import net.mynero.wallet.MoneroApplication;
|
||||
import net.mynero.wallet.R;
|
||||
import net.mynero.wallet.model.CoinsInfo;
|
||||
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.service.UTXOService;
|
||||
import net.mynero.wallet.util.Constants;
|
||||
import net.mynero.wallet.util.Helper;
|
||||
import net.mynero.wallet.util.UriData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SendBottomSheetDialog extends BottomSheetDialogFragment {
|
||||
private final MutableLiveData<Boolean> _sendingMax = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<PendingTransaction> _pendingTransaction = new MutableLiveData<>(null);
|
||||
public ArrayList<String> selectedUtxos = new ArrayList<>();
|
||||
public LiveData<Boolean> sendingMax = _sendingMax;
|
||||
public LiveData<PendingTransaction> pendingTransaction = _pendingTransaction;
|
||||
public UriData uriData = null; private final ActivityResultLauncher<String> cameraPermissionsLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(),
|
||||
granted -> {
|
||||
if (granted) {
|
||||
onScan();
|
||||
} else {
|
||||
Toast.makeText(getActivity(), getString(R.string.no_camera_permission), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
public boolean isChurning = false;
|
||||
public Listener listener = null;
|
||||
public PendingTransaction.Priority priority;
|
||||
private EditText addressEditText;
|
||||
private EditText amountEditText;
|
||||
private final ActivityResultLauncher<ScanOptions> barcodeLauncher = registerForActivityResult(new ScanContract(),
|
||||
result -> {
|
||||
if (result.getContents() != null) {
|
||||
pasteAddress(result.getContents());
|
||||
}
|
||||
});
|
||||
private TextView sendAllTextView;
|
||||
private TextView feeTextView;
|
||||
private TextView addressTextView;
|
||||
private TextView amountTextView;
|
||||
private TextView feeRadioGroupLabelTextView;
|
||||
private TextView selectedUtxosValueTextView;
|
||||
private Button createButton;
|
||||
private Button sendButton;
|
||||
private Button sendMaxButton;
|
||||
private ImageButton pasteAddressImageButton;
|
||||
private ImageButton scanAddressImageButton;
|
||||
private RadioGroup feeRadioGroup;
|
||||
private TextView donateTextView;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.send_bottom_sheet_dialog, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
pasteAddressImageButton = view.findViewById(R.id.paste_address_imagebutton);
|
||||
scanAddressImageButton = view.findViewById(R.id.scan_address_imagebutton);
|
||||
sendMaxButton = view.findViewById(R.id.send_max_button);
|
||||
addressEditText = view.findViewById(R.id.address_edittext);
|
||||
amountEditText = view.findViewById(R.id.amount_edittext);
|
||||
sendButton = view.findViewById(R.id.send_tx_button);
|
||||
createButton = view.findViewById(R.id.create_tx_button);
|
||||
sendAllTextView = view.findViewById(R.id.sending_all_textview);
|
||||
feeTextView = view.findViewById(R.id.fee_textview);
|
||||
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);
|
||||
selectedUtxosValueTextView = view.findViewById(R.id.selected_utxos_value_textview);
|
||||
donateTextView = view.findViewById(R.id.donate_label_textview);
|
||||
donateTextView.setOnClickListener(view1 -> addressEditText.setText(Constants.DONATE_ADDRESS));
|
||||
if (uriData != null) {
|
||||
addressEditText.setText(uriData.address);
|
||||
if (uriData.hasAmount()) {
|
||||
amountEditText.setText(uriData.getAmount());
|
||||
}
|
||||
}
|
||||
|
||||
if (!selectedUtxos.isEmpty()) {
|
||||
long selectedValue = 0;
|
||||
|
||||
for (CoinsInfo coinsInfo : UTXOService.getInstance().getUtxos()) {
|
||||
if (selectedUtxos.contains(coinsInfo.keyImage)) {
|
||||
selectedValue += coinsInfo.amount;
|
||||
}
|
||||
}
|
||||
|
||||
String valueString = Wallet.getDisplayAmount(selectedValue);
|
||||
selectedUtxosValueTextView.setVisibility(View.VISIBLE);
|
||||
if (isChurning) {
|
||||
_sendingMax.postValue(true);
|
||||
sendMaxButton.setEnabled(false);
|
||||
selectedUtxosValueTextView.setText(getResources().getString(R.string.selected_utxos_value_churning, valueString));
|
||||
} else {
|
||||
selectedUtxosValueTextView.setText(getResources().getString(R.string.selected_utxos_value, valueString));
|
||||
}
|
||||
} else {
|
||||
selectedUtxosValueTextView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
bindObservers();
|
||||
bindListeners();
|
||||
}
|
||||
|
||||
private void bindObservers() {
|
||||
BalanceService.instance.balanceInfo.observe(getViewLifecycleOwner(), balanceInfo -> {
|
||||
createButton.setEnabled(balanceInfo.getRawUnlocked() != 0);
|
||||
if (!isChurning) {
|
||||
sendMaxButton.setEnabled(balanceInfo.getRawUnlocked() != 0);
|
||||
}
|
||||
});
|
||||
|
||||
sendingMax.observe(getViewLifecycleOwner(), sendingMax -> {
|
||||
if (pendingTransaction.getValue() == null) {
|
||||
if (sendingMax) {
|
||||
amountEditText.setVisibility(View.INVISIBLE);
|
||||
sendAllTextView.setVisibility(View.VISIBLE);
|
||||
sendMaxButton.setText(getText(R.string.undo));
|
||||
} else {
|
||||
amountEditText.setVisibility(View.VISIBLE);
|
||||
sendAllTextView.setVisibility(View.GONE);
|
||||
sendMaxButton.setText(getText(R.string.send_max));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
pendingTransaction.observe(getViewLifecycleOwner(), pendingTx -> {
|
||||
showConfirmationLayout(pendingTx != null);
|
||||
|
||||
if (pendingTx != null) {
|
||||
String address = addressEditText.getText().toString();
|
||||
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())));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
pasteAddressImageButton.setOnClickListener(view1 -> {
|
||||
Context ctx = getContext();
|
||||
if (ctx != null) {
|
||||
String clipboard = Helper.getClipBoardText(ctx);
|
||||
if (clipboard != null) {
|
||||
pasteAddress(clipboard);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
scanAddressImageButton.setOnClickListener(view1 -> {
|
||||
onScan();
|
||||
});
|
||||
|
||||
sendMaxButton.setOnClickListener(view1 -> {
|
||||
boolean currentValue = sendingMax.getValue() != null ? sendingMax.getValue() : false;
|
||||
_sendingMax.postValue(!currentValue);
|
||||
});
|
||||
|
||||
createButton.setOnClickListener(view1 -> {
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity != null) {
|
||||
boolean sendAll = sendingMax.getValue() != null ? sendingMax.getValue() : false;
|
||||
String address = addressEditText.getText().toString().trim();
|
||||
String amount = amountEditText.getText().toString().trim();
|
||||
boolean validAddress = Wallet.isAddressValid(address);
|
||||
if (validAddress && (!amount.isEmpty() || sendAll)) {
|
||||
long amountRaw = Wallet.getAmountFromString(amount);
|
||||
long balance = BalanceService.instance.getUnlockedBalanceRaw();
|
||||
if ((amountRaw >= balance || amountRaw <= 0) && !sendAll) {
|
||||
Toast.makeText(activity, getString(R.string.send_amount_invalid), Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
Toast.makeText(activity, getString(R.string.creating_tx), Toast.LENGTH_SHORT).show();
|
||||
createButton.setEnabled(false);
|
||||
createTx(address, amount, sendAll, priority);
|
||||
} else if (!validAddress) {
|
||||
Toast.makeText(activity, getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(activity, getString(R.string.send_amount_empty), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
sendButton.setOnClickListener(view1 -> {
|
||||
PendingTransaction pendingTx = pendingTransaction.getValue();
|
||||
if (pendingTx != null) {
|
||||
Toast.makeText(getActivity(), getString(R.string.sending_tx), Toast.LENGTH_SHORT).show();
|
||||
sendButton.setEnabled(false);
|
||||
sendTx(pendingTx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onScan() {
|
||||
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);
|
||||
barcodeLauncher.launch(options);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendTx(PendingTransaction pendingTx) {
|
||||
Activity activity = getActivity();
|
||||
if (activity != null) {
|
||||
((MoneroApplication) activity.getApplication()).getExecutor().execute(() -> {
|
||||
boolean success = TxService.instance.sendTx(pendingTx);
|
||||
activity.runOnUiThread(() -> {
|
||||
if (success) {
|
||||
Toast.makeText(activity, getString(R.string.sent_tx), Toast.LENGTH_SHORT).show();
|
||||
if (listener != null) {
|
||||
listener.onSentTransaction();
|
||||
}
|
||||
dismiss();
|
||||
} else {
|
||||
sendButton.setEnabled(true);
|
||||
Toast.makeText(activity, getString(R.string.error_sending_tx), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void createTx(String address, String amount, boolean sendAll, PendingTransaction.Priority feePriority) {
|
||||
Activity activity = getActivity();
|
||||
if (activity != null) {
|
||||
((MoneroApplication) activity.getApplication()).getExecutor().execute(() -> {
|
||||
try {
|
||||
PendingTransaction pendingTx = TxService.instance.createTx(address, amount, sendAll, feePriority, selectedUtxos);
|
||||
if (pendingTx != null && pendingTx.getStatus() == PendingTransaction.Status.Status_Ok) {
|
||||
_pendingTransaction.postValue(pendingTx);
|
||||
} else {
|
||||
activity.runOnUiThread(() -> {
|
||||
createButton.setEnabled(true);
|
||||
if (pendingTx != null) {
|
||||
Toast.makeText(activity, getString(R.string.error_creating_tx, pendingTx.getErrorString()), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
activity.runOnUiThread(() -> {
|
||||
createButton.setEnabled(true);
|
||||
Toast.makeText(activity, e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void showConfirmationLayout(boolean show) {
|
||||
if (show) {
|
||||
sendButton.setVisibility(View.VISIBLE);
|
||||
addressEditText.setVisibility(View.GONE);
|
||||
amountEditText.setVisibility(View.GONE);
|
||||
sendAllTextView.setVisibility(View.GONE);
|
||||
createButton.setVisibility(View.GONE);
|
||||
sendMaxButton.setVisibility(View.GONE);
|
||||
pasteAddressImageButton.setVisibility(View.GONE);
|
||||
scanAddressImageButton.setVisibility(View.GONE);
|
||||
feeTextView.setVisibility(View.VISIBLE);
|
||||
addressTextView.setVisibility(View.VISIBLE);
|
||||
amountTextView.setVisibility(View.VISIBLE);
|
||||
selectedUtxosValueTextView.setVisibility(View.GONE);
|
||||
feeRadioGroup.setVisibility(View.GONE);
|
||||
feeRadioGroupLabelTextView.setVisibility(View.GONE);
|
||||
donateTextView.setVisibility(View.GONE);
|
||||
} else {
|
||||
sendButton.setVisibility(View.GONE);
|
||||
addressEditText.setVisibility(View.VISIBLE);
|
||||
amountEditText.setVisibility(Boolean.TRUE.equals(sendingMax.getValue()) ? View.GONE : View.VISIBLE);
|
||||
sendAllTextView.setVisibility(Boolean.TRUE.equals(sendingMax.getValue()) ? View.VISIBLE : View.GONE);
|
||||
createButton.setVisibility(View.VISIBLE);
|
||||
sendMaxButton.setVisibility(View.VISIBLE);
|
||||
pasteAddressImageButton.setVisibility(View.VISIBLE);
|
||||
scanAddressImageButton.setVisibility(View.VISIBLE);
|
||||
feeTextView.setVisibility(View.GONE);
|
||||
addressTextView.setVisibility(View.GONE);
|
||||
amountTextView.setVisibility(View.GONE);
|
||||
if (!selectedUtxos.isEmpty()) {
|
||||
selectedUtxosValueTextView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
feeRadioGroup.setVisibility(View.VISIBLE);
|
||||
feeRadioGroupLabelTextView.setVisibility(View.VISIBLE);
|
||||
donateTextView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void pasteAddress(String address) {
|
||||
UriData uriData = UriData.parse(address);
|
||||
if (uriData != null) {
|
||||
addressEditText.setText(uriData.address);
|
||||
if (uriData.hasAmount()) {
|
||||
amountEditText.setText(uriData.getAmount());
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(getActivity(), getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
void onSentTransaction();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,392 @@
|
|||
package net.mynero.wallet.fragment.dialog
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
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.RadioGroup
|
||||
import android.widget.TextView
|
||||
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
|
||||
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
|
||||
import net.mynero.wallet.model.Wallet.Companion.isAddressValid
|
||||
import net.mynero.wallet.service.BalanceService
|
||||
import net.mynero.wallet.service.TxService
|
||||
import net.mynero.wallet.service.UTXOService
|
||||
import net.mynero.wallet.util.Constants
|
||||
import net.mynero.wallet.util.Helper
|
||||
import net.mynero.wallet.util.Helper.getCameraPermission
|
||||
import net.mynero.wallet.util.Helper.getClipBoardText
|
||||
import net.mynero.wallet.util.UriData
|
||||
import net.mynero.wallet.util.UriData.Companion.parse
|
||||
|
||||
class SendBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
private val _sendingMax = MutableLiveData(false)
|
||||
private val _pendingTransaction = MutableLiveData<PendingTransaction?>(null)
|
||||
@JvmField
|
||||
var selectedUtxos = ArrayList<String>()
|
||||
private var sendingMax: LiveData<Boolean?> = _sendingMax
|
||||
private var pendingTransaction: LiveData<PendingTransaction?> = _pendingTransaction
|
||||
@JvmField
|
||||
var uriData: UriData? = null
|
||||
private val cameraPermissionsLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { granted: Boolean ->
|
||||
if (granted) {
|
||||
onScan()
|
||||
} else {
|
||||
Toast.makeText(activity, getString(R.string.no_camera_permission), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
@JvmField
|
||||
var isChurning = false
|
||||
@JvmField
|
||||
var listener: Listener? = null
|
||||
var priority: PendingTransaction.Priority = PendingTransaction.Priority.Priority_Low
|
||||
private var addressEditText: EditText? = null
|
||||
private var amountEditText: EditText? = null
|
||||
private val barcodeLauncher = registerForActivityResult(
|
||||
ScanContract()
|
||||
) { result: ScanIntentResult ->
|
||||
if (result.contents != null) {
|
||||
pasteAddress(result.contents)
|
||||
}
|
||||
}
|
||||
private var sendAllTextView: TextView? = null
|
||||
private var feeTextView: TextView? = null
|
||||
private var addressTextView: TextView? = null
|
||||
private var amountTextView: TextView? = null
|
||||
private var feeRadioGroupLabelTextView: TextView? = null
|
||||
private var selectedUtxosValueTextView: TextView? = null
|
||||
private var createButton: Button? = null
|
||||
private var sendButton: Button? = null
|
||||
private var sendMaxButton: Button? = null
|
||||
private var pasteAddressImageButton: ImageButton? = null
|
||||
private var scanAddressImageButton: ImageButton? = null
|
||||
private var feeRadioGroup: RadioGroup? = null
|
||||
private var donateTextView: TextView? = null
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.send_bottom_sheet_dialog, null)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
pasteAddressImageButton = view.findViewById(R.id.paste_address_imagebutton)
|
||||
scanAddressImageButton = view.findViewById(R.id.scan_address_imagebutton)
|
||||
sendMaxButton = view.findViewById(R.id.send_max_button)
|
||||
addressEditText = view.findViewById(R.id.address_edittext)
|
||||
amountEditText = view.findViewById(R.id.amount_edittext)
|
||||
sendButton = view.findViewById(R.id.send_tx_button)
|
||||
createButton = view.findViewById(R.id.create_tx_button)
|
||||
sendAllTextView = view.findViewById(R.id.sending_all_textview)
|
||||
feeTextView = view.findViewById(R.id.fee_textview)
|
||||
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)
|
||||
selectedUtxosValueTextView = view.findViewById(R.id.selected_utxos_value_textview)
|
||||
donateTextView = view.findViewById(R.id.donate_label_textview)
|
||||
donateTextView?.setOnClickListener {
|
||||
addressEditText?.setText(
|
||||
Constants.DONATE_ADDRESS
|
||||
)
|
||||
}
|
||||
if (uriData != null) {
|
||||
addressEditText?.setText(uriData?.address)
|
||||
if (uriData?.hasAmount() == true) {
|
||||
amountEditText?.setText(uriData?.amount)
|
||||
}
|
||||
}
|
||||
if (selectedUtxos.isNotEmpty()) {
|
||||
var selectedValue: Long = 0
|
||||
val utxos = UTXOService.instance?.getUtxos() ?: return
|
||||
for (coinsInfo in utxos) {
|
||||
if (selectedUtxos.contains(coinsInfo.keyImage)) {
|
||||
selectedValue += coinsInfo.amount
|
||||
}
|
||||
}
|
||||
val valueString = Wallet.getDisplayAmount(selectedValue)
|
||||
selectedUtxosValueTextView?.visibility = View.VISIBLE
|
||||
if (isChurning) {
|
||||
_sendingMax.postValue(true)
|
||||
sendMaxButton?.isEnabled = false
|
||||
selectedUtxosValueTextView?.text = resources.getString(
|
||||
R.string.selected_utxos_value_churning,
|
||||
valueString
|
||||
)
|
||||
} else {
|
||||
selectedUtxosValueTextView?.text = resources.getString(
|
||||
R.string.selected_utxos_value,
|
||||
valueString
|
||||
)
|
||||
}
|
||||
} else {
|
||||
selectedUtxosValueTextView?.visibility = View.GONE
|
||||
}
|
||||
bindObservers()
|
||||
bindListeners()
|
||||
}
|
||||
|
||||
private fun bindObservers() {
|
||||
BalanceService.instance?.balanceInfo?.observe(viewLifecycleOwner) { balanceInfo ->
|
||||
createButton?.isEnabled = balanceInfo?.rawUnlocked != 0L
|
||||
if (!isChurning) {
|
||||
sendMaxButton?.isEnabled = balanceInfo?.rawUnlocked != 0L
|
||||
}
|
||||
}
|
||||
sendingMax.observe(viewLifecycleOwner) { sendingMax: Boolean? ->
|
||||
if (pendingTransaction.value == null) {
|
||||
if (sendingMax == true) {
|
||||
amountEditText?.visibility = View.INVISIBLE
|
||||
sendAllTextView?.visibility = View.VISIBLE
|
||||
sendMaxButton?.text = getText(R.string.undo)
|
||||
} else {
|
||||
amountEditText?.visibility = View.VISIBLE
|
||||
sendAllTextView?.visibility = View.GONE
|
||||
sendMaxButton?.text = getText(R.string.send_max)
|
||||
}
|
||||
}
|
||||
}
|
||||
pendingTransaction.observe(viewLifecycleOwner) { pendingTx: PendingTransaction? ->
|
||||
showConfirmationLayout(pendingTx != null)
|
||||
if (pendingTx != null) {
|
||||
val address = addressEditText?.text.toString()
|
||||
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 fun bindListeners() {
|
||||
feeRadioGroup?.check(R.id.low_fee_radiobutton)
|
||||
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
|
||||
}
|
||||
}
|
||||
pasteAddressImageButton?.setOnClickListener {
|
||||
val ctx = context
|
||||
if (ctx != null) {
|
||||
val clipboard = getClipBoardText(ctx)
|
||||
clipboard?.let { pasteAddress(it) }
|
||||
}
|
||||
}
|
||||
scanAddressImageButton?.setOnClickListener { onScan() }
|
||||
sendMaxButton?.setOnClickListener {
|
||||
val currentValue = if (sendingMax.value != null) sendingMax.value else false
|
||||
_sendingMax.postValue(currentValue == false)
|
||||
}
|
||||
createButton?.setOnClickListener {
|
||||
val activity = activity
|
||||
if (activity != null) {
|
||||
val sendAll = sendingMax.value ?: false
|
||||
val address = addressEditText?.text.toString().trim { it <= ' ' }
|
||||
val amount = amountEditText?.text.toString().trim { it <= ' ' }
|
||||
val validAddress = isAddressValid(address)
|
||||
if (validAddress && (amount.isNotEmpty() || sendAll)) {
|
||||
val amountRaw = getAmountFromString(amount)
|
||||
val balance = BalanceService.instance?.unlockedBalanceRaw ?: 0
|
||||
if ((amountRaw >= balance || amountRaw <= 0) && !sendAll) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.send_amount_invalid),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
Toast.makeText(activity, getString(R.string.creating_tx), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
createButton?.isEnabled = false
|
||||
createTx(address, amount, sendAll, priority)
|
||||
} else if (!validAddress) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.send_address_invalid),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.send_amount_empty),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
sendButton?.setOnClickListener {
|
||||
val pendingTx = pendingTransaction.value
|
||||
if (pendingTx != null) {
|
||||
Toast.makeText(activity, getString(R.string.sending_tx), Toast.LENGTH_SHORT).show()
|
||||
sendButton?.isEnabled = false
|
||||
sendTx(pendingTx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onScan() {
|
||||
if (activity?.let { 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)
|
||||
barcodeLauncher.launch(options)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendTx(pendingTx: PendingTransaction) {
|
||||
val activity: Activity? = activity
|
||||
if (activity != null) {
|
||||
(activity.application as MoneroApplication).executor?.execute {
|
||||
val success = TxService.instance?.sendTx(pendingTx)
|
||||
activity.runOnUiThread(Runnable {
|
||||
if (success == true) {
|
||||
Toast.makeText(activity, getString(R.string.sent_tx), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
if (listener != null) {
|
||||
listener?.onSentTransaction()
|
||||
}
|
||||
dismiss()
|
||||
} else {
|
||||
sendButton?.isEnabled = true
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.error_sending_tx),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTx(
|
||||
address: String,
|
||||
amount: String,
|
||||
sendAll: Boolean,
|
||||
feePriority: PendingTransaction.Priority
|
||||
) {
|
||||
val activity: Activity? = activity
|
||||
if (activity != null) {
|
||||
(activity.application as MoneroApplication).executor?.execute {
|
||||
try {
|
||||
val pendingTx = TxService.instance?.createTx(
|
||||
address,
|
||||
amount,
|
||||
sendAll,
|
||||
feePriority,
|
||||
selectedUtxos
|
||||
)
|
||||
if (pendingTx != null && pendingTx.status === PendingTransaction.Status.Status_Ok) {
|
||||
_pendingTransaction.postValue(pendingTx)
|
||||
} else {
|
||||
activity.runOnUiThread(Runnable {
|
||||
createButton?.isEnabled = true
|
||||
if (pendingTx != null) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(
|
||||
R.string.error_creating_tx,
|
||||
pendingTx.getErrorString()
|
||||
),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
activity.runOnUiThread(Runnable {
|
||||
createButton?.isEnabled = true
|
||||
Toast.makeText(activity, e.message, Toast.LENGTH_SHORT).show()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showConfirmationLayout(show: Boolean) {
|
||||
if (show) {
|
||||
sendButton?.visibility = View.VISIBLE
|
||||
addressEditText?.visibility = View.GONE
|
||||
amountEditText?.visibility = View.GONE
|
||||
sendAllTextView?.visibility = View.GONE
|
||||
createButton?.visibility = View.GONE
|
||||
sendMaxButton?.visibility = View.GONE
|
||||
pasteAddressImageButton?.visibility = View.GONE
|
||||
scanAddressImageButton?.visibility = View.GONE
|
||||
feeTextView?.visibility = View.VISIBLE
|
||||
addressTextView?.visibility = View.VISIBLE
|
||||
amountTextView?.visibility = View.VISIBLE
|
||||
selectedUtxosValueTextView?.visibility = View.GONE
|
||||
feeRadioGroup?.visibility = View.GONE
|
||||
feeRadioGroupLabelTextView?.visibility = View.GONE
|
||||
donateTextView?.visibility = View.GONE
|
||||
} else {
|
||||
sendButton?.visibility = View.GONE
|
||||
addressEditText?.visibility = View.VISIBLE
|
||||
amountEditText?.visibility =
|
||||
if (java.lang.Boolean.TRUE == sendingMax.value) View.GONE else View.VISIBLE
|
||||
sendAllTextView?.visibility =
|
||||
if (java.lang.Boolean.TRUE == sendingMax.value) View.VISIBLE else View.GONE
|
||||
createButton?.visibility = View.VISIBLE
|
||||
sendMaxButton?.visibility = View.VISIBLE
|
||||
pasteAddressImageButton?.visibility = View.VISIBLE
|
||||
scanAddressImageButton?.visibility = View.VISIBLE
|
||||
feeTextView?.visibility = View.GONE
|
||||
addressTextView?.visibility = View.GONE
|
||||
amountTextView?.visibility = View.GONE
|
||||
if (selectedUtxos.isNotEmpty()) {
|
||||
selectedUtxosValueTextView?.visibility = View.VISIBLE
|
||||
}
|
||||
feeRadioGroup?.visibility = View.VISIBLE
|
||||
feeRadioGroupLabelTextView?.visibility = View.VISIBLE
|
||||
donateTextView?.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
private fun pasteAddress(address: String) {
|
||||
val uriData = parse(address)
|
||||
if (uriData != null) {
|
||||
addressEditText?.setText(uriData.address)
|
||||
if (uriData.hasAmount()) {
|
||||
amountEditText?.setText(uriData.amount)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(activity, getString(R.string.send_address_invalid), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onSentTransaction()
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package net.mynero.wallet.fragment.dialog;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import net.mynero.wallet.R;
|
||||
import net.mynero.wallet.model.Wallet;
|
||||
import net.mynero.wallet.model.WalletManager;
|
||||
import net.mynero.wallet.service.PrefService;
|
||||
import net.mynero.wallet.util.Constants;
|
||||
import net.mynero.wallet.util.Helper;
|
||||
|
||||
public class WalletKeysBottomSheetDialog extends BottomSheetDialogFragment {
|
||||
public String password = "";
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.wallet_keys_bottom_sheet_dialog, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
ImageButton copyViewKeyImageButton = view.findViewById(R.id.copy_viewkey_imagebutton);
|
||||
TextView informationTextView = view.findViewById(R.id.information_textview); // seed
|
||||
TextView viewKeyTextView = view.findViewById(R.id.viewkey_textview);
|
||||
TextView restoreHeightTextView = view.findViewById(R.id.restore_height_textview);
|
||||
|
||||
Wallet wallet = WalletManager.getInstance().getWallet();
|
||||
String seed = wallet.getSeed("");
|
||||
boolean usesOffset = PrefService.getInstance().getBoolean(Constants.PREF_USES_OFFSET, false);
|
||||
if (usesOffset) {
|
||||
seed = wallet.getSeed(password);
|
||||
view.findViewById(R.id.wallet_seed_offset_textview).setVisibility(View.VISIBLE);
|
||||
}
|
||||
String privateViewKey = wallet.getSecretViewKey();
|
||||
|
||||
informationTextView.setText(seed);
|
||||
viewKeyTextView.setText(privateViewKey);
|
||||
restoreHeightTextView.setText(wallet.getRestoreHeight() + "");
|
||||
|
||||
copyViewKeyImageButton.setOnClickListener(view1 -> Helper.clipBoardCopy(getContext(), "private view-key", privateViewKey));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package net.mynero.wallet.fragment.dialog
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import net.mynero.wallet.R
|
||||
import net.mynero.wallet.model.WalletManager
|
||||
import net.mynero.wallet.service.PrefService
|
||||
import net.mynero.wallet.util.Constants
|
||||
import net.mynero.wallet.util.Helper.clipBoardCopy
|
||||
|
||||
class WalletKeysBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
@JvmField
|
||||
var password = ""
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.wallet_keys_bottom_sheet_dialog, null)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val copyViewKeyImageButton = view.findViewById<ImageButton>(R.id.copy_viewkey_imagebutton)
|
||||
val informationTextView = view.findViewById<TextView>(R.id.information_textview) // seed
|
||||
val viewKeyTextView = view.findViewById<TextView>(R.id.viewkey_textview)
|
||||
val restoreHeightTextView = view.findViewById<TextView>(R.id.restore_height_textview)
|
||||
val wallet = WalletManager.instance!!.wallet
|
||||
var seed = wallet!!.getSeed("")
|
||||
val usesOffset = PrefService.instance!!.getBoolean(Constants.PREF_USES_OFFSET, false)
|
||||
if (usesOffset) {
|
||||
seed = wallet.getSeed(password)
|
||||
view.findViewById<View>(R.id.wallet_seed_offset_textview).visibility = View.VISIBLE
|
||||
}
|
||||
val privateViewKey = wallet.getSecretViewKey()
|
||||
informationTextView.text = seed
|
||||
viewKeyTextView.text = privateViewKey
|
||||
restoreHeightTextView.text = "${wallet.getRestoreHeight()}"
|
||||
copyViewKeyImageButton.setOnClickListener {
|
||||
clipBoardCopy(
|
||||
context, "private view-key", privateViewKey
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,204 +0,0 @@
|
|||
package net.mynero.wallet.fragment.home;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import net.mynero.wallet.MainActivity;
|
||||
import net.mynero.wallet.R;
|
||||
import net.mynero.wallet.adapter.TransactionInfoAdapter;
|
||||
import net.mynero.wallet.model.TransactionInfo;
|
||||
import net.mynero.wallet.model.Wallet;
|
||||
import net.mynero.wallet.model.WalletManager;
|
||||
import net.mynero.wallet.service.BalanceService;
|
||||
import net.mynero.wallet.service.BlockchainService;
|
||||
import net.mynero.wallet.service.HistoryService;
|
||||
import net.mynero.wallet.service.PrefService;
|
||||
import net.mynero.wallet.util.Constants;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class HomeFragment extends Fragment implements TransactionInfoAdapter.TxInfoAdapterListener {
|
||||
|
||||
long startHeight = 0;
|
||||
private HomeViewModel mViewModel;
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_home, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
MainActivity mainActivity = (MainActivity) getActivity();
|
||||
mViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
|
||||
bindObservers(view);
|
||||
bindListeners(view);
|
||||
|
||||
mainActivity.restartEvents.observe(getViewLifecycleOwner(), o -> {
|
||||
bindObservers(view);
|
||||
bindListeners(view);
|
||||
});
|
||||
}
|
||||
|
||||
private void bindListeners(View view) {
|
||||
ImageView settingsImageView = view.findViewById(R.id.settings_imageview);
|
||||
Button sendButton = view.findViewById(R.id.send_button);
|
||||
Button receiveButton = view.findViewById(R.id.receive_button);
|
||||
|
||||
settingsImageView.setOnClickListener(view12 -> {
|
||||
navigate(HomeFragmentDirections.navToSettings());
|
||||
});
|
||||
|
||||
sendButton.setOnClickListener(view1 -> {
|
||||
navigate(HomeFragmentDirections.navToSend());
|
||||
|
||||
});
|
||||
|
||||
receiveButton.setOnClickListener(view1 -> {
|
||||
navigate(HomeFragmentDirections.navToReceive());
|
||||
});
|
||||
}
|
||||
|
||||
private void bindObservers(View view) {
|
||||
RecyclerView txHistoryRecyclerView = view.findViewById(R.id.transaction_history_recyclerview);
|
||||
TextView unlockedBalanceTextView = view.findViewById(R.id.balance_unlocked_textview);
|
||||
TextView lockedBalanceTextView = view.findViewById(R.id.balance_locked_textview);
|
||||
|
||||
BalanceService balanceService = BalanceService.instance;
|
||||
HistoryService historyService = HistoryService.getInstance();
|
||||
BlockchainService blockchainService = BlockchainService.instance;
|
||||
|
||||
if (balanceService != null) {
|
||||
balanceService.balanceInfo.observe(getViewLifecycleOwner(), balanceInfo -> {
|
||||
if (balanceInfo != null) {
|
||||
unlockedBalanceTextView.setText(balanceInfo.getUnlockedDisplay());
|
||||
|
||||
if (balanceInfo.getLockedDisplay().equals(Constants.STREET_MODE_BALANCE) || balanceInfo.isLockedBalanceZero()) {
|
||||
lockedBalanceTextView.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
lockedBalanceTextView.setText(getString(R.string.wallet_locked_balance_text, balanceInfo.getLockedDisplay()));
|
||||
lockedBalanceTextView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ProgressBar progressBar = view.findViewById(R.id.sync_progress_bar);
|
||||
TextView progressBarText = view.findViewById(R.id.sync_progress_text);
|
||||
if (blockchainService != null) {
|
||||
blockchainService.height.observe(getViewLifecycleOwner(), height -> {
|
||||
Wallet wallet = WalletManager.getInstance().getWallet();
|
||||
if (!wallet.isSynchronized()) {
|
||||
if (startHeight == 0 && height != 1) {
|
||||
startHeight = height;
|
||||
}
|
||||
long daemonHeight = blockchainService.getDaemonHeight();
|
||||
long n = daemonHeight - height;
|
||||
int x = 100 - Math.round(100f * n / (1f * daemonHeight - startHeight));
|
||||
progressBar.setIndeterminate(height <= 1 || daemonHeight <= 0);
|
||||
if (height > 1 && daemonHeight > 1) {
|
||||
progressBar.setProgress(x);
|
||||
progressBarText.setVisibility(View.VISIBLE);
|
||||
progressBarText.setText("Syncing... " + n + " blocks remaining");
|
||||
} else {
|
||||
progressBarText.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
progressBarText.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
TransactionInfoAdapter adapter = new TransactionInfoAdapter(this);
|
||||
txHistoryRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
txHistoryRecyclerView.setAdapter(adapter);
|
||||
if (historyService != null) {
|
||||
historyService.history.observe(getViewLifecycleOwner(), history -> {
|
||||
if (history.isEmpty()) {
|
||||
// DISPLAYING EMPTY WALLET HISTORY
|
||||
Wallet wallet = WalletManager.getInstance().getWallet();
|
||||
int textResId, botImgResId = 0;
|
||||
if (wallet != null && wallet.isSynchronized()) {
|
||||
textResId = R.string.no_history_nget_some_monero_in_here;
|
||||
botImgResId = R.drawable.xmrchan_empty; // img for synchronized
|
||||
} else {
|
||||
textResId = R.string.no_history_loading;
|
||||
botImgResId = R.drawable.xmrchan_loading; // img for loading
|
||||
}
|
||||
|
||||
txHistoryRecyclerView.setVisibility(View.GONE);
|
||||
displayEmptyHistory(true, view, textResId, botImgResId);
|
||||
} else {
|
||||
// POPULATED WALLET HISTORY
|
||||
Collections.sort(history);
|
||||
if (history.size() > 100) {
|
||||
adapter.submitList(history.subList(0, 99));
|
||||
} else {
|
||||
adapter.submitList(history);
|
||||
}
|
||||
txHistoryRecyclerView.setVisibility(View.VISIBLE);
|
||||
displayEmptyHistory(false, view, R.string.no_history_nget_some_monero_in_here, R.drawable.xmrchan_loading);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickTransaction(TransactionInfo txInfo) {
|
||||
NavDirections directions = HomeFragmentDirections.navToTransaction(txInfo);
|
||||
navigate(directions);
|
||||
}
|
||||
|
||||
private void navigate(NavDirections destination) {
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity != null) {
|
||||
FragmentManager fm = activity.getSupportFragmentManager();
|
||||
NavHostFragment navHostFragment =
|
||||
(NavHostFragment) fm.findFragmentById(R.id.nav_host_fragment);
|
||||
if (navHostFragment != null) {
|
||||
navHostFragment.getNavController().navigate(destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void displayEmptyHistory(boolean display, View view, int textResId, int botImgResId) {
|
||||
TextView mnrjTextView = view.findViewById(R.id.monerochan_empty_tx_textview);
|
||||
TextView textView = view.findViewById(R.id.empty_tx_textview);
|
||||
ImageView botImageView = view.findViewById(R.id.monerochan_imageview);
|
||||
view.findViewById(R.id.no_history_layout).setVisibility(display ? View.VISIBLE : View.GONE);
|
||||
boolean displayMonerochan = PrefService.getInstance().getBoolean(Constants.PREF_MONEROCHAN, true);
|
||||
if (displayMonerochan) {
|
||||
botImageView.setVisibility(View.VISIBLE);
|
||||
mnrjTextView.setVisibility(View.VISIBLE);
|
||||
textView.setVisibility(View.GONE);
|
||||
} else {
|
||||
botImageView.setVisibility(View.GONE);
|
||||
mnrjTextView.setVisibility(View.GONE);
|
||||
textView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
botImageView.setImageResource(botImgResId);
|
||||
mnrjTextView.setText(textResId);
|
||||
textView.setText(textResId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
package net.mynero.wallet.fragment.home
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.NavDirections
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import net.mynero.wallet.MainActivity
|
||||
import net.mynero.wallet.R
|
||||
import net.mynero.wallet.adapter.TransactionInfoAdapter
|
||||
import net.mynero.wallet.adapter.TransactionInfoAdapter.TxInfoAdapterListener
|
||||
import net.mynero.wallet.model.TransactionInfo
|
||||
import net.mynero.wallet.model.WalletManager
|
||||
import net.mynero.wallet.service.BalanceService
|
||||
import net.mynero.wallet.service.BlockchainService
|
||||
import net.mynero.wallet.service.HistoryService
|
||||
import net.mynero.wallet.service.PrefService
|
||||
import net.mynero.wallet.util.Constants
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class HomeFragment : Fragment(), TxInfoAdapterListener {
|
||||
private var startHeight: Long = 0
|
||||
private var mViewModel: HomeViewModel? = null
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_home, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val mainActivity = activity as MainActivity?
|
||||
mViewModel = ViewModelProvider(this)[HomeViewModel::class.java]
|
||||
bindObservers(view)
|
||||
bindListeners(view)
|
||||
mainActivity?.restartEvents?.observe(viewLifecycleOwner) {
|
||||
bindObservers(view)
|
||||
bindListeners(view)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindListeners(view: View) {
|
||||
val settingsImageView = view.findViewById<ImageView>(R.id.settings_imageview)
|
||||
val sendButton = view.findViewById<Button>(R.id.send_button)
|
||||
val receiveButton = view.findViewById<Button>(R.id.receive_button)
|
||||
settingsImageView.setOnClickListener { navigate(HomeFragmentDirections.navToSettings()) }
|
||||
sendButton.setOnClickListener { navigate(HomeFragmentDirections.navToSend()) }
|
||||
receiveButton.setOnClickListener { navigate(HomeFragmentDirections.navToReceive()) }
|
||||
}
|
||||
|
||||
private fun bindObservers(view: View) {
|
||||
val txHistoryRecyclerView =
|
||||
view.findViewById<RecyclerView>(R.id.transaction_history_recyclerview)
|
||||
val unlockedBalanceTextView = view.findViewById<TextView>(R.id.balance_unlocked_textview)
|
||||
val lockedBalanceTextView = view.findViewById<TextView>(R.id.balance_locked_textview)
|
||||
val balanceService = BalanceService.instance
|
||||
val historyService = HistoryService.instance
|
||||
val blockchainService = BlockchainService.instance
|
||||
|
||||
balanceService?.balanceInfo?.observe(viewLifecycleOwner) { balanceInfo ->
|
||||
if (balanceInfo != null) {
|
||||
unlockedBalanceTextView.text = balanceInfo.unlockedDisplay
|
||||
if (balanceInfo.lockedDisplay == Constants.STREET_MODE_BALANCE || balanceInfo.isLockedBalanceZero) {
|
||||
lockedBalanceTextView.visibility = View.INVISIBLE
|
||||
} else {
|
||||
lockedBalanceTextView.text = getString(
|
||||
R.string.wallet_locked_balance_text,
|
||||
balanceInfo.lockedDisplay
|
||||
)
|
||||
lockedBalanceTextView.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
val progressBar = view.findViewById<ProgressBar>(R.id.sync_progress_bar)
|
||||
val progressBarText = view.findViewById<TextView>(R.id.sync_progress_text)
|
||||
blockchainService?.height?.observe(viewLifecycleOwner) { height: Long ->
|
||||
val wallet = WalletManager.instance?.wallet
|
||||
if (wallet?.isSynchronized == false) {
|
||||
if (startHeight == 0L && height != 1L) {
|
||||
startHeight = height
|
||||
}
|
||||
val daemonHeight = blockchainService.daemonHeight
|
||||
val n = daemonHeight - height
|
||||
val x = Math.round(100 - (100f * n / (1f * daemonHeight - startHeight)))
|
||||
progressBar.isIndeterminate = height <= 1 || daemonHeight <= 0
|
||||
if (height > 1 && daemonHeight > 1) {
|
||||
progressBar.progress = x
|
||||
progressBarText.visibility = View.VISIBLE
|
||||
progressBarText.text = "Syncing... $n blocks remaining"
|
||||
} else {
|
||||
progressBarText.visibility = View.GONE
|
||||
}
|
||||
} else {
|
||||
progressBar.visibility = View.INVISIBLE
|
||||
progressBarText.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
val adapter = TransactionInfoAdapter(this)
|
||||
txHistoryRecyclerView.layoutManager = LinearLayoutManager(activity)
|
||||
txHistoryRecyclerView.adapter = adapter
|
||||
historyService?.history?.observe(viewLifecycleOwner) { history: List<TransactionInfo> ->
|
||||
if (history.isEmpty()) {
|
||||
// DISPLAYING EMPTY WALLET HISTORY
|
||||
val wallet = WalletManager.instance?.wallet
|
||||
val textResId: Int
|
||||
val botImgResId = if (wallet != null && wallet.isSynchronized) {
|
||||
textResId = R.string.no_history_nget_some_monero_in_here
|
||||
R.drawable.xmrchan_empty // img for synchronized
|
||||
} else {
|
||||
textResId = R.string.no_history_loading
|
||||
R.drawable.xmrchan_loading // img for loading
|
||||
}
|
||||
txHistoryRecyclerView.visibility = View.GONE
|
||||
displayEmptyHistory(true, view, textResId, botImgResId)
|
||||
} else {
|
||||
// POPULATED WALLET HISTORY
|
||||
history.sorted()
|
||||
if (history.size > 100) {
|
||||
adapter.submitList(history.subList(0, 99))
|
||||
} else {
|
||||
adapter.submitList(history)
|
||||
}
|
||||
txHistoryRecyclerView.visibility = View.VISIBLE
|
||||
displayEmptyHistory(
|
||||
false,
|
||||
view,
|
||||
R.string.no_history_nget_some_monero_in_here,
|
||||
R.drawable.xmrchan_loading
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClickTransaction(txInfo: TransactionInfo?) {
|
||||
val directions: NavDirections = HomeFragmentDirections.navToTransaction(txInfo)
|
||||
navigate(directions)
|
||||
}
|
||||
|
||||
private fun navigate(destination: NavDirections) {
|
||||
val activity = activity
|
||||
if (activity != null) {
|
||||
val fm = activity.supportFragmentManager
|
||||
val navHostFragment = fm.findFragmentById(R.id.nav_host_fragment) as NavHostFragment?
|
||||
navHostFragment?.navController?.navigate(destination)
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayEmptyHistory(
|
||||
display: Boolean,
|
||||
view: View,
|
||||
textResId: Int,
|
||||
botImgResId: Int
|
||||
) {
|
||||
val mnrjTextView = view.findViewById<TextView>(R.id.monerochan_empty_tx_textview)
|
||||
val textView = view.findViewById<TextView>(R.id.empty_tx_textview)
|
||||
val botImageView = view.findViewById<ImageView>(R.id.monerochan_imageview)
|
||||
view.findViewById<View>(R.id.no_history_layout).visibility =
|
||||
if (display) View.VISIBLE else View.GONE
|
||||
val displayMonerochan = PrefService.instance?.getBoolean(Constants.PREF_MONEROCHAN, true)
|
||||
if (displayMonerochan == true) {
|
||||
botImageView.visibility = View.VISIBLE
|
||||
mnrjTextView.visibility = View.VISIBLE
|
||||
textView.visibility = View.GONE
|
||||
} else {
|
||||
botImageView.visibility = View.GONE
|
||||
mnrjTextView.visibility = View.GONE
|
||||
textView.visibility = View.VISIBLE
|
||||
}
|
||||
botImageView.setImageResource(botImgResId)
|
||||
mnrjTextView.setText(textResId)
|
||||
textView.setText(textResId)
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package net.mynero.wallet.fragment.home;
|
||||
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
public class HomeViewModel extends ViewModel {
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package net.mynero.wallet.fragment.home
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class HomeViewModel : ViewModel()
|
|
@ -70,7 +70,7 @@ public class UtxosFragment extends Fragment implements CoinsInfoAdapter.CoinsInf
|
|||
sendUtxosButton.setOnClickListener(view1 -> {
|
||||
ArrayList<String> selectedKeyImages = new ArrayList<>();
|
||||
for (CoinsInfo coinsInfo : adapter.selectedUtxos.values()) {
|
||||
selectedKeyImages.add(coinsInfo.keyImage);
|
||||
selectedKeyImages.add(coinsInfo.getKeyImage());
|
||||
}
|
||||
SendBottomSheetDialog sendDialog = new SendBottomSheetDialog();
|
||||
sendDialog.listener = this;
|
||||
|
@ -80,7 +80,7 @@ public class UtxosFragment extends Fragment implements CoinsInfoAdapter.CoinsInf
|
|||
churnUtxosButton.setOnClickListener(view1 -> {
|
||||
ArrayList<String> selectedKeyImages = new ArrayList<>();
|
||||
for (CoinsInfo coinsInfo : adapter.selectedUtxos.values()) {
|
||||
selectedKeyImages.add(coinsInfo.keyImage);
|
||||
selectedKeyImages.add(coinsInfo.getKeyImage());
|
||||
}
|
||||
SendBottomSheetDialog sendDialog = new SendBottomSheetDialog();
|
||||
sendDialog.listener = this;
|
||||
|
@ -101,7 +101,7 @@ public class UtxosFragment extends Fragment implements CoinsInfoAdapter.CoinsInf
|
|||
HashMap<String, CoinsInfo> filteredUtxos = new HashMap<>();
|
||||
for (CoinsInfo coinsInfo : utxos) {
|
||||
if (!coinsInfo.isSpent()) {
|
||||
filteredUtxos.put(coinsInfo.pubKey, coinsInfo);
|
||||
filteredUtxos.put(coinsInfo.getPubKey(), coinsInfo);
|
||||
}
|
||||
}
|
||||
if (filteredUtxos.isEmpty()) {
|
||||
|
|
|
@ -20,28 +20,15 @@ import android.os.Parcelable
|
|||
import android.os.Parcelable.Creator
|
||||
|
||||
class CoinsInfo : Parcelable, Comparable<CoinsInfo> {
|
||||
@JvmField
|
||||
var globalOutputIndex: Long
|
||||
var isSpent = false
|
||||
|
||||
@JvmField
|
||||
var keyImage: String? = null
|
||||
|
||||
@JvmField
|
||||
var amount: Long = 0
|
||||
|
||||
@JvmField
|
||||
var hash: String? = null
|
||||
|
||||
@JvmField
|
||||
var pubKey: String? = null
|
||||
var isUnlocked = false
|
||||
|
||||
@JvmField
|
||||
var localOutputIndex: Long = 0
|
||||
var isFrozen = false
|
||||
|
||||
@JvmField
|
||||
var address: String? = null
|
||||
|
||||
constructor(
|
||||
|
@ -68,7 +55,7 @@ class CoinsInfo : Parcelable, Comparable<CoinsInfo> {
|
|||
this.address = address
|
||||
}
|
||||
|
||||
protected constructor(`in`: Parcel) {
|
||||
private constructor(`in`: Parcel) {
|
||||
globalOutputIndex = `in`.readLong()
|
||||
}
|
||||
|
||||
|
@ -80,15 +67,15 @@ class CoinsInfo : Parcelable, Comparable<CoinsInfo> {
|
|||
parcel.writeLong(globalOutputIndex)
|
||||
}
|
||||
|
||||
override fun compareTo(another: CoinsInfo): Int {
|
||||
override fun compareTo(other: CoinsInfo): Int {
|
||||
val b1 = amount
|
||||
val b2 = another.amount
|
||||
val b2 = other.amount
|
||||
return if (b1 > b2) {
|
||||
-1
|
||||
} else if (b1 < b2) {
|
||||
1
|
||||
} else {
|
||||
hash!!.compareTo(another.hash!!)
|
||||
other.hash?.let { hash?.compareTo(it) } ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,11 +15,10 @@
|
|||
*/
|
||||
package net.mynero.wallet.model
|
||||
|
||||
enum class NetworkType(@JvmField val value: Int) {
|
||||
enum class NetworkType(val value: Int) {
|
||||
NetworkType_Mainnet(0), NetworkType_Testnet(1), NetworkType_Stagenet(2);
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun fromInteger(n: Int): NetworkType? {
|
||||
when (n) {
|
||||
0 -> return NetworkType_Mainnet
|
||||
|
|
|
@ -38,7 +38,7 @@ class PendingTransaction internal constructor(var handle: Long) {
|
|||
Status_Ok, Status_Error, Status_Critical
|
||||
}
|
||||
|
||||
enum class Priority(@JvmField val value: Int) {
|
||||
enum class Priority(value: Int) {
|
||||
Priority_Default(0), Priority_Low(1), Priority_Medium(2), Priority_High(3), Priority_Last(4);
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -19,6 +19,8 @@ package net.mynero.wallet.model;
|
|||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.mynero.wallet.data.Subaddress;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -100,7 +102,7 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
|
|||
addressIndex = in.readInt();
|
||||
confirmations = in.readLong();
|
||||
subaddressLabel = in.readString();
|
||||
transfers = in.readArrayList(Transfer.class.getClassLoader());
|
||||
in.readList(transfers, Transfer.class.getClassLoader());
|
||||
txKey = in.readString();
|
||||
notes = in.readString();
|
||||
address = in.readString();
|
||||
|
@ -117,6 +119,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
|
|||
return subaddressLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return direction + "@" + blockheight + " " + amount;
|
||||
}
|
||||
|
@ -171,13 +175,11 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
|
|||
}
|
||||
|
||||
public static Direction fromInteger(int n) {
|
||||
switch (n) {
|
||||
case 0:
|
||||
return Direction_In;
|
||||
case 1:
|
||||
return Direction_Out;
|
||||
}
|
||||
return null;
|
||||
return switch (n) {
|
||||
case 0 -> Direction_In;
|
||||
case 1 -> Direction_Out;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package net.mynero.wallet.model
|
||||
|
||||
class TransactionOutput(@JvmField val destination: String, @JvmField val amount: Long)
|
||||
class TransactionOutput(val destination: String, val amount: Long)
|
|
@ -17,7 +17,6 @@ package net.mynero.wallet.model
|
|||
|
||||
import android.util.Pair
|
||||
import net.mynero.wallet.data.Subaddress
|
||||
import net.mynero.wallet.model.NetworkType.Companion.fromInteger
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
|
@ -129,7 +128,7 @@ class Wallet {
|
|||
|
||||
external fun getPath(): String?
|
||||
val networkType: NetworkType?
|
||||
get() = fromInteger(nettype())
|
||||
get() = NetworkType.fromInteger(nettype())
|
||||
|
||||
external fun nettype(): Int
|
||||
|
||||
|
@ -255,7 +254,7 @@ class Wallet {
|
|||
destinations: List<Pair<String, Long>>,
|
||||
priority: PendingTransaction.Priority
|
||||
): Long {
|
||||
val _priority = priority.value
|
||||
val _priority = priority.ordinal
|
||||
return estimateTransactionFee(destinations, _priority)
|
||||
}
|
||||
|
||||
|
@ -270,7 +269,7 @@ class Wallet {
|
|||
keyImages: ArrayList<String>
|
||||
): PendingTransaction? {
|
||||
disposePendingTransaction()
|
||||
val _priority = priority.value
|
||||
val _priority = priority.ordinal
|
||||
val txHandle = createSweepTransaction(dstAddr, "", 0, _priority, accountIndex, keyImages)
|
||||
pendingTransaction = PendingTransaction(txHandle)
|
||||
return pendingTransaction
|
||||
|
@ -282,7 +281,7 @@ class Wallet {
|
|||
keyImages: ArrayList<String>
|
||||
): PendingTransaction? {
|
||||
disposePendingTransaction()
|
||||
val _priority = priority.value
|
||||
val _priority = priority.ordinal
|
||||
val destinations = ArrayList<String>()
|
||||
val amounts = LongArray(outputs.size)
|
||||
for (i in outputs.indices) {
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.util.Calendar
|
|||
import java.util.Locale
|
||||
|
||||
class WalletManager {
|
||||
@JvmField
|
||||
val networkType = NetworkType.NetworkType_Mainnet
|
||||
var wallet: Wallet? = null
|
||||
private set
|
||||
|
@ -209,7 +208,7 @@ class WalletManager {
|
|||
subaddressLookahead: String
|
||||
): Long
|
||||
|
||||
external fun closeJ(wallet: Wallet?): Boolean
|
||||
private external fun closeJ(wallet: Wallet?): Boolean
|
||||
fun close(wallet: Wallet): Boolean {
|
||||
unmanageWallet(wallet)
|
||||
val closed = closeJ(wallet)
|
||||
|
@ -225,28 +224,28 @@ class WalletManager {
|
|||
return walletExists(aFile.absolutePath)
|
||||
}
|
||||
|
||||
external fun walletExists(path: String?): Boolean
|
||||
private external fun walletExists(path: String?): Boolean
|
||||
external fun verifyWalletPassword(
|
||||
keys_file_name: String?,
|
||||
keysFileName: String?,
|
||||
password: String?,
|
||||
watch_only: Boolean
|
||||
watchOnly: Boolean
|
||||
): Boolean
|
||||
|
||||
fun verifyWalletPasswordOnly(keys_file_name: String, password: String): Boolean {
|
||||
return queryWalletDeviceJ(keys_file_name, password) >= 0
|
||||
fun verifyWalletPasswordOnly(keysFileName: String, password: String): Boolean {
|
||||
return queryWalletDeviceJ(keysFileName, password) >= 0
|
||||
}
|
||||
|
||||
fun queryWalletDevice(keys_file_name: String, password: String): Wallet.Device {
|
||||
val device = queryWalletDeviceJ(keys_file_name, password)
|
||||
fun queryWalletDevice(keysFileName: String, password: String): Wallet.Device {
|
||||
val device = queryWalletDeviceJ(keysFileName, password)
|
||||
return Wallet.Device.values()[device + 1] // mapping is monero+1=android
|
||||
}
|
||||
|
||||
private external fun queryWalletDeviceJ(keys_file_name: String, password: String): Int
|
||||
private external fun queryWalletDeviceJ(keysFileName: String, password: String): Int
|
||||
fun findWallets(path: File): List<WalletInfo> {
|
||||
val wallets: MutableList<WalletInfo> = ArrayList()
|
||||
Timber.d("Scanning: %s", path.absolutePath)
|
||||
val found =
|
||||
path.listFiles { dir, filename -> filename.endsWith(".keys") } ?: return emptyList()
|
||||
path.listFiles { _, filename -> filename.endsWith(".keys") } ?: return emptyList()
|
||||
for (i in found.indices) {
|
||||
val filename = found[i].name
|
||||
val f = File(
|
||||
|
@ -291,8 +290,8 @@ class WalletManager {
|
|||
|
||||
external fun startMining(
|
||||
address: String?,
|
||||
background_mining: Boolean,
|
||||
ignore_battery: Boolean
|
||||
backgroundMining: Boolean,
|
||||
ignoreBattery: Boolean
|
||||
): Boolean
|
||||
|
||||
external fun stopMining(): Boolean
|
||||
|
@ -320,7 +319,6 @@ class WalletManager {
|
|||
|
||||
companion object {
|
||||
//TODO: maybe put these in an enum like in monero core - but why?
|
||||
@JvmField
|
||||
var LOGLEVEL_SILENT = -1
|
||||
var LOGLEVEL_WARN = 0
|
||||
var LOGLEVEL_INFO = 1
|
||||
|
@ -349,7 +347,6 @@ class WalletManager {
|
|||
NetworkType.NetworkType_Testnet -> "9A-"
|
||||
NetworkType.NetworkType_Mainnet -> "4-"
|
||||
NetworkType.NetworkType_Stagenet -> "5-"
|
||||
else -> throw IllegalStateException("Unsupported Network: $networkType")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,7 @@ import net.mynero.wallet.model.WalletManager
|
|||
class BlockchainService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
||||
private val _currentHeight = MutableLiveData(0L)
|
||||
private val _connectionStatus = MutableLiveData(ConnectionStatus.ConnectionStatus_Disconnected)
|
||||
|
||||
@JvmField
|
||||
var height: LiveData<Long> = _currentHeight
|
||||
|
||||
@JvmField
|
||||
var connectionStatus: LiveData<ConnectionStatus> = _connectionStatus
|
||||
var daemonHeight: Long = 0
|
||||
|
@ -22,7 +19,7 @@ class BlockchainService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
|||
lastDaemonHeightUpdateTimeMs = t
|
||||
} else {
|
||||
if (t - lastDaemonHeightUpdateTimeMs > 120000) {
|
||||
field = WalletManager.instance!!.wallet!!.getDaemonBlockChainHeight()
|
||||
field = WalletManager.instance?.wallet?.getDaemonBlockChainHeight() ?: return
|
||||
lastDaemonHeightUpdateTimeMs = t
|
||||
}
|
||||
}
|
||||
|
@ -37,8 +34,8 @@ class BlockchainService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
|||
_currentHeight.postValue(currentHeight)
|
||||
}
|
||||
|
||||
val currentHeight: Long
|
||||
get() = WalletManager.instance!!.wallet!!.getBlockChainHeight()
|
||||
private val currentHeight: Long
|
||||
get() = WalletManager.instance?.wallet?.getBlockChainHeight() ?: -1
|
||||
|
||||
fun setConnectionStatus(status: ConnectionStatus) {
|
||||
_connectionStatus.postValue(status)
|
||||
|
|
|
@ -20,7 +20,7 @@ class HistoryService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
|||
}
|
||||
|
||||
private fun getHistory(): List<TransactionInfo> {
|
||||
return WalletManager.instance!!.wallet!!.history!!.all
|
||||
return WalletManager.instance?.wallet?.history?.all ?: emptyList()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -32,10 +32,10 @@ import java.security.SecureRandom
|
|||
* used to create handler classes. Note that start() must still be called.
|
||||
* The started Thread has a stck size of STACK_SIZE (=5MB)
|
||||
*/
|
||||
class MoneroHandlerThread(name: String?, val listener: Listener?, wallet: Wallet) :
|
||||
class MoneroHandlerThread(name: String, val listener: Listener?, wallet: Wallet) :
|
||||
Thread(null, null, name, THREAD_STACK_SIZE), WalletListener {
|
||||
private val wallet: Wallet
|
||||
var triesLeft = 5
|
||||
private var triesLeft = 5
|
||||
|
||||
init {
|
||||
this.wallet = wallet
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package net.mynero.wallet.service
|
||||
|
||||
open class ServiceBase(@JvmField val thread: MoneroHandlerThread?)
|
||||
open class ServiceBase(val thread: MoneroHandlerThread?)
|
|
@ -55,7 +55,7 @@ class UTXOService(thread: MoneroHandlerThread?) : ServiceBase(thread) {
|
|||
frozenCoins = frozenCoinsCopy
|
||||
saveFrozenCoins()
|
||||
refreshUtxos()
|
||||
BalanceService.instance!!.refreshBalance()
|
||||
BalanceService.instance?.refreshBalance()
|
||||
}
|
||||
|
||||
fun isCoinFrozen(coinsInfo: CoinsInfo): Boolean {
|
||||
|
@ -65,7 +65,7 @@ class UTXOService(thread: MoneroHandlerThread?) : ServiceBase(thread) {
|
|||
@Throws(JSONException::class)
|
||||
private fun loadFrozenCoins() {
|
||||
val prefService = PrefService.instance
|
||||
val frozenCoinsArrayString = prefService!!.getString(Constants.PREF_FROZEN_COINS, "[]")
|
||||
val frozenCoinsArrayString = prefService?.getString(Constants.PREF_FROZEN_COINS, "[]")
|
||||
val frozenCoinsArray = JSONArray(frozenCoinsArrayString)
|
||||
for (i in 0 until frozenCoinsArray.length()) {
|
||||
val pubKey = frozenCoinsArray.getString(i)
|
||||
|
@ -81,8 +81,7 @@ class UTXOService(thread: MoneroHandlerThread?) : ServiceBase(thread) {
|
|||
for (pubKey in frozenCoinsCopy) {
|
||||
jsonArray.put(pubKey)
|
||||
}
|
||||
prefService!!.edit()!!
|
||||
.putString(Constants.PREF_FROZEN_COINS, jsonArray.toString()).apply()
|
||||
prefService?.edit()?.putString(Constants.PREF_FROZEN_COINS, jsonArray.toString())?.apply()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
|
@ -91,7 +90,7 @@ class UTXOService(thread: MoneroHandlerThread?) : ServiceBase(thread) {
|
|||
sendAll: Boolean,
|
||||
feePriority: PendingTransaction.Priority
|
||||
): ArrayList<String> {
|
||||
val basicFeeEstimate = calculateBasicFee(amount, feePriority)
|
||||
val basicFeeEstimate = calculateBasicFee(amount, feePriority) ?: return arrayListOf()
|
||||
val amountWithBasicFee = amount + basicFeeEstimate
|
||||
val selectedUtxos = ArrayList<String>()
|
||||
val seenTxs = ArrayList<String>()
|
||||
|
@ -125,7 +124,7 @@ class UTXOService(thread: MoneroHandlerThread?) : ServiceBase(thread) {
|
|||
return selectedUtxos
|
||||
}
|
||||
|
||||
private fun calculateBasicFee(amount: Long, feePriority: PendingTransaction.Priority): Long {
|
||||
private fun calculateBasicFee(amount: Long, feePriority: PendingTransaction.Priority): Long? {
|
||||
val destinations = ArrayList<Pair<String, Long>>()
|
||||
destinations.add(
|
||||
Pair(
|
||||
|
@ -134,7 +133,7 @@ class UTXOService(thread: MoneroHandlerThread?) : ServiceBase(thread) {
|
|||
)
|
||||
)
|
||||
// destination string doesn't actually matter here, so i'm using the donation address. amount also technically doesn't matter
|
||||
return WalletManager.instance!!.wallet!!.estimateTransactionFee(destinations, feePriority)
|
||||
return WalletManager.instance?.wallet?.estimateTransactionFee(destinations, feePriority)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -19,7 +19,6 @@ object Constants {
|
|||
const val NAV_ARG_TXINFO = "nav_arg_txinfo"
|
||||
const val STREET_MODE_BALANCE = "#.############"
|
||||
|
||||
@JvmField
|
||||
val DONATION_ADDRESSES = arrayOf(
|
||||
"87BqQYkugEzh6Tuyotm2uc3DzJzKM6MuZaC161e6u1TsQxxPmXVPHpdNRyK47JY4d1hhbe25YVz4e9vTXCLDxvHkRXEAeBC", // primary Mysu Donation address
|
||||
"89QoPxs4cQSGbJrJddwzV3Ca7s2gVYHE1Xd1hGZafuVJVyNKt2LCQhxUdBF57PemxQiX3dmGUZLRRAzfeYyh9pq3GiWsDVo", // second Mysu Donation address
|
||||
|
|
Loading…
Reference in a new issue