mirror of
https://codeberg.org/anoncontributorxmr/mysu.git
synced 2024-11-25 00:42:32 +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_LEVIN_PORT = 0
|
||||||
private var DEFAULT_RPC_PORT = 0
|
private var DEFAULT_RPC_PORT = 0
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun fromString(nodeString: String?): Node? {
|
fun fromString(nodeString: String?): Node? {
|
||||||
return try {
|
return try {
|
||||||
Node(nodeString)
|
Node(nodeString)
|
||||||
|
|
|
@ -23,7 +23,6 @@ class Subaddress(
|
||||||
@JvmField val address: String,
|
@JvmField val address: String,
|
||||||
val label: String
|
val label: String
|
||||||
) : Comparable<Subaddress> {
|
) : Comparable<Subaddress> {
|
||||||
@JvmField
|
|
||||||
var amount: Long = 0
|
var amount: Long = 0
|
||||||
|
|
||||||
override fun compareTo(other: Subaddress): Int { // newer is <
|
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(",")
|
||||||
sb.append(xmrtoDestination)
|
sb.append(xmrtoDestination)
|
||||||
sb.append("}")
|
sb.append("}")
|
||||||
if (note != null && !note!!.isEmpty()) sb.append(" ")
|
if (note != null && note?.isNotEmpty() == true) sb.append(" ")
|
||||||
}
|
}
|
||||||
sb.append(note)
|
sb.append(note)
|
||||||
return sb.toString()
|
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 -> {
|
sendUtxosButton.setOnClickListener(view1 -> {
|
||||||
ArrayList<String> selectedKeyImages = new ArrayList<>();
|
ArrayList<String> selectedKeyImages = new ArrayList<>();
|
||||||
for (CoinsInfo coinsInfo : adapter.selectedUtxos.values()) {
|
for (CoinsInfo coinsInfo : adapter.selectedUtxos.values()) {
|
||||||
selectedKeyImages.add(coinsInfo.keyImage);
|
selectedKeyImages.add(coinsInfo.getKeyImage());
|
||||||
}
|
}
|
||||||
SendBottomSheetDialog sendDialog = new SendBottomSheetDialog();
|
SendBottomSheetDialog sendDialog = new SendBottomSheetDialog();
|
||||||
sendDialog.listener = this;
|
sendDialog.listener = this;
|
||||||
|
@ -80,7 +80,7 @@ public class UtxosFragment extends Fragment implements CoinsInfoAdapter.CoinsInf
|
||||||
churnUtxosButton.setOnClickListener(view1 -> {
|
churnUtxosButton.setOnClickListener(view1 -> {
|
||||||
ArrayList<String> selectedKeyImages = new ArrayList<>();
|
ArrayList<String> selectedKeyImages = new ArrayList<>();
|
||||||
for (CoinsInfo coinsInfo : adapter.selectedUtxos.values()) {
|
for (CoinsInfo coinsInfo : adapter.selectedUtxos.values()) {
|
||||||
selectedKeyImages.add(coinsInfo.keyImage);
|
selectedKeyImages.add(coinsInfo.getKeyImage());
|
||||||
}
|
}
|
||||||
SendBottomSheetDialog sendDialog = new SendBottomSheetDialog();
|
SendBottomSheetDialog sendDialog = new SendBottomSheetDialog();
|
||||||
sendDialog.listener = this;
|
sendDialog.listener = this;
|
||||||
|
@ -101,7 +101,7 @@ public class UtxosFragment extends Fragment implements CoinsInfoAdapter.CoinsInf
|
||||||
HashMap<String, CoinsInfo> filteredUtxos = new HashMap<>();
|
HashMap<String, CoinsInfo> filteredUtxos = new HashMap<>();
|
||||||
for (CoinsInfo coinsInfo : utxos) {
|
for (CoinsInfo coinsInfo : utxos) {
|
||||||
if (!coinsInfo.isSpent()) {
|
if (!coinsInfo.isSpent()) {
|
||||||
filteredUtxos.put(coinsInfo.pubKey, coinsInfo);
|
filteredUtxos.put(coinsInfo.getPubKey(), coinsInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (filteredUtxos.isEmpty()) {
|
if (filteredUtxos.isEmpty()) {
|
||||||
|
|
|
@ -20,28 +20,15 @@ import android.os.Parcelable
|
||||||
import android.os.Parcelable.Creator
|
import android.os.Parcelable.Creator
|
||||||
|
|
||||||
class CoinsInfo : Parcelable, Comparable<CoinsInfo> {
|
class CoinsInfo : Parcelable, Comparable<CoinsInfo> {
|
||||||
@JvmField
|
|
||||||
var globalOutputIndex: Long
|
var globalOutputIndex: Long
|
||||||
var isSpent = false
|
var isSpent = false
|
||||||
|
|
||||||
@JvmField
|
|
||||||
var keyImage: String? = null
|
var keyImage: String? = null
|
||||||
|
|
||||||
@JvmField
|
|
||||||
var amount: Long = 0
|
var amount: Long = 0
|
||||||
|
|
||||||
@JvmField
|
|
||||||
var hash: String? = null
|
var hash: String? = null
|
||||||
|
|
||||||
@JvmField
|
|
||||||
var pubKey: String? = null
|
var pubKey: String? = null
|
||||||
var isUnlocked = false
|
var isUnlocked = false
|
||||||
|
|
||||||
@JvmField
|
|
||||||
var localOutputIndex: Long = 0
|
var localOutputIndex: Long = 0
|
||||||
var isFrozen = false
|
var isFrozen = false
|
||||||
|
|
||||||
@JvmField
|
|
||||||
var address: String? = null
|
var address: String? = null
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -68,7 +55,7 @@ class CoinsInfo : Parcelable, Comparable<CoinsInfo> {
|
||||||
this.address = address
|
this.address = address
|
||||||
}
|
}
|
||||||
|
|
||||||
protected constructor(`in`: Parcel) {
|
private constructor(`in`: Parcel) {
|
||||||
globalOutputIndex = `in`.readLong()
|
globalOutputIndex = `in`.readLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,15 +67,15 @@ class CoinsInfo : Parcelable, Comparable<CoinsInfo> {
|
||||||
parcel.writeLong(globalOutputIndex)
|
parcel.writeLong(globalOutputIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun compareTo(another: CoinsInfo): Int {
|
override fun compareTo(other: CoinsInfo): Int {
|
||||||
val b1 = amount
|
val b1 = amount
|
||||||
val b2 = another.amount
|
val b2 = other.amount
|
||||||
return if (b1 > b2) {
|
return if (b1 > b2) {
|
||||||
-1
|
-1
|
||||||
} else if (b1 < b2) {
|
} else if (b1 < b2) {
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
hash!!.compareTo(another.hash!!)
|
other.hash?.let { hash?.compareTo(it) } ?: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package net.mynero.wallet.model
|
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);
|
NetworkType_Mainnet(0), NetworkType_Testnet(1), NetworkType_Stagenet(2);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
|
||||||
fun fromInteger(n: Int): NetworkType? {
|
fun fromInteger(n: Int): NetworkType? {
|
||||||
when (n) {
|
when (n) {
|
||||||
0 -> return NetworkType_Mainnet
|
0 -> return NetworkType_Mainnet
|
||||||
|
|
|
@ -38,7 +38,7 @@ class PendingTransaction internal constructor(var handle: Long) {
|
||||||
Status_Ok, Status_Error, Status_Critical
|
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);
|
Priority_Default(0), Priority_Low(1), Priority_Medium(2), Priority_High(3), Priority_Last(4);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -19,6 +19,8 @@ package net.mynero.wallet.model;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import net.mynero.wallet.data.Subaddress;
|
import net.mynero.wallet.data.Subaddress;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -100,7 +102,7 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
|
||||||
addressIndex = in.readInt();
|
addressIndex = in.readInt();
|
||||||
confirmations = in.readLong();
|
confirmations = in.readLong();
|
||||||
subaddressLabel = in.readString();
|
subaddressLabel = in.readString();
|
||||||
transfers = in.readArrayList(Transfer.class.getClassLoader());
|
in.readList(transfers, Transfer.class.getClassLoader());
|
||||||
txKey = in.readString();
|
txKey = in.readString();
|
||||||
notes = in.readString();
|
notes = in.readString();
|
||||||
address = in.readString();
|
address = in.readString();
|
||||||
|
@ -117,6 +119,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
|
||||||
return subaddressLabel;
|
return subaddressLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return direction + "@" + blockheight + " " + amount;
|
return direction + "@" + blockheight + " " + amount;
|
||||||
}
|
}
|
||||||
|
@ -171,13 +175,11 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Direction fromInteger(int n) {
|
public static Direction fromInteger(int n) {
|
||||||
switch (n) {
|
return switch (n) {
|
||||||
case 0:
|
case 0 -> Direction_In;
|
||||||
return Direction_In;
|
case 1 -> Direction_Out;
|
||||||
case 1:
|
default -> null;
|
||||||
return Direction_Out;
|
};
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getValue() {
|
public int getValue() {
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
package net.mynero.wallet.model
|
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 android.util.Pair
|
||||||
import net.mynero.wallet.data.Subaddress
|
import net.mynero.wallet.data.Subaddress
|
||||||
import net.mynero.wallet.model.NetworkType.Companion.fromInteger
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
@ -129,7 +128,7 @@ class Wallet {
|
||||||
|
|
||||||
external fun getPath(): String?
|
external fun getPath(): String?
|
||||||
val networkType: NetworkType?
|
val networkType: NetworkType?
|
||||||
get() = fromInteger(nettype())
|
get() = NetworkType.fromInteger(nettype())
|
||||||
|
|
||||||
external fun nettype(): Int
|
external fun nettype(): Int
|
||||||
|
|
||||||
|
@ -255,7 +254,7 @@ class Wallet {
|
||||||
destinations: List<Pair<String, Long>>,
|
destinations: List<Pair<String, Long>>,
|
||||||
priority: PendingTransaction.Priority
|
priority: PendingTransaction.Priority
|
||||||
): Long {
|
): Long {
|
||||||
val _priority = priority.value
|
val _priority = priority.ordinal
|
||||||
return estimateTransactionFee(destinations, _priority)
|
return estimateTransactionFee(destinations, _priority)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +269,7 @@ class Wallet {
|
||||||
keyImages: ArrayList<String>
|
keyImages: ArrayList<String>
|
||||||
): PendingTransaction? {
|
): PendingTransaction? {
|
||||||
disposePendingTransaction()
|
disposePendingTransaction()
|
||||||
val _priority = priority.value
|
val _priority = priority.ordinal
|
||||||
val txHandle = createSweepTransaction(dstAddr, "", 0, _priority, accountIndex, keyImages)
|
val txHandle = createSweepTransaction(dstAddr, "", 0, _priority, accountIndex, keyImages)
|
||||||
pendingTransaction = PendingTransaction(txHandle)
|
pendingTransaction = PendingTransaction(txHandle)
|
||||||
return pendingTransaction
|
return pendingTransaction
|
||||||
|
@ -282,7 +281,7 @@ class Wallet {
|
||||||
keyImages: ArrayList<String>
|
keyImages: ArrayList<String>
|
||||||
): PendingTransaction? {
|
): PendingTransaction? {
|
||||||
disposePendingTransaction()
|
disposePendingTransaction()
|
||||||
val _priority = priority.value
|
val _priority = priority.ordinal
|
||||||
val destinations = ArrayList<String>()
|
val destinations = ArrayList<String>()
|
||||||
val amounts = LongArray(outputs.size)
|
val amounts = LongArray(outputs.size)
|
||||||
for (i in outputs.indices) {
|
for (i in outputs.indices) {
|
||||||
|
|
|
@ -23,7 +23,6 @@ import java.util.Calendar
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class WalletManager {
|
class WalletManager {
|
||||||
@JvmField
|
|
||||||
val networkType = NetworkType.NetworkType_Mainnet
|
val networkType = NetworkType.NetworkType_Mainnet
|
||||||
var wallet: Wallet? = null
|
var wallet: Wallet? = null
|
||||||
private set
|
private set
|
||||||
|
@ -209,7 +208,7 @@ class WalletManager {
|
||||||
subaddressLookahead: String
|
subaddressLookahead: String
|
||||||
): Long
|
): Long
|
||||||
|
|
||||||
external fun closeJ(wallet: Wallet?): Boolean
|
private external fun closeJ(wallet: Wallet?): Boolean
|
||||||
fun close(wallet: Wallet): Boolean {
|
fun close(wallet: Wallet): Boolean {
|
||||||
unmanageWallet(wallet)
|
unmanageWallet(wallet)
|
||||||
val closed = closeJ(wallet)
|
val closed = closeJ(wallet)
|
||||||
|
@ -225,28 +224,28 @@ class WalletManager {
|
||||||
return walletExists(aFile.absolutePath)
|
return walletExists(aFile.absolutePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
external fun walletExists(path: String?): Boolean
|
private external fun walletExists(path: String?): Boolean
|
||||||
external fun verifyWalletPassword(
|
external fun verifyWalletPassword(
|
||||||
keys_file_name: String?,
|
keysFileName: String?,
|
||||||
password: String?,
|
password: String?,
|
||||||
watch_only: Boolean
|
watchOnly: Boolean
|
||||||
): Boolean
|
): Boolean
|
||||||
|
|
||||||
fun verifyWalletPasswordOnly(keys_file_name: String, password: String): Boolean {
|
fun verifyWalletPasswordOnly(keysFileName: String, password: String): Boolean {
|
||||||
return queryWalletDeviceJ(keys_file_name, password) >= 0
|
return queryWalletDeviceJ(keysFileName, password) >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun queryWalletDevice(keys_file_name: String, password: String): Wallet.Device {
|
fun queryWalletDevice(keysFileName: String, password: String): Wallet.Device {
|
||||||
val device = queryWalletDeviceJ(keys_file_name, password)
|
val device = queryWalletDeviceJ(keysFileName, password)
|
||||||
return Wallet.Device.values()[device + 1] // mapping is monero+1=android
|
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> {
|
fun findWallets(path: File): List<WalletInfo> {
|
||||||
val wallets: MutableList<WalletInfo> = ArrayList()
|
val wallets: MutableList<WalletInfo> = ArrayList()
|
||||||
Timber.d("Scanning: %s", path.absolutePath)
|
Timber.d("Scanning: %s", path.absolutePath)
|
||||||
val found =
|
val found =
|
||||||
path.listFiles { dir, filename -> filename.endsWith(".keys") } ?: return emptyList()
|
path.listFiles { _, filename -> filename.endsWith(".keys") } ?: return emptyList()
|
||||||
for (i in found.indices) {
|
for (i in found.indices) {
|
||||||
val filename = found[i].name
|
val filename = found[i].name
|
||||||
val f = File(
|
val f = File(
|
||||||
|
@ -291,8 +290,8 @@ class WalletManager {
|
||||||
|
|
||||||
external fun startMining(
|
external fun startMining(
|
||||||
address: String?,
|
address: String?,
|
||||||
background_mining: Boolean,
|
backgroundMining: Boolean,
|
||||||
ignore_battery: Boolean
|
ignoreBattery: Boolean
|
||||||
): Boolean
|
): Boolean
|
||||||
|
|
||||||
external fun stopMining(): Boolean
|
external fun stopMining(): Boolean
|
||||||
|
@ -320,7 +319,6 @@ class WalletManager {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
//TODO: maybe put these in an enum like in monero core - but why?
|
//TODO: maybe put these in an enum like in monero core - but why?
|
||||||
@JvmField
|
|
||||||
var LOGLEVEL_SILENT = -1
|
var LOGLEVEL_SILENT = -1
|
||||||
var LOGLEVEL_WARN = 0
|
var LOGLEVEL_WARN = 0
|
||||||
var LOGLEVEL_INFO = 1
|
var LOGLEVEL_INFO = 1
|
||||||
|
@ -349,7 +347,6 @@ class WalletManager {
|
||||||
NetworkType.NetworkType_Testnet -> "9A-"
|
NetworkType.NetworkType_Testnet -> "9A-"
|
||||||
NetworkType.NetworkType_Mainnet -> "4-"
|
NetworkType.NetworkType_Mainnet -> "4-"
|
||||||
NetworkType.NetworkType_Stagenet -> "5-"
|
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) {
|
class BlockchainService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
||||||
private val _currentHeight = MutableLiveData(0L)
|
private val _currentHeight = MutableLiveData(0L)
|
||||||
private val _connectionStatus = MutableLiveData(ConnectionStatus.ConnectionStatus_Disconnected)
|
private val _connectionStatus = MutableLiveData(ConnectionStatus.ConnectionStatus_Disconnected)
|
||||||
|
|
||||||
@JvmField
|
|
||||||
var height: LiveData<Long> = _currentHeight
|
var height: LiveData<Long> = _currentHeight
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var connectionStatus: LiveData<ConnectionStatus> = _connectionStatus
|
var connectionStatus: LiveData<ConnectionStatus> = _connectionStatus
|
||||||
var daemonHeight: Long = 0
|
var daemonHeight: Long = 0
|
||||||
|
@ -22,7 +19,7 @@ class BlockchainService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
||||||
lastDaemonHeightUpdateTimeMs = t
|
lastDaemonHeightUpdateTimeMs = t
|
||||||
} else {
|
} else {
|
||||||
if (t - lastDaemonHeightUpdateTimeMs > 120000) {
|
if (t - lastDaemonHeightUpdateTimeMs > 120000) {
|
||||||
field = WalletManager.instance!!.wallet!!.getDaemonBlockChainHeight()
|
field = WalletManager.instance?.wallet?.getDaemonBlockChainHeight() ?: return
|
||||||
lastDaemonHeightUpdateTimeMs = t
|
lastDaemonHeightUpdateTimeMs = t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,8 +34,8 @@ class BlockchainService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
||||||
_currentHeight.postValue(currentHeight)
|
_currentHeight.postValue(currentHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentHeight: Long
|
private val currentHeight: Long
|
||||||
get() = WalletManager.instance!!.wallet!!.getBlockChainHeight()
|
get() = WalletManager.instance?.wallet?.getBlockChainHeight() ?: -1
|
||||||
|
|
||||||
fun setConnectionStatus(status: ConnectionStatus) {
|
fun setConnectionStatus(status: ConnectionStatus) {
|
||||||
_connectionStatus.postValue(status)
|
_connectionStatus.postValue(status)
|
||||||
|
|
|
@ -20,7 +20,7 @@ class HistoryService(thread: MoneroHandlerThread) : ServiceBase(thread) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getHistory(): List<TransactionInfo> {
|
private fun getHistory(): List<TransactionInfo> {
|
||||||
return WalletManager.instance!!.wallet!!.history!!.all
|
return WalletManager.instance?.wallet?.history?.all ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -32,10 +32,10 @@ import java.security.SecureRandom
|
||||||
* used to create handler classes. Note that start() must still be called.
|
* used to create handler classes. Note that start() must still be called.
|
||||||
* The started Thread has a stck size of STACK_SIZE (=5MB)
|
* 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 {
|
Thread(null, null, name, THREAD_STACK_SIZE), WalletListener {
|
||||||
private val wallet: Wallet
|
private val wallet: Wallet
|
||||||
var triesLeft = 5
|
private var triesLeft = 5
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.wallet = wallet
|
this.wallet = wallet
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
package net.mynero.wallet.service
|
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
|
frozenCoins = frozenCoinsCopy
|
||||||
saveFrozenCoins()
|
saveFrozenCoins()
|
||||||
refreshUtxos()
|
refreshUtxos()
|
||||||
BalanceService.instance!!.refreshBalance()
|
BalanceService.instance?.refreshBalance()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isCoinFrozen(coinsInfo: CoinsInfo): Boolean {
|
fun isCoinFrozen(coinsInfo: CoinsInfo): Boolean {
|
||||||
|
@ -65,7 +65,7 @@ class UTXOService(thread: MoneroHandlerThread?) : ServiceBase(thread) {
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
private fun loadFrozenCoins() {
|
private fun loadFrozenCoins() {
|
||||||
val prefService = PrefService.instance
|
val prefService = PrefService.instance
|
||||||
val frozenCoinsArrayString = prefService!!.getString(Constants.PREF_FROZEN_COINS, "[]")
|
val frozenCoinsArrayString = prefService?.getString(Constants.PREF_FROZEN_COINS, "[]")
|
||||||
val frozenCoinsArray = JSONArray(frozenCoinsArrayString)
|
val frozenCoinsArray = JSONArray(frozenCoinsArrayString)
|
||||||
for (i in 0 until frozenCoinsArray.length()) {
|
for (i in 0 until frozenCoinsArray.length()) {
|
||||||
val pubKey = frozenCoinsArray.getString(i)
|
val pubKey = frozenCoinsArray.getString(i)
|
||||||
|
@ -81,8 +81,7 @@ class UTXOService(thread: MoneroHandlerThread?) : ServiceBase(thread) {
|
||||||
for (pubKey in frozenCoinsCopy) {
|
for (pubKey in frozenCoinsCopy) {
|
||||||
jsonArray.put(pubKey)
|
jsonArray.put(pubKey)
|
||||||
}
|
}
|
||||||
prefService!!.edit()!!
|
prefService?.edit()?.putString(Constants.PREF_FROZEN_COINS, jsonArray.toString())?.apply()
|
||||||
.putString(Constants.PREF_FROZEN_COINS, jsonArray.toString()).apply()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -91,7 +90,7 @@ class UTXOService(thread: MoneroHandlerThread?) : ServiceBase(thread) {
|
||||||
sendAll: Boolean,
|
sendAll: Boolean,
|
||||||
feePriority: PendingTransaction.Priority
|
feePriority: PendingTransaction.Priority
|
||||||
): ArrayList<String> {
|
): ArrayList<String> {
|
||||||
val basicFeeEstimate = calculateBasicFee(amount, feePriority)
|
val basicFeeEstimate = calculateBasicFee(amount, feePriority) ?: return arrayListOf()
|
||||||
val amountWithBasicFee = amount + basicFeeEstimate
|
val amountWithBasicFee = amount + basicFeeEstimate
|
||||||
val selectedUtxos = ArrayList<String>()
|
val selectedUtxos = ArrayList<String>()
|
||||||
val seenTxs = ArrayList<String>()
|
val seenTxs = ArrayList<String>()
|
||||||
|
@ -125,7 +124,7 @@ class UTXOService(thread: MoneroHandlerThread?) : ServiceBase(thread) {
|
||||||
return selectedUtxos
|
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>>()
|
val destinations = ArrayList<Pair<String, Long>>()
|
||||||
destinations.add(
|
destinations.add(
|
||||||
Pair(
|
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
|
// 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 {
|
companion object {
|
||||||
|
|
|
@ -19,7 +19,6 @@ object Constants {
|
||||||
const val NAV_ARG_TXINFO = "nav_arg_txinfo"
|
const val NAV_ARG_TXINFO = "nav_arg_txinfo"
|
||||||
const val STREET_MODE_BALANCE = "#.############"
|
const val STREET_MODE_BALANCE = "#.############"
|
||||||
|
|
||||||
@JvmField
|
|
||||||
val DONATION_ADDRESSES = arrayOf(
|
val DONATION_ADDRESSES = arrayOf(
|
||||||
"87BqQYkugEzh6Tuyotm2uc3DzJzKM6MuZaC161e6u1TsQxxPmXVPHpdNRyK47JY4d1hhbe25YVz4e9vTXCLDxvHkRXEAeBC", // primary Mysu Donation address
|
"87BqQYkugEzh6Tuyotm2uc3DzJzKM6MuZaC161e6u1TsQxxPmXVPHpdNRyK47JY4d1hhbe25YVz4e9vTXCLDxvHkRXEAeBC", // primary Mysu Donation address
|
||||||
"89QoPxs4cQSGbJrJddwzV3Ca7s2gVYHE1Xd1hGZafuVJVyNKt2LCQhxUdBF57PemxQiX3dmGUZLRRAzfeYyh9pq3GiWsDVo", // second Mysu Donation address
|
"89QoPxs4cQSGbJrJddwzV3Ca7s2gVYHE1Xd1hGZafuVJVyNKt2LCQhxUdBF57PemxQiX3dmGUZLRRAzfeYyh9pq3GiWsDVo", // second Mysu Donation address
|
||||||
|
|
Loading…
Reference in a new issue