Add feature to edit/delete nodes from list

This commit is contained in:
pokkst 2022-10-07 16:45:11 -05:00
parent 2b92660dcb
commit 58329e5212
No known key found for this signature in database
GPG key ID: 90C2ED85E67A50FF
6 changed files with 265 additions and 2 deletions

View file

@ -20,6 +20,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.recyclerview.widget.RecyclerView;
@ -79,6 +80,7 @@ public class NodeSelectionAdapter extends RecyclerView.Adapter<NodeSelectionAdap
public interface NodeSelectionAdapterListener {
void onSelectNode(Node node);
boolean onSelectEditNode(Node node);
}
/**
@ -108,8 +110,29 @@ public class NodeSelectionAdapter extends RecyclerView.Adapter<NodeSelectionAdap
nodeNameTextView.setText(node.getName());
nodeAddressTextView.setText(node.getAddress());
itemView.setOnLongClickListener(view -> {
if(match) {
Toast.makeText(itemView.getContext(), itemView.getResources().getString(R.string.cant_edit_current_node), Toast.LENGTH_SHORT).show();
return false;
} else if(isDefaultNode(node)) {
Toast.makeText(itemView.getContext(), itemView.getResources().getString(R.string.cant_edit_default_nodes), Toast.LENGTH_SHORT).show();
return false;
} else {
return listener.onSelectEditNode(node);
}
});
itemView.setOnClickListener(view -> listener.onSelectNode(node));
}
private boolean isDefaultNode(Node currentNode) {
boolean isDefault = false;
for(DefaultNodes defaultNode : DefaultNodes.values()) {
if(currentNode.toNodeString().equals(defaultNode.getUri()))
isDefault = true;
}
return isDefault;
}
}
}

View file

@ -0,0 +1,79 @@
package net.mynero.wallet.fragment.dialog;
import android.content.Context;
import android.os.Bundle;
import android.util.Patterns;
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.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;
public class EditNodeBottomSheetDialog extends BottomSheetDialogFragment {
public EditNodeListener listener = null;
public String nodeString = "";
@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 nodeNameEditText = view.findViewById(R.id.node_name_edittext);
ImageButton pasteAddressImageButton = view.findViewById(R.id.paste_address_imagebutton);
Node node = Node.fromString(nodeString);
addressEditText.setText(node.getAddress());
nodeNameEditText.setText(node.getName());
pasteAddressImageButton.setOnClickListener(view1 -> {
Context ctx = getContext();
if (ctx != null) {
addressEditText.setText(Helper.getClipBoardText(ctx));
}
});
deleteNodeButton.setOnClickListener(view1 -> {
listener.onNodeDeleted(Node.fromString(nodeString));
dismiss();
});
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));
}
}
dismiss();
});
}
public interface EditNodeListener {
void onNodeDeleted(Node node);
void onNodeEdited(Node oldNode, Node newNode);
}
}

View file

@ -89,9 +89,18 @@ public class NodeSelectionBottomSheetDialog extends BottomSheetDialogFragment im
listener.onNodeSelected();
}
@Override
public boolean onSelectEditNode(Node node) {
if (listener != null) {
listener.onClickedEditNode(node.toNodeString());
}
dismiss();
return true;
}
public interface NodeSelectionDialogListener {
void onNodeSelected();
void onClickedEditNode(String nodeString);
void onClickedAddNode();
}
}

View file

@ -27,6 +27,7 @@ import net.mynero.wallet.R;
import net.mynero.wallet.data.DefaultNodes;
import net.mynero.wallet.data.Node;
import net.mynero.wallet.fragment.dialog.AddNodeBottomSheetDialog;
import net.mynero.wallet.fragment.dialog.EditNodeBottomSheetDialog;
import net.mynero.wallet.fragment.dialog.WalletKeysBottomSheetDialog;
import net.mynero.wallet.fragment.dialog.NodeSelectionBottomSheetDialog;
import net.mynero.wallet.fragment.dialog.PasswordBottomSheetDialog;
@ -38,7 +39,9 @@ import net.mynero.wallet.util.Constants;
import net.mynero.wallet.util.DayNightMode;
import net.mynero.wallet.util.NightmodeHelper;
public class SettingsFragment extends Fragment implements PasswordBottomSheetDialog.PasswordListener, NodeSelectionBottomSheetDialog.NodeSelectionDialogListener, AddNodeBottomSheetDialog.AddNodeListener {
import org.json.JSONArray;
public class SettingsFragment extends Fragment implements PasswordBottomSheetDialog.PasswordListener, NodeSelectionBottomSheetDialog.NodeSelectionDialogListener, AddNodeBottomSheetDialog.AddNodeListener, EditNodeBottomSheetDialog.EditNodeListener {
private SettingsViewModel mViewModel;
TextWatcher proxyAddressListener = new TextWatcher() {
@ -235,6 +238,14 @@ public class SettingsFragment extends Fragment implements PasswordBottomSheetDia
addNodeDialog.show(getActivity().getSupportFragmentManager(), "add_node_dialog");
}
@Override
public void onClickedEditNode(String nodeString) {
EditNodeBottomSheetDialog editNodeDialog = new EditNodeBottomSheetDialog();
editNodeDialog.listener = this;
editNodeDialog.nodeString = nodeString;
editNodeDialog.show(getActivity().getSupportFragmentManager(), "edit_node_dialog");
}
@Override
public void onNodeAdded() {
NodeSelectionBottomSheetDialog dialog = new NodeSelectionBottomSheetDialog();
@ -253,4 +264,43 @@ public class SettingsFragment extends Fragment implements PasswordBottomSheetDia
}
}
}
@Override
public void onNodeDeleted(Node node) {
try {
String nodesArray = PrefService.getInstance().getString(Constants.PREF_CUSTOM_NODES, "[]");
JSONArray jsonArray = new JSONArray(nodesArray);
for (int i = 0; i < jsonArray.length(); i++) {
String jsonNodeString = jsonArray.getString(i);
Node savedNode = Node.fromString(jsonNodeString);
if (savedNode.toNodeString().equals(node.toNodeString()))
jsonArray.remove(i);
}
saveNodesAndReopen(jsonArray);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onNodeEdited(Node oldNode, Node newNode) {
try {
String nodesArray = PrefService.getInstance().getString(Constants.PREF_CUSTOM_NODES, "[]");
JSONArray jsonArray = new JSONArray(nodesArray);
for (int i = 0; i < jsonArray.length(); i++) {
String jsonNodeString = jsonArray.getString(i);
Node savedNode = Node.fromString(jsonNodeString);
if (savedNode.toNodeString().equals(oldNode.toNodeString()))
jsonArray.put(i, newNode.toNodeString());
}
saveNodesAndReopen(jsonArray);
} catch (Exception e) {
e.printStackTrace();
}
}
private void saveNodesAndReopen(JSONArray jsonArray) {
PrefService.getInstance().edit().putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString()).apply();
onNodeAdded();
}
}

View file

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:fitsSystemWindows="true"
android:padding="24dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- CREATE LAYOUT -->
<TextView
android:id="@+id/send_monero_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:text="@string/edit_node"
android:textSize="32sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/node_name_edittext"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/node_name_edittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:background="@drawable/edittext_bg"
android:ellipsize="middle"
android:hint="@string/node_name_hint"
android:singleLine="true"
app:layout_constraintBottom_toTopOf="@id/address_edittext"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/send_monero_textview" />
<ImageButton
android:id="@+id/paste_address_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/address_edittext"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/address_edittext"
app:layout_constraintTop_toTopOf="@id/address_edittext"
tools:ignore="SpeakableTextPresentCheck" />
<EditText
android:id="@+id/address_edittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:background="@drawable/edittext_bg"
android:hint="@string/node_address_hint"
android:inputType="text"
android:digits="-QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890.:"
app:layout_constraintBottom_toTopOf="@id/delete_node_button"
app:layout_constraintEnd_toStartOf="@id/paste_address_imagebutton"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/delete_node_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
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" />
<Button
android:id="@+id/done_editing_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
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" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View file

@ -99,4 +99,9 @@
<string name="create_wallet_failed">Create wallet failed: %1$s</string>
<string name="wallet_keys_label">Wallet Keys</string>
<string name="max_subaddresses_warning">Max subaddresses. Please receive funds first.</string>
<string name="done">Done</string>
<string name="delete">Delete</string>
<string name="cant_edit_current_node">Cannot edit current node.</string>
<string name="cant_edit_default_nodes">Cannot edit default node.</string>
<string name="edit_node">Edit Node</string>
</resources>