mirror of
https://codeberg.org/anoncontributorxmr/mysu.git
synced 2024-12-22 21:17:47 +00:00
Add ability to add custom nodes
This commit is contained in:
parent
1e4a91d046
commit
4657132067
15 changed files with 702 additions and 179 deletions
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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 com.m2049r.xmrwallet.adapter;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.data.DefaultNodes;
|
||||
import com.m2049r.xmrwallet.data.Node;
|
||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||
import com.m2049r.xmrwallet.service.PrefService;
|
||||
import com.m2049r.xmrwallet.util.Constants;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NodeSelectionAdapter extends RecyclerView.Adapter<NodeSelectionAdapter.ViewHolder> {
|
||||
|
||||
private List<Node> localDataSet;
|
||||
private NodeSelectionAdapterListener listener = null;
|
||||
|
||||
/**
|
||||
* Initialize the dataset of the Adapter.
|
||||
*/
|
||||
public NodeSelectionAdapter(NodeSelectionAdapterListener listener) {
|
||||
this.listener = listener;
|
||||
this.localDataSet = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void submitList(List<Node> dataSet) {
|
||||
this.localDataSet = dataSet;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void updateSelectedNode() {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
// Create new views (invoked by the layout manager)
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
|
||||
// Create a new view, which defines the UI of the list item
|
||||
View view = LayoutInflater.from(viewGroup.getContext())
|
||||
.inflate(R.layout.node_selection_item, viewGroup, false);
|
||||
|
||||
return new ViewHolder(listener, view);
|
||||
}
|
||||
|
||||
// Replace the contents of a view (invoked by the layout manager)
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
|
||||
Node node = localDataSet.get(position);
|
||||
viewHolder.bind(node);
|
||||
}
|
||||
|
||||
// Return the size of your dataset (invoked by the layout manager)
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return localDataSet.size();
|
||||
}
|
||||
|
||||
public interface NodeSelectionAdapterListener {
|
||||
void onSelectNode(Node node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a reference to the type of views that you are using
|
||||
* (custom ViewHolder).
|
||||
*/
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private final NodeSelectionAdapterListener listener;
|
||||
public ViewHolder(NodeSelectionAdapterListener listener, View view) {
|
||||
super(view);
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void bind(Node node) {
|
||||
String currentNodeString = PrefService.getInstance().getString(Constants.PREF_NODE, DefaultNodes.XMRTW.getAddress());
|
||||
Node currentNode = Node.fromString(currentNodeString);
|
||||
boolean match = node.equals(currentNode);
|
||||
if(match) {
|
||||
itemView.setBackgroundColor(itemView.getResources().getColor(R.color.oled_colorSecondary));
|
||||
} else {
|
||||
itemView.setBackgroundColor(itemView.getResources().getColor(android.R.color.transparent));
|
||||
|
||||
}
|
||||
TextView nodeNameTextView = itemView.findViewById(R.id.node_name_textview);
|
||||
TextView nodeAddressTextView = itemView.findViewById(R.id.node_uri_textview);
|
||||
nodeNameTextView.setText(node.getName());
|
||||
nodeAddressTextView.setText(node.getAddress());
|
||||
|
||||
itemView.setOnClickListener(view -> listener.onSelectNode(node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,25 +16,34 @@
|
|||
|
||||
package com.m2049r.xmrwallet.data;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
// Nodes stolen from https://moneroworld.com/#nodes
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum DefaultNodes {
|
||||
MONERUJO("nodex.monerujo.io:18081"),
|
||||
XMRTO("node.xmr.to:18081"),
|
||||
SUPPORTXMR("node.supportxmr.com:18081"),
|
||||
HASHVAULT("nodes.hashvault.pro:18081"),
|
||||
MONEROWORLD("node.moneroworld.com:18089"),
|
||||
XMRTW("opennode.xmr-tw.org:18089"),
|
||||
MONERUJO("nodex.monerujo.io:18081/mainnet/monerujo"),
|
||||
SUPPORTXMR("node.supportxmr.com:18081/mainnet/SupportXMR"),
|
||||
HASHVAULT("nodes.hashvault.pro:18081/mainnet/Hashvault"),
|
||||
MONEROWORLD("node.moneroworld.com:18089/mainnet/MoneroWorld"),
|
||||
XMRTW("opennode.xmr-tw.org:18089/mainnet/XMRTW"),
|
||||
MONERUJO_ONION("monerujods7mbghwe6cobdr6ujih6c22zu5rl7zshmizz2udf7v7fsad.onion:18081/mainnet/monerujo.onion"),
|
||||
Criminales78("56wl7y2ebhamkkiza4b7il4mrzwtyvpdym7bm2bkg3jrei2je646k3qd.onion:18089/mainnet/Criminales78.onion"),
|
||||
xmrfail("mxcd4577fldb3ppzy7obmmhnu3tf57gbcbd4qhwr2kxyjj2qi3dnbfqd.onion:18081/mainnet/xmrfail.onion"),
|
||||
boldsuck("6dsdenp6vjkvqzy4wzsnzn6wixkdzihx3khiumyzieauxuxslmcaeiad.onion:18081/mainnet/boldsuck.onion"),
|
||||
SAMOURAI("446unwib5vc7pfbzflosy6m6vtyuhddnalr3hutyavwe4esfuu5g6ryd.onion:18089/mainnet/samourai.onion");
|
||||
|
||||
@Getter
|
||||
private final String uri;
|
||||
DefaultNodes(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return uri.split("/")[0];
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return uri.split("/")[2];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,6 @@ public class Node {
|
|||
@Getter
|
||||
@Setter
|
||||
private final boolean selected = false;
|
||||
Address hostAddress;
|
||||
@Getter
|
||||
@Setter
|
||||
int rpcPort = 0;
|
||||
|
@ -151,7 +150,6 @@ public class Node {
|
|||
// constructor used for created nodes from retrieved peer lists
|
||||
public Node(InetSocketAddress socketAddress) {
|
||||
this();
|
||||
this.hostAddress = Address.of(socketAddress.getAddress());
|
||||
this.host = socketAddress.getHostString();
|
||||
this.rpcPort = 0; // unknown
|
||||
this.levinPort = socketAddress.getPort();
|
||||
|
@ -213,7 +211,7 @@ public class Node {
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hostAddress.hashCode();
|
||||
return host.hashCode();
|
||||
}
|
||||
|
||||
// Nodes are equal if they are the same host address:port & are on the same network
|
||||
|
@ -221,13 +219,14 @@ public class Node {
|
|||
public boolean equals(Object other) {
|
||||
if (!(other instanceof Node)) return false;
|
||||
final Node anotherNode = (Node) other;
|
||||
return (hostAddress.equals(anotherNode.hostAddress)
|
||||
return (host.equals(anotherNode.host)
|
||||
&& (getAddress().equals(anotherNode.getAddress()))
|
||||
&& (rpcPort == anotherNode.rpcPort)
|
||||
&& (networkType == anotherNode.networkType));
|
||||
}
|
||||
|
||||
public boolean isOnion() {
|
||||
return hostAddress.isOnion();
|
||||
return OnionHelper.isOnionHost(host);
|
||||
}
|
||||
|
||||
public String toNodeString() {
|
||||
|
@ -263,49 +262,23 @@ public class Node {
|
|||
}
|
||||
|
||||
public String getAddress() {
|
||||
return getHostAddress() + ":" + rpcPort;
|
||||
return getHost() + ":" + rpcPort;
|
||||
}
|
||||
|
||||
public String getHostAddress() {
|
||||
return hostAddress.getHostAddress();
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) throws UnknownHostException {
|
||||
if ((host == null) || (host.isEmpty()))
|
||||
throw new UnknownHostException("loopback not supported (yet?)");
|
||||
this.host = host;
|
||||
this.hostAddress = Address.of(host);
|
||||
}
|
||||
|
||||
public void setDefaultName() {
|
||||
if (name != null) return;
|
||||
String nodeName = hostAddress.getHostName();
|
||||
if (hostAddress.isOnion()) {
|
||||
nodeName = nodeName.substring(0, nodeName.length() - ".onion".length());
|
||||
if (nodeName.length() > 16) {
|
||||
nodeName = nodeName.substring(0, 8) + "…" + nodeName.substring(nodeName.length() - 6);
|
||||
}
|
||||
nodeName = nodeName + ".onion";
|
||||
}
|
||||
this.name = nodeName;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
if ((name == null) || (name.isEmpty()))
|
||||
setDefaultName();
|
||||
else
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void toggleFavourite() {
|
||||
favourite = !favourite;
|
||||
}
|
||||
|
||||
public void overwriteWith(Node anotherNode) {
|
||||
if (networkType != anotherNode.networkType)
|
||||
throw new IllegalStateException("network types do not match");
|
||||
name = anotherNode.name;
|
||||
hostAddress = anotherNode.hostAddress;
|
||||
host = anotherNode.host;
|
||||
rpcPort = anotherNode.rpcPort;
|
||||
levinPort = anotherNode.levinPort;
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package com.m2049r.xmrwallet.fragment.dialog;
|
||||
|
||||
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 androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.service.PrefService;
|
||||
import com.m2049r.xmrwallet.util.Constants;
|
||||
|
||||
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 nodeNameEditText = view.findViewById(R.id.node_name_edittext);
|
||||
|
||||
addNodeButton.setOnClickListener(view1 -> {
|
||||
String node = addressEditText.getText().toString();
|
||||
String name = nodeNameEditText.getText().toString();
|
||||
if(node.contains(":") && !name.isEmpty()) {
|
||||
String[] nodeParts = node.split(":");
|
||||
if(nodeParts.length == 2) {
|
||||
try {
|
||||
String address = nodeParts[0];
|
||||
int port = Integer.parseInt(nodeParts[1]);
|
||||
String newNodeString = address + ":" + port + "/mainnet/" + name;
|
||||
boolean validIp = Patterns.IP_ADDRESS.matcher(address).matches();
|
||||
if(validIp) {
|
||||
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;
|
||||
}
|
||||
|
||||
if(!exists) {
|
||||
jsonArray.put(newNodeString);
|
||||
}
|
||||
|
||||
PrefService.getInstance().edit().putString(Constants.PREF_CUSTOM_NODES, jsonArray.toString()).apply();
|
||||
if(listener != null) {
|
||||
listener.onNodeAdded();
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
} catch(NumberFormatException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface AddNodeListener {
|
||||
void onNodeAdded();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package com.m2049r.xmrwallet.fragment.dialog;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
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 com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.adapter.NodeSelectionAdapter;
|
||||
import com.m2049r.xmrwallet.data.DefaultNodes;
|
||||
import com.m2049r.xmrwallet.data.Node;
|
||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
import com.m2049r.xmrwallet.service.PrefService;
|
||||
import com.m2049r.xmrwallet.util.Constants;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
public class NodeSelectionBottomSheetDialog extends BottomSheetDialogFragment implements NodeSelectionAdapter.NodeSelectionAdapterListener {
|
||||
private NodeSelectionAdapter adapter = null;
|
||||
public NodeSelectionDialogListener listener = 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();
|
||||
});
|
||||
|
||||
for(DefaultNodes defaultNode : DefaultNodes.values()) {
|
||||
nodes.add(Node.fromString(defaultNode.getUri()));
|
||||
}
|
||||
try {
|
||||
String nodesArray = PrefService.getInstance().getString(Constants.PREF_CUSTOM_NODES, "[]");
|
||||
JSONArray jsonArray = new JSONArray(nodesArray);
|
||||
for(int i = 0; i < jsonArray.length(); i++) {
|
||||
String nodeString = jsonArray.getString(i);
|
||||
Node node = Node.fromString(nodeString);
|
||||
nodes.add(node);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
adapter.submitList(nodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectNode(Node node) {
|
||||
PrefService.getInstance().edit().putString(Constants.PREF_NODE, node.getAddress()).apply();
|
||||
WalletManager.getInstance().setDaemon(node);
|
||||
adapter.updateSelectedNode();
|
||||
}
|
||||
|
||||
public interface NodeSelectionDialogListener {
|
||||
void onClickedAddNode();
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -16,18 +17,25 @@ import androidx.annotation.Nullable;
|
|||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.data.DefaultNodes;
|
||||
import com.m2049r.xmrwallet.data.Node;
|
||||
import com.m2049r.xmrwallet.fragment.dialog.AddNodeBottomSheetDialog;
|
||||
import com.m2049r.xmrwallet.fragment.dialog.InformationBottomSheetDialog;
|
||||
import com.m2049r.xmrwallet.fragment.dialog.NodeSelectionBottomSheetDialog;
|
||||
import com.m2049r.xmrwallet.fragment.dialog.PasswordBottomSheetDialog;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
import com.m2049r.xmrwallet.service.BlockchainService;
|
||||
import com.m2049r.xmrwallet.service.PrefService;
|
||||
import com.m2049r.xmrwallet.util.Constants;
|
||||
import com.m2049r.xmrwallet.util.DayNightMode;
|
||||
import com.m2049r.xmrwallet.util.NightmodeHelper;
|
||||
|
||||
public class SettingsFragment extends Fragment implements PasswordBottomSheetDialog.PasswordListener {
|
||||
public class SettingsFragment extends Fragment implements PasswordBottomSheetDialog.PasswordListener, NodeSelectionBottomSheetDialog.NodeSelectionDialogListener, AddNodeBottomSheetDialog.AddNodeListener {
|
||||
|
||||
private SettingsViewModel mViewModel;
|
||||
TextWatcher proxyAddressListener = new TextWatcher() {
|
||||
|
@ -64,6 +72,7 @@ public class SettingsFragment extends Fragment implements PasswordBottomSheetDia
|
|||
super.onViewCreated(view, savedInstanceState);
|
||||
mViewModel = new ViewModelProvider(this).get(SettingsViewModel.class);
|
||||
Button displaySeedButton = view.findViewById(R.id.display_seed_button);
|
||||
Button selectNodeButton = view.findViewById(R.id.select_node_button);
|
||||
SwitchCompat nightModeSwitch = view.findViewById(R.id.day_night_switch);
|
||||
SwitchCompat torSwitch = view.findViewById(R.id.tor_switch);
|
||||
ConstraintLayout proxySettingsLayout = view.findViewById(R.id.wallet_proxy_settings_layout);
|
||||
|
@ -126,6 +135,24 @@ public class SettingsFragment extends Fragment implements PasswordBottomSheetDia
|
|||
displaySeedDialog();
|
||||
}
|
||||
});
|
||||
|
||||
TextView statusTextView = view.findViewById(R.id.status_textview);
|
||||
BlockchainService.getInstance().connectionStatus.observe(getViewLifecycleOwner(), connectionStatus -> {
|
||||
if(connectionStatus == Wallet.ConnectionStatus.ConnectionStatus_Connected) {
|
||||
statusTextView.setText(getResources().getText(R.string.connected));
|
||||
} else if(connectionStatus == Wallet.ConnectionStatus.ConnectionStatus_Disconnected) {
|
||||
statusTextView.setText(getResources().getText(R.string.disconnected));
|
||||
} else if(connectionStatus == Wallet.ConnectionStatus.ConnectionStatus_WrongVersion) {
|
||||
statusTextView.setText(getResources().getText(R.string.version_mismatch));
|
||||
}
|
||||
});
|
||||
Node node = Node.fromString(PrefService.getInstance().getString(Constants.PREF_NODE, DefaultNodes.XMRTW.getAddress()));
|
||||
selectNodeButton.setText(getString(R.string.node_button_text, node.getAddress()));
|
||||
selectNodeButton.setOnClickListener(view1 -> {
|
||||
NodeSelectionBottomSheetDialog dialog = new NodeSelectionBottomSheetDialog();
|
||||
dialog.listener = this;
|
||||
dialog.show(getActivity().getSupportFragmentManager(), "node_selection_dialog");
|
||||
});
|
||||
}
|
||||
|
||||
private void displaySeedDialog() {
|
||||
|
@ -164,4 +191,18 @@ public class SettingsFragment extends Fragment implements PasswordBottomSheetDia
|
|||
walletProxyAddressEditText.addTextChangedListener(proxyAddressListener);
|
||||
walletProxyPortEditText.addTextChangedListener(proxyPortListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickedAddNode() {
|
||||
AddNodeBottomSheetDialog addNodeDialog = new AddNodeBottomSheetDialog();
|
||||
addNodeDialog.listener = this;
|
||||
addNodeDialog.show(getActivity().getSupportFragmentManager(), "add_node_dialog");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNodeAdded() {
|
||||
NodeSelectionBottomSheetDialog dialog = new NodeSelectionBottomSheetDialog();
|
||||
dialog.listener = this;
|
||||
dialog.show(getActivity().getSupportFragmentManager(), "node_selection_dialog");
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ import android.widget.Toast;
|
|||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.data.DefaultNodes;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
import com.m2049r.xmrwallet.service.PrefService;
|
||||
import com.m2049r.xmrwallet.service.TxService;
|
||||
|
@ -20,7 +21,10 @@ public class SettingsViewModel extends ViewModel {
|
|||
public void updateProxy() {
|
||||
AsyncTask.execute(() -> {
|
||||
boolean usesProxy = PrefService.getInstance().getBoolean(Constants.PREF_USES_TOR, false);
|
||||
if(!usesProxy) {
|
||||
String currentNodeString = PrefService.getInstance().getString(Constants.PREF_NODE, DefaultNodes.XMRTW.getAddress());
|
||||
boolean isNodeLocalIp = currentNodeString.startsWith("10.") || currentNodeString.startsWith("192.168.") || currentNodeString.equals("localhost") || currentNodeString.equals("127.0.0.1");
|
||||
|
||||
if(!usesProxy || isNodeLocalIp) {
|
||||
WalletManager.getInstance().setProxy("");
|
||||
WalletManager.getInstance().getWallet().setProxy("");
|
||||
return;
|
||||
|
@ -29,6 +33,7 @@ public class SettingsViewModel extends ViewModel {
|
|||
if(proxyAddress.isEmpty()) proxyAddress = "127.0.0.1";
|
||||
if(proxyPort.isEmpty()) proxyPort = "9050";
|
||||
boolean validIpAddress = Patterns.IP_ADDRESS.matcher(proxyAddress).matches();
|
||||
|
||||
if(validIpAddress) {
|
||||
String proxy = proxyAddress + ":" + proxyPort;
|
||||
PrefService.getInstance().edit().putString(Constants.PREF_PROXY, proxy).apply();
|
||||
|
|
|
@ -3,12 +3,15 @@ package com.m2049r.xmrwallet.service;
|
|||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
|
||||
public class BlockchainService extends ServiceBase {
|
||||
public static BlockchainService instance = null;
|
||||
private final MutableLiveData<Long> _currentHeight = new MutableLiveData<>(0L);
|
||||
public LiveData<Long> height = _currentHeight;
|
||||
private final MutableLiveData<Wallet.ConnectionStatus> _connectionStatus = new MutableLiveData<>(Wallet.ConnectionStatus.ConnectionStatus_Disconnected);
|
||||
public LiveData<Wallet.ConnectionStatus> connectionStatus = _connectionStatus;
|
||||
private long daemonHeight = 0;
|
||||
private long lastDaemonHeightUpdateTimeMs = 0;
|
||||
public BlockchainService(MoneroHandlerThread thread) {
|
||||
|
@ -44,4 +47,8 @@ public class BlockchainService extends ServiceBase {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setConnectionStatus(Wallet.ConnectionStatus status) {
|
||||
_connectionStatus.postValue(status);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,15 +55,16 @@ public class MoneroHandlerThread extends Thread implements WalletListener {
|
|||
|
||||
@Override
|
||||
public void run() {
|
||||
String currentNodeString = PrefService.getInstance().getString(Constants.PREF_NODE, DefaultNodes.XMRTW.getAddress());
|
||||
Node selectedNode = Node.fromString(currentNodeString);
|
||||
boolean usesTor = PrefService.getInstance().getBoolean(Constants.PREF_USES_TOR, false);
|
||||
if (usesTor) {
|
||||
boolean isLocalIp = currentNodeString.startsWith("10.") || currentNodeString.startsWith("192.168.") || currentNodeString.equals("localhost") || currentNodeString.equals("127.0.0.1");
|
||||
if (usesTor && !isLocalIp) {
|
||||
String proxy = PrefService.getInstance().getString(Constants.PREF_PROXY, "");
|
||||
WalletManager.getInstance().setProxy(proxy);
|
||||
WalletManager.getInstance().setDaemon(Node.fromString(DefaultNodes.SAMOURAI.getUri()));
|
||||
wallet.setProxy(proxy);
|
||||
} else {
|
||||
WalletManager.getInstance().setDaemon(Node.fromString(DefaultNodes.XMRTW.getUri()));
|
||||
}
|
||||
WalletManager.getInstance().setDaemon(selectedNode);
|
||||
wallet.init(0);
|
||||
wallet.setListener(this);
|
||||
wallet.startRefresh();
|
||||
|
@ -108,6 +109,8 @@ public class MoneroHandlerThread extends Thread implements WalletListener {
|
|||
wallet.store();
|
||||
refresh();
|
||||
}
|
||||
|
||||
BlockchainService.getInstance().setConnectionStatus(status);
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
|
|
|
@ -7,6 +7,8 @@ public class Constants {
|
|||
public static final String PREF_USES_TOR = "pref_uses_tor";
|
||||
public static final String PREF_NIGHT_MODE = "pref_night_mode";
|
||||
public static final String PREF_PROXY = "pref_proxy";
|
||||
public static final String PREF_NODE = "pref_node";
|
||||
public static final String PREF_CUSTOM_NODES = "pref_custom_nodes";
|
||||
|
||||
public static final String URI_PREFIX = "monero:";
|
||||
public static final String URI_ARG_AMOUNT = "tx_amount";
|
||||
|
|
84
app/src/main/res/layout/add_node_bottom_sheet_dialog.xml
Normal file
84
app/src/main/res/layout/add_node_bottom_sheet_dialog.xml
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?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/add_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="1234567890.:"
|
||||
app:layout_constraintBottom_toTopOf="@id/add_node_button"
|
||||
app:layout_constraintEnd_toStartOf="@id/paste_address_imagebutton"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/add_node_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:background="@drawable/button_bg"
|
||||
android:text="@string/add_node"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/address_edittext" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
|
@ -1,146 +1,168 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<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:padding="24dp"
|
||||
tools:context=".fragment.settings.SettingsFragment">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/settings_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings"
|
||||
android:textSize="32sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@id/wallet_settings_textview"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/wallet_settings_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/wallet"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@id/display_seed_button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/settings_textview" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/display_seed_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/button_bg"
|
||||
android:text="@string/display_recovery_phrase"
|
||||
app:layout_constraintBottom_toTopOf="@id/appearance_settings_textview"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/wallet_settings_textview" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/appearance_settings_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/appearance"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@id/day_night_switch"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/display_seed_button" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/day_night_textview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/night_mode"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/day_night_switch"
|
||||
app:layout_constraintEnd_toStartOf="@id/day_night_switch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/day_night_switch" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/day_night_switch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/network_settings_textview"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appearance_settings_textview" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/network_settings_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/network"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@id/tor_switch"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/day_night_switch" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tor_textview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/tor_switch_label"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tor_switch"
|
||||
app:layout_constraintEnd_toStartOf="@id/tor_switch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tor_switch" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/tor_switch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/network_settings_textview" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/wallet_proxy_settings_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintTop_toBottomOf="@id/tor_switch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
<EditText
|
||||
android:id="@+id/wallet_proxy_address_edittext"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/settings_textview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/edittext_bg"
|
||||
android:hint="@string/wallet_proxy_address_hint"
|
||||
app:layout_constraintBottom_toTopOf="@id/wallet_proxy_port_edittext"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:text="@string/settings"
|
||||
android:textSize="32sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toStartOf="@id/status_textview"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/wallet_proxy_port_edittext"
|
||||
android:layout_width="0dp"
|
||||
<TextView
|
||||
android:id="@+id/status_textview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/edittext_bg"
|
||||
android:hint="@string/wallet_proxy_port_hint"
|
||||
android:inputType="number"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:text="@string/disconnected"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/settings_textview"
|
||||
app:layout_constraintBottom_toBottomOf="@id/settings_textview"
|
||||
app:layout_constraintTop_toTopOf="@id/settings_textview" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/wallet_settings_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/wallet"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/wallet_proxy_address_edittext" />
|
||||
app:layout_constraintTop_toBottomOf="@id/settings_textview" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/display_seed_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/button_bg"
|
||||
android:text="@string/display_recovery_phrase"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/wallet_settings_textview" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/appearance_settings_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/appearance"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/display_seed_button" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/day_night_textview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/night_mode"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/day_night_switch"
|
||||
app:layout_constraintEnd_toStartOf="@id/day_night_switch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/day_night_switch" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/day_night_switch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appearance_settings_textview" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/network_settings_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/network"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/day_night_switch" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/select_node_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="middle"
|
||||
android:background="@drawable/button_bg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/network_settings_textview" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tor_textview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/tor_switch_label"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tor_switch"
|
||||
app:layout_constraintEnd_toStartOf="@id/tor_switch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tor_switch" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/tor_switch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/select_node_button" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/wallet_proxy_settings_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintTop_toBottomOf="@id/tor_switch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
<EditText
|
||||
android:id="@+id/wallet_proxy_address_edittext"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/edittext_bg"
|
||||
android:hint="@string/wallet_proxy_address_hint"
|
||||
app:layout_constraintBottom_toTopOf="@id/wallet_proxy_port_edittext"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/wallet_proxy_port_edittext"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/edittext_bg"
|
||||
android:hint="@string/wallet_proxy_port_hint"
|
||||
android:inputType="number"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/wallet_proxy_address_edittext" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.core.widget.NestedScrollView 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:padding="24dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nodes_textview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:text="@string/nodes"
|
||||
android:textSize="32sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@id/add_node_button"
|
||||
app:layout_constraintEnd_toStartOf="@id/add_node_button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/add_node_button" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/add_node_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/button_bg"
|
||||
android:text="@string/add_node"
|
||||
app:layout_constraintStart_toEndOf="@id/nodes_textview"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/node_selection_recyclerview"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/node_selection_recyclerview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="128dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/add_node_button">
|
||||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
31
app/src/main/res/layout/node_selection_item.xml
Normal file
31
app/src/main/res/layout/node_selection_item.xml
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:layout_marginBottom="8dp">
|
||||
<TextView
|
||||
android:id="@+id/node_name_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Node Name"
|
||||
android:textStyle="bold"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/node_uri_textview"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
<TextView
|
||||
android:id="@+id/node_uri_textview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="NODE::"
|
||||
android:ellipsize="middle"
|
||||
app:layout_constraintTop_toBottomOf="@id/node_name_textview"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:singleLine="true" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -76,9 +76,17 @@
|
|||
<string name="wallet">Wallet</string>
|
||||
<string name="appearance">Appearance</string>
|
||||
<string name="network">Network</string>
|
||||
<string name="add_node">Add Node</string>
|
||||
<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="wallet_proxy_address_hint">127.0.0.1</string>
|
||||
<string name="wallet_proxy_port_hint">9050</string>
|
||||
<string name="invalid_ip">Invalid IP address</string>
|
||||
<string name="no_history_nget_some_monero_in_here">No history!\nGet some Monero in here!</string>
|
||||
<string name="node_button_text">Node: %1$s</string>
|
||||
<string name="connected">Connected</string>
|
||||
<string name="disconnected">Disconnected</string>
|
||||
<string name="version_mismatch">Version mismatch</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue