Add user/pass to node settings, cleanup node parsing, fix quite a few bugs

This commit is contained in:
pokkst 2022-12-15 11:01:23 -06:00
parent ecdd6d12d3
commit 480cbd11f1
No known key found for this signature in database
GPG Key ID: 90C2ED85E67A50FF
13 changed files with 330 additions and 88 deletions

View File

@ -109,7 +109,6 @@ public class NodeSelectionAdapter extends RecyclerView.Adapter<NodeSelectionAdap
TextView nodeAddressTextView = itemView.findViewById(R.id.node_uri_textview);
nodeNameTextView.setText(node.getName());
nodeAddressTextView.setText(node.getAddress());
ImageView nodeAnonymityNetworkImageView = itemView.findViewById(R.id.anonymity_network_imageview);
if(node.isOnion()) {
@ -125,10 +124,10 @@ public class NodeSelectionAdapter extends RecyclerView.Adapter<NodeSelectionAdap
itemView.setOnLongClickListener(view -> {
if(match) {
Toast.makeText(itemView.getContext(), itemView.getResources().getString(R.string.cant_edit_current_node), Toast.LENGTH_SHORT).show();
return false;
return true;
} else if(isDefaultNode(node)) {
Toast.makeText(itemView.getContext(), itemView.getResources().getString(R.string.cant_edit_default_nodes), Toast.LENGTH_SHORT).show();
return false;
return true;
} else {
return listener.onSelectEditNode(node);
}

View File

@ -223,7 +223,9 @@ public class Node {
return (host.equals(anotherNode.host)
&& (getAddress().equals(anotherNode.getAddress()))
&& (rpcPort == anotherNode.rpcPort)
&& (networkType == anotherNode.networkType));
&& (networkType == anotherNode.networkType))
&& (username.equals(anotherNode.getUsername()))
&& (password.equals(anotherNode.getPassword()));
}
public boolean isOnion() {

View File

@ -2,6 +2,8 @@ package net.mynero.wallet.fragment.dialog;
import android.content.Context;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.View;
@ -9,6 +11,7 @@ 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;
@ -16,6 +19,7 @@ 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;
@ -37,67 +41,89 @@ public class AddNodeBottomSheetDialog extends BottomSheetDialogFragment {
Button addNodeButton = view.findViewById(R.id.add_node_button);
EditText addressEditText = view.findViewById(R.id.address_edittext);
EditText nodeNameEditText = view.findViewById(R.id.node_name_edittext);
ImageButton pasteAddressImageButton = view.findViewById(R.id.paste_address_imagebutton);
pasteAddressImageButton.setOnClickListener(view1 -> {
Context ctx = getContext();
if (ctx != null) {
addressEditText.setText(Helper.getClipBoardText(ctx));
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 node = addressEditText.getText().toString();
String name = nodeNameEditText.getText().toString();
String user = usernameEditText.getText().toString();
String pass = passwordEditText.getText().toString();
try {
String auth = "";
if(!user.isEmpty() && !pass.isEmpty()) {
auth = user + ":" + pass + "@";
} else if(!user.isEmpty()) {
Toast.makeText(getContext(), "Enter password", Toast.LENGTH_SHORT).show();
return;
}
if (node.contains(":") && !name.isEmpty()) {
String[] nodeParts = node.split(":");
if (nodeParts.length == 2) {
String address = nodeParts[0];
int port = Integer.parseInt(nodeParts[1]);
String newNodeString = address + ":" + port + "/mainnet/" + name;
boolean validAddress = Patterns.IP_ADDRESS.matcher(address).matches() || Patterns.DOMAIN_NAME.matcher(address).matches() || address.endsWith(".b32.i2p");
if (validAddress) {
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++) {
String nodeString = jsonArray.getString(i);
if (nodeString.equals(newNodeString))
exists = true;
}
StringBuilder nodeStringBuilder = new StringBuilder();
nodeStringBuilder.append(auth);
if (!exists) {
jsonArray.put(newNodeString);
if(!name.isEmpty()) {
if(!node.isEmpty()) {
if (node.contains(":")) {
String[] nodeParts = node.split(":");
if (nodeParts.length == 2) {
String address = nodeParts[0];
int port = Integer.parseInt(nodeParts[1]);
nodeStringBuilder.append(address).append(":").append(port);
}
PrefService.getInstance().edit().putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString()).apply();
if (listener != null) {
listener.onNodeAdded();
}
dismiss();
} else {
nodeStringBuilder.append(node);
}
nodeStringBuilder.append("/mainnet/").append(name);
} else {
Toast.makeText(getContext(), "Enter node address", Toast.LENGTH_SHORT).show();
return;
}
} else if(node.endsWith(".b32.i2p")) {
String newNodeString = node + "/mainnet/" + name;
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++) {
String nodeString = jsonArray.getString(i);
if (nodeString.equals(newNodeString))
exists = true;
}
} else {
Toast.makeText(getContext(), "Enter node name", Toast.LENGTH_SHORT).show();
return;
}
if (!exists) {
jsonArray.put(newNodeString);
}
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++) {
String nodeString = jsonArray.getString(i);
if (nodeString.equals(nodeStringBuilder.toString()))
exists = true;
}
PrefService.getInstance().edit().putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString()).apply();
if (listener != null) {
listener.onNodeAdded();
}
dismiss();
if (!exists) {
jsonArray.put(nodeStringBuilder.toString());
} else {
Toast.makeText(getContext(), "Node already exists", Toast.LENGTH_SHORT).show();
return;
}
PrefService.getInstance().edit().putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString()).apply();
if (listener != null) {
listener.onNodeAdded();
}
} catch (NumberFormatException | JSONException e) {
e.printStackTrace();
@ -105,6 +131,16 @@ public class AddNodeBottomSheetDialog extends BottomSheetDialogFragment {
});
}
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();
}

View File

@ -2,6 +2,8 @@ package net.mynero.wallet.fragment.dialog;
import android.content.Context;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.View;
@ -9,6 +11,7 @@ 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;
@ -40,18 +43,40 @@ public class EditNodeBottomSheetDialog extends BottomSheetDialogFragment {
Button doneEditingButton = view.findViewById(R.id.done_editing_button);
EditText addressEditText = view.findViewById(R.id.address_edittext);
EditText nodeNameEditText = view.findViewById(R.id.node_name_edittext);
ImageButton pasteAddressImageButton = view.findViewById(R.id.paste_address_imagebutton);
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);
Node node = Node.fromString(nodeString);
addressEditText.setText(node.getAddress());
nodeNameEditText.setText(node.getName());
usernameEditText.setText(node.getUsername());
if(!node.getPassword().isEmpty()) {
passwordEditText.setText(node.getPassword());
passwordEditText.setVisibility(View.VISIBLE);
pastePasswordImageButton.setVisibility(View.VISIBLE);
}
pasteAddressImageButton.setOnClickListener(view1 -> {
Context ctx = getContext();
if (ctx != null) {
addressEditText.setText(Helper.getClipBoardText(ctx));
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.fromString(nodeString));
dismiss();
@ -59,19 +84,57 @@ public class EditNodeBottomSheetDialog extends BottomSheetDialogFragment {
doneEditingButton.setOnClickListener(view1 -> {
String nodeAddress = addressEditText.getText().toString();
String nodeName = nodeNameEditText.getText().toString();
if (nodeAddress.contains(":") && !nodeName.isEmpty()) {
String[] nodeParts = nodeAddress.split(":");
if (nodeParts.length == 2) {
String address = nodeParts[0];
int port = Integer.parseInt(nodeParts[1]);
String newNodeString = address + ":" + port + "/mainnet/" + nodeName;
listener.onNodeEdited(Node.fromString(nodeString), Node.fromString(newNodeString));
String user = usernameEditText.getText().toString();
String pass = passwordEditText.getText().toString();
String auth = "";
if(!user.isEmpty() && !pass.isEmpty()) {
auth = user + ":" + pass + "@";
} else if(!user.isEmpty()) {
Toast.makeText(getContext(), "Enter password", Toast.LENGTH_SHORT).show();
return;
}
StringBuilder nodeStringBuilder = new StringBuilder();
nodeStringBuilder.append(auth);
if(!nodeName.isEmpty()) {
if(!nodeAddress.isEmpty()) {
if (nodeAddress.contains(":")) {
String[] nodeParts = nodeAddress.split(":");
if (nodeParts.length == 2) {
String address = nodeParts[0];
int port = Integer.parseInt(nodeParts[1]);
nodeStringBuilder.append(address).append(":").append(port);
}
} else {
nodeStringBuilder.append(nodeAddress);
}
nodeStringBuilder.append("/mainnet/").append(nodeName);
listener.onNodeEdited(Node.fromString(nodeString), Node.fromString(nodeStringBuilder.toString()));
} else {
Toast.makeText(getContext(), "Enter node address", Toast.LENGTH_SHORT).show();
return;
}
} else {
Toast.makeText(getContext(), "Enter node name", Toast.LENGTH_SHORT).show();
return;
}
dismiss();
});
}
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);

View File

@ -5,10 +5,12 @@ import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import net.mynero.wallet.data.Subaddress;
import net.mynero.wallet.model.Wallet;
import net.mynero.wallet.model.WalletManager;
import net.mynero.wallet.service.AddressService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ReceiveViewModel extends ViewModel {
@ -23,12 +25,13 @@ public class ReceiveViewModel extends ViewModel {
}
private List<Subaddress> getSubaddresses() {
Wallet wallet = WalletManager.getInstance().getWallet();
ArrayList<Subaddress> subaddresses = new ArrayList<>();
int addressesSize = AddressService.getInstance().getLatestAddressIndex();
for(int i = 0; i < addressesSize; i++) {
subaddresses.add(WalletManager.getInstance().getWallet().getSubaddressObject(i));
for(int i = addressesSize - 1; i >= 0; i--) {
subaddresses.add(wallet.getSubaddressObject(i));
}
return subaddresses;
return Collections.unmodifiableList(subaddresses);
}
public void getFreshSubaddress() {

View File

@ -90,9 +90,9 @@ public class MoneroHandlerThread extends Thread implements WalletListener {
@Override
public void newBlock(long height) {
if(isEveryNthBlock(height, 100)) { // refresh services every 100 blocks downloaded
refresh(false, height);
} else if(isEveryNthBlock(height, 2160)) { // save wallet every 2160 blocks (~3 days)
if(isEveryNthBlock(height, 5)) { // refresh services every 5 blocks downloaded
refresh(height);
} else if(isEveryNthBlock(height, 720)) { // save wallet every 720 blocks (~1 day), we also save upon finishing sync (in refreshed())
wallet.store();
}
BlockchainService.getInstance().setDaemonHeight(wallet.isSynchronized() ? height : 0);
@ -100,7 +100,7 @@ public class MoneroHandlerThread extends Thread implements WalletListener {
@Override
public void updated() {
refresh(false, BlockchainService.GETHEIGHT_FETCH);
refresh(BlockchainService.GETHEIGHT_FETCH);
}
@Override
@ -118,17 +118,15 @@ public class MoneroHandlerThread extends Thread implements WalletListener {
BlockchainService.getInstance().setDaemonHeight(height);
wallet.setSynchronized();
wallet.store();
refresh(true, height);
refresh(height);
}
BlockchainService.getInstance().setConnectionStatus(status);
}
private void refresh(boolean refreshCoins, long height) {
private void refresh(long height) {
wallet.refreshHistory();
if (refreshCoins) {
wallet.refreshCoins();
}
wallet.refreshCoins();
listener.onRefresh(height);
}

View File

@ -31,7 +31,7 @@
android:id="@+id/node_name_edittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:layout_marginBottom="16dp"
android:background="@drawable/edittext_bg"
android:ellipsize="middle"
android:hint="@string/node_name_hint"
@ -63,11 +63,12 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
android:background="@drawable/edittext_bg"
android:hint="@string/node_address_hint"
android:inputType="text"
android:digits="-QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890.:"
app:layout_constraintBottom_toTopOf="@id/add_node_button"
app:layout_constraintBottom_toTopOf="@id/username_edittext"
app:layout_constraintEnd_toStartOf="@id/paste_address_imagebutton"
app:layout_constraintStart_toStartOf="parent" />
@ -75,11 +76,75 @@
android:id="@+id/add_node_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginTop="16dp"
android:background="@drawable/button_bg"
android:text="@string/add_node"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/address_edittext" />
app:layout_constraintTop_toBottomOf="@id/password_edittext" />
<EditText
android:id="@+id/username_edittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
android:background="@drawable/edittext_bg"
android:hint="@string/node_username_hint"
android:inputType="text"
android:digits="-QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890.:"
app:layout_constraintBottom_toTopOf="@id/password_edittext"
app:layout_constraintEnd_toStartOf="@id/paste_username_imagebutton"
app:layout_constraintStart_toStartOf="parent" />
<ImageButton
android:id="@+id/paste_username_imagebutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:background="@android:color/transparent"
android:minWidth="24dp"
android:minHeight="24dp"
android:padding="8dp"
android:src="@drawable/ic_content_paste_24dp"
app:layout_constraintBottom_toBottomOf="@id/username_edittext"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/username_edittext"
app:layout_constraintTop_toTopOf="@id/username_edittext"
tools:ignore="SpeakableTextPresentCheck" />
<EditText
android:id="@+id/password_edittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:background="@drawable/edittext_bg"
android:hint="@string/node_password_hint"
android:inputType="textPassword"
android:visibility="gone"
android:digits="-QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890.:"
app:layout_constraintBottom_toTopOf="@id/add_node_button"
app:layout_constraintEnd_toStartOf="@id/paste_password_imagebutton"
app:layout_constraintStart_toStartOf="parent" />
<ImageButton
android:id="@+id/paste_password_imagebutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:background="@android:color/transparent"
android:minWidth="24dp"
android:minHeight="24dp"
android:padding="8dp"
android:visibility="gone"
android:src="@drawable/ic_content_paste_24dp"
app:layout_constraintBottom_toBottomOf="@id/password_edittext"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/password_edittext"
app:layout_constraintTop_toTopOf="@id/password_edittext"
tools:ignore="SpeakableTextPresentCheck" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@ -4,7 +4,9 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp">
android:layout_marginBottom="8dp"
android:paddingEnd="8dp"
android:paddingStart="8dp">
<TextView
android:id="@+id/address_item_address_textview"
android:layout_width="0dp"

View File

@ -31,7 +31,7 @@
android:id="@+id/node_name_edittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:layout_marginBottom="16dp"
android:background="@drawable/edittext_bg"
android:ellipsize="middle"
android:hint="@string/node_name_hint"
@ -67,32 +67,97 @@
android:hint="@string/node_address_hint"
android:inputType="text"
android:digits="-QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890.:"
app:layout_constraintBottom_toTopOf="@id/delete_node_button"
app:layout_constraintBottom_toTopOf="@id/username_edittext"
app:layout_constraintEnd_toStartOf="@id/paste_address_imagebutton"
app:layout_constraintStart_toStartOf="parent" />
<EditText
android:id="@+id/username_edittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginTop="16dp"
android:background="@drawable/edittext_bg"
android:hint="@string/node_username_hint"
android:inputType="text"
android:digits="-QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890.:"
app:layout_constraintTop_toBottomOf="@id/address_edittext"
app:layout_constraintEnd_toStartOf="@id/paste_username_imagebutton"
app:layout_constraintStart_toStartOf="parent" />
<ImageButton
android:id="@+id/paste_username_imagebutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:background="@android:color/transparent"
android:minWidth="24dp"
android:minHeight="24dp"
android:padding="8dp"
android:src="@drawable/ic_content_paste_24dp"
app:layout_constraintBottom_toBottomOf="@id/username_edittext"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/username_edittext"
app:layout_constraintTop_toTopOf="@id/username_edittext"
tools:ignore="SpeakableTextPresentCheck" />
<EditText
android:id="@+id/password_edittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginTop="16dp"
android:background="@drawable/edittext_bg"
android:hint="@string/node_password_hint"
android:inputType="textPassword"
android:visibility="gone"
android:digits="-QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890.:"
app:layout_constraintTop_toBottomOf="@id/username_edittext"
app:layout_constraintBottom_toTopOf="@id/delete_node_button"
app:layout_constraintEnd_toStartOf="@id/paste_password_imagebutton"
app:layout_constraintStart_toStartOf="parent" />
<ImageButton
android:id="@+id/paste_password_imagebutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:background="@android:color/transparent"
android:minWidth="24dp"
android:minHeight="24dp"
android:padding="8dp"
android:visibility="gone"
android:src="@drawable/ic_content_paste_24dp"
app:layout_constraintBottom_toBottomOf="@id/password_edittext"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/password_edittext"
app:layout_constraintTop_toTopOf="@id/password_edittext"
tools:ignore="SpeakableTextPresentCheck" />
<Button
android:id="@+id/delete_node_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="1dp"
android:background="@drawable/button_bg_left"
android:text="@string/delete"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/done_editing_button"
app:layout_constraintTop_toBottomOf="@id/address_edittext" />
app:layout_constraintTop_toBottomOf="@id/password_edittext" />
<Button
android:id="@+id/done_editing_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginTop="16dp"
android:layout_marginStart="1dp"
android:background="@drawable/button_bg_right"
android:text="@string/done"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/delete_node_button"
app:layout_constraintTop_toBottomOf="@id/address_edittext" />
app:layout_constraintTop_toBottomOf="@id/password_edittext" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@ -118,9 +118,7 @@
android:layout_height="0dp"
android:background="@drawable/round_bg"
android:layout_marginTop="64dp"
android:paddingTop="8dp"
android:clipToPadding="true"
android:paddingBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

View File

@ -30,6 +30,9 @@
android:layout_height="wrap_content"
android:background="@drawable/button_bg"
android:text="@string/add_node"
android:textSize="12sp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
app:layout_constraintStart_toEndOf="@id/nodes_textview"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/node_selection_recyclerview"

View File

@ -47,6 +47,8 @@
android:textSize="12sp"
android:textColor="#f00"
android:text="@string/wallet_seed_desc"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintBottom_toTopOf="@id/information_textview"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/wallet_seed_label_textview" />
@ -95,8 +97,10 @@
android:textSize="12sp"
android:textColor="#f80"
android:text="@string/wallet_viewkey_desc"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@id/copy_viewkey_imagebutton"
app:layout_constraintTop_toBottomOf="@id/wallet_viewkey_label_textview" />
<TextView
@ -121,6 +125,8 @@
android:padding="8dp"
android:src="@drawable/ic_content_copy_24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintBottom_toTopOf="@id/wallet_restore_height_label_textview"
app:layout_constraintStart_toEndOf="@id/viewkey_textview"
app:layout_constraintTop_toBottomOf="@id/wallet_viewkey_desc_textview" />
<TextView

View File

@ -78,6 +78,8 @@
<string name="nodes">Nodes</string>
<string name="node_name_hint">My Monero Node</string>
<string name="node_address_hint">127.0.0.1:18081</string>
<string name="node_username_hint">Username (optional)</string>
<string name="node_password_hint">Password</string>
<string name="transaction">Transaction</string>
<string name="amount_label">Amount</string>
<string name="tx_amount_no_prefix">%1$s XMR</string>