Add option to onboarding screen to connect to proxy

This commit is contained in:
pokkst 2022-10-07 23:13:04 -05:00
parent 58329e5212
commit 6be5d2a504
No known key found for this signature in database
GPG key ID: 90C2ED85E67A50FF
9 changed files with 202 additions and 11 deletions

View file

@ -12,6 +12,7 @@ import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.navigation.fragment.NavHostFragment;
import net.mynero.wallet.data.DefaultNodes;
import net.mynero.wallet.fragment.dialog.PasswordBottomSheetDialog;
import net.mynero.wallet.fragment.dialog.SendBottomSheetDialog;
import net.mynero.wallet.livedata.SingleLiveEvent;

View file

@ -96,7 +96,7 @@ public class NodeSelectionAdapter extends RecyclerView.Adapter<NodeSelectionAdap
}
public void bind(Node node) {
String currentNodeString = PrefService.getInstance().getString(Constants.PREF_NODE, DefaultNodes.XMRTW.getAddress());
String currentNodeString = PrefService.getInstance().getString(Constants.PREF_NODE, "");
Node currentNode = Node.fromString(currentNodeString);
boolean match = node.equals(currentNode);
if (match) {

View file

@ -3,6 +3,7 @@ package net.mynero.wallet.fragment.onboarding;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -14,12 +15,15 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.SwitchCompat;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import net.mynero.wallet.MainActivity;
import net.mynero.wallet.MoneroApplication;
import net.mynero.wallet.R;
import net.mynero.wallet.data.DefaultNodes;
import net.mynero.wallet.model.Wallet;
import net.mynero.wallet.model.WalletManager;
import net.mynero.wallet.service.PrefService;
@ -32,6 +36,42 @@ import java.util.concurrent.Executors;
public class OnboardingFragment extends Fragment {
private OnboardingViewModel mViewModel;
TextWatcher proxyAddressListener = 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 (mViewModel != null) {
mViewModel.setProxyAddress(editable.toString());
mViewModel.updateProxy(((MoneroApplication)getActivity().getApplication()));
}
}
};
TextWatcher proxyPortListener = 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 (mViewModel != null) {
mViewModel.setProxyPort(editable.toString());
mViewModel.updateProxy(((MoneroApplication)getActivity().getApplication()));
}
}
};
private EditText walletProxyAddressEditText;
private EditText walletProxyPortEditText;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@ -49,11 +89,16 @@ public class OnboardingFragment extends Fragment {
Button createWalletButton = view.findViewById(R.id.create_wallet_button);
TextView moreOptionsDropdownTextView = view.findViewById(R.id.advanced_settings_dropdown_textview);
ImageView moreOptionsChevronImageView = view.findViewById(R.id.advanced_settings_chevron_imageview);
SwitchCompat torSwitch = view.findViewById(R.id.tor_onboarding_switch);
ConstraintLayout proxySettingsLayout = view.findViewById(R.id.wallet_proxy_settings_layout);
walletProxyAddressEditText = view.findViewById(R.id.wallet_proxy_address_edittext);
walletProxyPortEditText = view.findViewById(R.id.wallet_proxy_port_edittext);
moreOptionsDropdownTextView.setOnClickListener(view12 -> mViewModel.onMoreOptionsClicked());
moreOptionsChevronImageView.setOnClickListener(view12 -> mViewModel.onMoreOptionsClicked());
createWalletButton.setOnClickListener(view1 -> {
prepareDefaultNode();
((MoneroApplication)getActivity().getApplication()).getExecutor().execute(() -> {
createOrImportWallet(
walletPasswordEditText.getText().toString(),
@ -81,6 +126,26 @@ public class OnboardingFragment extends Fragment {
}
}
});
torSwitch.setOnCheckedChangeListener((compoundButton, b) -> {
PrefService.getInstance().edit().putBoolean(Constants.PREF_USES_TOR, b).apply();
if (b) {
String proxyString = PrefService.getInstance().getString(Constants.PREF_PROXY, "");
if (proxyString.contains(":")) {
removeProxyTextListeners();
String proxyAddress = proxyString.split(":")[0];
String proxyPort = proxyString.split(":")[1];
initProxyStuff(proxyAddress, proxyPort);
addProxyTextListeners();
}
proxySettingsLayout.setVisibility(View.VISIBLE);
} else {
proxySettingsLayout.setVisibility(View.GONE);
}
mViewModel.updateProxy(((MoneroApplication)getActivity().getApplication()));
});
mViewModel.showMoreOptions.observe(getViewLifecycleOwner(), show -> {
if (show) {
@ -95,6 +160,12 @@ public class OnboardingFragment extends Fragment {
});
}
private void prepareDefaultNode() {
boolean usesTor = PrefService.getInstance().getBoolean(Constants.PREF_USES_TOR, false);
DefaultNodes defaultNode = usesTor ? DefaultNodes.SAMOURAI_ONION : DefaultNodes.SAMOURAI;
PrefService.getInstance().edit().putString(Constants.PREF_NODE, defaultNode.getAddress()).apply();
}
private void createOrImportWallet(String walletPassword, String walletSeed, String restoreHeightText) {
MainActivity mainActivity = (MainActivity) getActivity();
if (mainActivity != null) {
@ -130,6 +201,26 @@ public class OnboardingFragment extends Fragment {
}
}
private void removeProxyTextListeners() {
walletProxyAddressEditText.removeTextChangedListener(proxyAddressListener);
walletProxyPortEditText.removeTextChangedListener(proxyPortListener);
}
private void addProxyTextListeners() {
walletProxyAddressEditText.addTextChangedListener(proxyAddressListener);
walletProxyPortEditText.addTextChangedListener(proxyPortListener);
}
private void initProxyStuff(String proxyAddress, String proxyPort) {
boolean validIpAddress = Patterns.IP_ADDRESS.matcher(proxyAddress).matches();
if (validIpAddress) {
mViewModel.setProxyAddress(proxyAddress);
mViewModel.setProxyPort(proxyPort);
walletProxyAddressEditText.setText(proxyAddress);
walletProxyPortEditText.setText(proxyPort);
}
}
private boolean checkMnemonic(String seed) {
return (seed.split("\\s").length == 25);
}

View file

@ -1,16 +1,54 @@
package net.mynero.wallet.fragment.onboarding;
import android.util.Patterns;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import net.mynero.wallet.MoneroApplication;
import net.mynero.wallet.data.DefaultNodes;
import net.mynero.wallet.model.WalletManager;
import net.mynero.wallet.service.PrefService;
import net.mynero.wallet.util.Constants;
public class OnboardingViewModel extends ViewModel {
private final MutableLiveData<Boolean> _showMoreOptions = new MutableLiveData<>(false);
public LiveData<Boolean> showMoreOptions = _showMoreOptions;
private String proxyAddress = "";
private String proxyPort = "";
public void onMoreOptionsClicked() {
boolean currentValue = showMoreOptions.getValue() != null ? showMoreOptions.getValue() : false;
boolean newValue = !currentValue;
_showMoreOptions.setValue(newValue);
}
public void updateProxy(MoneroApplication application) {
application.getExecutor().execute(() -> {
boolean usesProxy = PrefService.getInstance().getBoolean(Constants.PREF_USES_TOR, false);
if (!usesProxy) {
return;
}
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();
}
});
}
public void setProxyAddress(String address) {
this.proxyAddress = address;
}
public void setProxyPort(String port) {
this.proxyPort = port;
}
}

View file

@ -174,7 +174,7 @@ public class SettingsFragment extends Fragment implements PasswordBottomSheetDia
statusTextView.setText(getResources().getText(R.string.version_mismatch));
}
});
Node node = Node.fromString(PrefService.getInstance().getString(Constants.PREF_NODE, DefaultNodes.XMRTW.getAddress()));
Node node = Node.fromString(PrefService.getInstance().getString(Constants.PREF_NODE, "")); // shouldn't use default value here
selectNodeButton.setText(getString(R.string.node_button_text, node.getAddress()));
selectNodeButton.setOnClickListener(view1 -> {
NodeSelectionBottomSheetDialog dialog = new NodeSelectionBottomSheetDialog();
@ -222,7 +222,7 @@ public class SettingsFragment extends Fragment implements PasswordBottomSheetDia
@Override
public void onNodeSelected() {
Node node = Node.fromString(PrefService.getInstance().getString(Constants.PREF_NODE, DefaultNodes.XMRTW.getAddress()));
Node node = Node.fromString(PrefService.getInstance().getString(Constants.PREF_NODE, ""));
selectNodeButton.setText(getString(R.string.node_button_text, node.getAddress()));
mViewModel.updateProxy(((MoneroApplication)getActivity().getApplication()));
((MoneroApplication)getActivity().getApplication()).getExecutor().execute(() -> {

View file

@ -21,7 +21,7 @@ public class SettingsViewModel extends ViewModel {
public void updateProxy(MoneroApplication application) {
application.getExecutor().execute(() -> {
boolean usesProxy = PrefService.getInstance().getBoolean(Constants.PREF_USES_TOR, false);
String currentNodeString = PrefService.getInstance().getString(Constants.PREF_NODE, DefaultNodes.XMRTW.getAddress());
String currentNodeString = PrefService.getInstance().getString(Constants.PREF_NODE, "");
boolean isNodeLocalIp = currentNodeString.startsWith("10.") || currentNodeString.startsWith("192.168.") || currentNodeString.equals("localhost") || currentNodeString.equals("127.0.0.1");
if (!usesProxy || isNodeLocalIp) {

View file

@ -56,9 +56,13 @@ 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);
DefaultNodes defaultNode = DefaultNodes.SAMOURAI;
if(usesTor) {
defaultNode = DefaultNodes.SAMOURAI_ONION;
}
String currentNodeString = PrefService.getInstance().getString(Constants.PREF_NODE, defaultNode.getAddress());
Node selectedNode = Node.fromString(currentNodeString);
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, "");

View file

@ -28,25 +28,82 @@
android:background="@drawable/edittext_bg"
android:hint="@string/password_optional"
android:inputType="textPassword"
app:layout_constraintBottom_toTopOf="@id/advanced_settings_dropdown_textview"
app:layout_constraintBottom_toTopOf="@id/tor_onboarding_switch"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/create_wallet_textview"
tools:visibility="visible" />
<TextView
android:id="@+id/tor_onboarding_switch_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/tor_switch_label"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/tor_onboarding_switch"
app:layout_constraintEnd_toStartOf="@id/tor_onboarding_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/tor_onboarding_switch" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/tor_onboarding_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:minHeight="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/wallet_password_edittext" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/wallet_proxy_settings_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/tor_onboarding_switch"
app:layout_constraintBottom_toTopOf="@id/advanced_settings_dropdown_textview"
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>
<TextView
android:id="@+id/advanced_settings_dropdown_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="16dp"
android:text="@string/more_options"
android:textStyle="bold"
android:padding="4dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
app:layout_constraintBottom_toTopOf="@id/wallet_seed_edittext"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/wallet_password_edittext" />
app:layout_constraintTop_toBottomOf="@id/wallet_proxy_settings_layout" />
<ImageView
android:id="@+id/advanced_settings_chevron_imageview"
@ -63,7 +120,7 @@
android:id="@+id/wallet_seed_edittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:layout_marginBottom="16dp"
android:background="@drawable/edittext_bg"
android:hint="@string/recovery_phrase_optional"
android:visibility="gone"

View file

@ -40,7 +40,7 @@
<string name="copied_to_clipboard">Copied to clipboard</string>
<string name="night_mode">Night mode</string>
<string name="display_recovery_phrase">Display wallet keys</string>
<string name="tor_switch_label">Enable proxy</string>
<string name="tor_switch_label">Connect to proxy</string>
<string name="connection_failed">Connection failed</string>
<string name="address">87MRtZPrWUCVUgcFHdsVb5MoZUcLtqfD3FvQVGwftFb8eSdMnE39JhAJcbuSW8X2vRaRsB9RQfuCpFciybJFHaz3QYPhCLw</string>
<string name="amount">0.00</string>