mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-07 11:35:59 +02:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
84ce392192 | ||
![]() |
4ebcda2b14 | ||
![]() |
c49351a8a9 | ||
![]() |
41e84f2e29 | ||
![]() |
f08ddf93c8 | ||
![]() |
4bfc9c1bdd | ||
![]() |
1a13bdde91 | ||
![]() |
3e56d5a54b |
@@ -8,8 +8,8 @@ android {
|
|||||||
compileSdk 35
|
compileSdk 35
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 35
|
targetSdkVersion 35
|
||||||
versionCode 4006
|
versionCode 4102
|
||||||
versionName "4.0.6 'Sidekick'"
|
versionName "4.1.2 'Exolix'"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
@@ -58,7 +58,7 @@ android {
|
|||||||
applicationIdSuffix ".debug"
|
applicationIdSuffix ".debug"
|
||||||
}
|
}
|
||||||
applicationVariants.all { variant ->
|
applicationVariants.all { variant ->
|
||||||
variant.buildConfigField "String", "ID_A", "\"" + getId("ID_A") + "\""
|
variant.buildConfigField "String", "ID_F", "\"" + getId("ID_F") + "\""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -99,7 +99,7 @@ public class GenerateReviewFragment extends Fragment {
|
|||||||
private String walletPath;
|
private String walletPath;
|
||||||
private String walletName;
|
private String walletName;
|
||||||
|
|
||||||
private OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(false) {
|
private final OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(false) {
|
||||||
@Override
|
@Override
|
||||||
public void handleOnBackPressed() {
|
public void handleOnBackPressed() {
|
||||||
// nothing
|
// nothing
|
||||||
@@ -164,6 +164,7 @@ public class GenerateReviewFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Bundle args = getArguments();
|
Bundle args = getArguments();
|
||||||
|
assert args != null;
|
||||||
type = args.getString(REQUEST_TYPE);
|
type = args.getString(REQUEST_TYPE);
|
||||||
walletPath = args.getString(REQUEST_PATH);
|
walletPath = args.getString(REQUEST_PATH);
|
||||||
localPassword = args.getString(REQUEST_PASSWORD);
|
localPassword = args.getString(REQUEST_PASSWORD);
|
||||||
|
@@ -22,13 +22,14 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class LockFragment extends Fragment {
|
public class LockFragment extends Fragment {
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
Timber.d("onCreateView");
|
Timber.d("onCreateView");
|
||||||
final FrameLayout frame = new FrameLayout(requireContext());
|
final FrameLayout frame = new FrameLayout(requireContext());
|
||||||
frame.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
frame.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
@@ -40,6 +40,7 @@ import android.widget.Toast;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
@@ -618,6 +619,7 @@ public class LoginActivity extends BaseActivity
|
|||||||
try {
|
try {
|
||||||
GenerateReviewFragment detailsFragment = (GenerateReviewFragment)
|
GenerateReviewFragment detailsFragment = (GenerateReviewFragment)
|
||||||
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
||||||
|
assert detailsFragment != null;
|
||||||
AlertDialog dialog = detailsFragment.createChangePasswordDialog();
|
AlertDialog dialog = detailsFragment.createChangePasswordDialog();
|
||||||
if (dialog != null) {
|
if (dialog != null) {
|
||||||
Helper.showKeyboard(dialog);
|
Helper.showKeyboard(dialog);
|
||||||
@@ -895,6 +897,7 @@ public class LoginActivity extends BaseActivity
|
|||||||
try {
|
try {
|
||||||
GenerateFragment genFragment = (GenerateFragment)
|
GenerateFragment genFragment = (GenerateFragment)
|
||||||
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
||||||
|
assert genFragment != null;
|
||||||
genFragment.walletGenerateError();
|
genFragment.walletGenerateError();
|
||||||
} catch (ClassCastException ex) {
|
} catch (ClassCastException ex) {
|
||||||
Timber.e("walletGenerateError() but not in GenerateFragment");
|
Timber.e("walletGenerateError() but not in GenerateFragment");
|
||||||
@@ -1260,7 +1263,7 @@ public class LoginActivity extends BaseActivity
|
|||||||
if (usbManager.hasPermission(device)) {
|
if (usbManager.hasPermission(device)) {
|
||||||
connectLedger(usbManager, device);
|
connectLedger(usbManager, device);
|
||||||
} else {
|
} else {
|
||||||
registerReceiver(usbPermissionReceiver, new IntentFilter(ACTION_USB_PERMISSION));
|
ContextCompat.registerReceiver(this, usbPermissionReceiver, new IntentFilter(ACTION_USB_PERMISSION), ContextCompat.RECEIVER_EXPORTED);
|
||||||
usbManager.requestPermission(device,
|
usbManager.requestPermission(device,
|
||||||
PendingIntent.getBroadcast(this, 0,
|
PendingIntent.getBroadcast(this, 0,
|
||||||
new Intent(ACTION_USB_PERMISSION),
|
new Intent(ACTION_USB_PERMISSION),
|
||||||
@@ -1365,7 +1368,7 @@ public class LoginActivity extends BaseActivity
|
|||||||
private void registerDetachReceiver() {
|
private void registerDetachReceiver() {
|
||||||
detachReceiver = new BroadcastReceiver() {
|
detachReceiver = new BroadcastReceiver() {
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
|
if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
|
||||||
unregisterDetachReceiver();
|
unregisterDetachReceiver();
|
||||||
final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
Timber.i("Ledger detached!");
|
Timber.i("Ledger detached!");
|
||||||
@@ -1379,8 +1382,7 @@ public class LoginActivity extends BaseActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
ContextCompat.registerReceiver(this, detachReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED), ContextCompat.RECEIVER_EXPORTED);
|
||||||
registerReceiver(detachReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onLedgerAction() {
|
public void onLedgerAction() {
|
||||||
@@ -1409,6 +1411,7 @@ public class LoginActivity extends BaseActivity
|
|||||||
Timber.d("onDeviceConnected: %s", connectedDeviceName);
|
Timber.d("onDeviceConnected: %s", connectedDeviceName);
|
||||||
try {
|
try {
|
||||||
SidekickConnectFragment f = (SidekickConnectFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
SidekickConnectFragment f = (SidekickConnectFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
||||||
|
assert f != null;
|
||||||
f.allowClick();
|
f.allowClick();
|
||||||
} catch (ClassCastException ex) {
|
} catch (ClassCastException ex) {
|
||||||
// ignore it
|
// ignore it
|
||||||
|
@@ -152,7 +152,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
showNetwork();
|
showNetwork();
|
||||||
}
|
}
|
||||||
|
|
||||||
private OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||||
@Override
|
@Override
|
||||||
public void handleOnBackPressed() {
|
public void handleOnBackPressed() {
|
||||||
animateFAB();
|
animateFAB();
|
||||||
@@ -283,7 +283,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove information of non-existent wallet
|
// remove information of non-existent wallet
|
||||||
Set<String> removedWallets = getActivity()
|
Set<String> removedWallets = requireActivity()
|
||||||
.getSharedPreferences(KeyStoreHelper.SecurityConstants.WALLET_PASS_PREFS_NAME, Context.MODE_PRIVATE)
|
.getSharedPreferences(KeyStoreHelper.SecurityConstants.WALLET_PASS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
.getAll().keySet();
|
.getAll().keySet();
|
||||||
for (WalletManager.WalletInfo s : walletList) {
|
for (WalletManager.WalletInfo s : walletList) {
|
||||||
@@ -445,7 +445,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setSubtext(String status) {
|
private void setSubtext(String status) {
|
||||||
final Context ctx = getContext();
|
final Context ctx = requireContext();
|
||||||
final Spanned text = Html.fromHtml(ctx.getString(R.string.status,
|
final Spanned text = Html.fromHtml(ctx.getString(R.string.status,
|
||||||
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
|
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
|
||||||
Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 0xFFFFFF),
|
Integer.toHexString(ThemeHelper.getThemedColor(ctx, android.R.attr.colorBackground) & 0xFFFFFF),
|
||||||
|
@@ -32,6 +32,7 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.activity.OnBackPressedCallback;
|
import androidx.activity.OnBackPressedCallback;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
@@ -68,7 +69,7 @@ public class NodeFragment extends Fragment
|
|||||||
|
|
||||||
static private int NODES_TO_FIND = 10;
|
static private int NODES_TO_FIND = 10;
|
||||||
|
|
||||||
static private NumberFormat FORMATTER = NumberFormat.getInstance();
|
static private final NumberFormat FORMATTER = NumberFormat.getInstance();
|
||||||
|
|
||||||
private SwipeRefreshLayout pullToRefresh;
|
private SwipeRefreshLayout pullToRefresh;
|
||||||
private TextView tvPull;
|
private TextView tvPull;
|
||||||
@@ -104,7 +105,7 @@ public class NodeFragment extends Fragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof Listener) {
|
if (context instanceof Listener) {
|
||||||
this.activityCallback = (Listener) context;
|
this.activityCallback = (Listener) context;
|
||||||
@@ -146,7 +147,7 @@ public class NodeFragment extends Fragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||||
@Override
|
@Override
|
||||||
public void handleOnBackPressed() {
|
public void handleOnBackPressed() {
|
||||||
Toast.makeText(requireActivity(), getString(R.string.node_refresh_wait), Toast.LENGTH_LONG).show();
|
Toast.makeText(requireActivity(), getString(R.string.node_refresh_wait), Toast.LENGTH_LONG).show();
|
||||||
@@ -210,7 +211,7 @@ public class NodeFragment extends Fragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.node_menu, menu);
|
inflater.inflate(R.menu.node_menu, menu);
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
}
|
}
|
||||||
@@ -289,8 +290,7 @@ public class NodeFragment extends Fragment
|
|||||||
} else if (params[0] == SCAN) {
|
} else if (params[0] == SCAN) {
|
||||||
// otherwise scan the network
|
// otherwise scan the network
|
||||||
Timber.d("scanning");
|
Timber.d("scanning");
|
||||||
Set<NodeInfo> seedList = new HashSet<>();
|
Set<NodeInfo> seedList = new HashSet<>(nodeList);
|
||||||
seedList.addAll(nodeList);
|
|
||||||
nodeList.clear();
|
nodeList.clear();
|
||||||
Timber.d("seed %d", seedList.size());
|
Timber.d("seed %d", seedList.size());
|
||||||
Dispatcher d = new Dispatcher(info -> publishProgress(info));
|
Dispatcher d = new Dispatcher(info -> publishProgress(info));
|
||||||
@@ -455,7 +455,7 @@ public class NodeFragment extends Fragment
|
|||||||
|
|
||||||
private void closeDialog() {
|
private void closeDialog() {
|
||||||
if (editDialog == null) throw new IllegalStateException();
|
if (editDialog == null) throw new IllegalStateException();
|
||||||
Helper.hideKeyboardAlways(getActivity());
|
Helper.hideKeyboardAlways(requireActivity());
|
||||||
editDialog.dismiss();
|
editDialog.dismiss();
|
||||||
editDialog = null;
|
editDialog = null;
|
||||||
NodeFragment.this.editDialog = null;
|
NodeFragment.this.editDialog = null;
|
||||||
|
@@ -32,12 +32,10 @@ import android.view.KeyEvent;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -84,8 +82,6 @@ public class ReceiveFragment extends Fragment {
|
|||||||
private ImageView ivQrCode;
|
private ImageView ivQrCode;
|
||||||
private ImageView ivQrCodeFull;
|
private ImageView ivQrCodeFull;
|
||||||
private EditText etDummy;
|
private EditText etDummy;
|
||||||
private ImageButton bCopyAddress;
|
|
||||||
private MenuItem shareItem;
|
|
||||||
|
|
||||||
private Wallet wallet = null;
|
private Wallet wallet = null;
|
||||||
private boolean isMyWallet = false;
|
private boolean isMyWallet = false;
|
||||||
@@ -116,11 +112,10 @@ public class ReceiveFragment extends Fragment {
|
|||||||
tvQrCode = view.findViewById(R.id.tvQrCode);
|
tvQrCode = view.findViewById(R.id.tvQrCode);
|
||||||
ivQrCodeFull = view.findViewById(R.id.qrCodeFull);
|
ivQrCodeFull = view.findViewById(R.id.qrCodeFull);
|
||||||
etDummy = view.findViewById(R.id.etDummy);
|
etDummy = view.findViewById(R.id.etDummy);
|
||||||
bCopyAddress = view.findViewById(R.id.bCopyAddress);
|
|
||||||
|
|
||||||
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||||
|
|
||||||
bCopyAddress.setOnClickListener(v -> copyAddress());
|
view.findViewById(R.id.bCopyAddress).setOnClickListener(v -> copyAddress());
|
||||||
|
|
||||||
evAmount.setOnNewAmountListener(xmr -> {
|
evAmount.setOnNewAmountListener(xmr -> {
|
||||||
Timber.d("new amount = %s", xmr);
|
Timber.d("new amount = %s", xmr);
|
||||||
@@ -211,8 +206,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
inflater.inflate(R.menu.receive_menu, menu);
|
inflater.inflate(R.menu.receive_menu, menu);
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
|
||||||
shareItem = menu.findItem(R.id.menu_item_share);
|
menu.findItem(R.id.menu_item_share).setOnMenuItemClickListener(item -> {
|
||||||
shareItem.setOnMenuItemClickListener(item -> {
|
|
||||||
if (shareRequested) return true;
|
if (shareRequested) return true;
|
||||||
shareRequested = true;
|
shareRequested = true;
|
||||||
if (!qrValid) {
|
if (!qrValid) {
|
||||||
@@ -238,7 +232,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
private boolean saveQrCode() {
|
private boolean saveQrCode() {
|
||||||
if (!qrValid) throw new IllegalStateException("trying to save null qr code!");
|
if (!qrValid) throw new IllegalStateException("trying to save null qr code!");
|
||||||
|
|
||||||
File cachePath = new File(getActivity().getCacheDir(), "images");
|
File cachePath = new File(requireActivity().getCacheDir(), "images");
|
||||||
if (!cachePath.exists())
|
if (!cachePath.exists())
|
||||||
if (!cachePath.mkdirs()) throw new IllegalStateException("cannot create images folder");
|
if (!cachePath.mkdirs()) throw new IllegalStateException("cannot create images folder");
|
||||||
File png = new File(cachePath, "QR.png");
|
File png = new File(cachePath, "QR.png");
|
||||||
@@ -452,7 +446,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
.withEndAction(resetSize).start();
|
.withEndAction(resetSize).start();
|
||||||
}
|
}
|
||||||
subaddress = newSubaddress;
|
subaddress = newSubaddress;
|
||||||
final Context context = getContext();
|
final Context context = requireContext();
|
||||||
Spanned label = Html.fromHtml(context.getString(R.string.receive_subaddress,
|
Spanned label = Html.fromHtml(context.getString(R.string.receive_subaddress,
|
||||||
Integer.toHexString(ThemeHelper.getThemedColor(context, R.attr.positiveColor) & 0xFFFFFF),
|
Integer.toHexString(ThemeHelper.getThemedColor(context, R.attr.positiveColor) & 0xFFFFFF),
|
||||||
Integer.toHexString(ThemeHelper.getThemedColor(context, android.R.attr.colorBackground) & 0xFFFFFF),
|
Integer.toHexString(ThemeHelper.getThemedColor(context, android.R.attr.colorBackground) & 0xFFFFFF),
|
||||||
|
@@ -24,6 +24,7 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import com.google.zxing.BarcodeFormat;
|
import com.google.zxing.BarcodeFormat;
|
||||||
@@ -43,7 +44,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
|
|||||||
private ZXingScannerView mScannerView;
|
private ZXingScannerView mScannerView;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
Timber.d("onCreateView");
|
Timber.d("onCreateView");
|
||||||
mScannerView = new ZXingScannerView(getActivity());
|
mScannerView = new ZXingScannerView(getActivity());
|
||||||
return mScannerView;
|
return mScannerView;
|
||||||
@@ -85,7 +86,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof OnScannedListener) {
|
if (context instanceof OnScannedListener) {
|
||||||
this.onScannedListener = (OnScannedListener) context;
|
this.onScannedListener = (OnScannedListener) context;
|
||||||
|
@@ -5,6 +5,7 @@ import android.content.Context;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.StyleRes;
|
import androidx.annotation.StyleRes;
|
||||||
import androidx.preference.ListPreference;
|
import androidx.preference.ListPreference;
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
@@ -60,7 +61,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
private SettingsFragment.Listener activity;
|
private SettingsFragment.Listener activity;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof SettingsFragment.Listener) {
|
if (context instanceof SettingsFragment.Listener) {
|
||||||
activity = (SettingsFragment.Listener) context;
|
activity = (SettingsFragment.Listener) context;
|
||||||
|
@@ -20,6 +20,7 @@ import android.bluetooth.BluetoothAdapter;
|
|||||||
import android.bluetooth.BluetoothClass;
|
import android.bluetooth.BluetoothClass;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
@@ -29,6 +30,7 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
@@ -73,6 +75,8 @@ public class SidekickConnectFragment extends Fragment
|
|||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
Timber.d("onPause()");
|
Timber.d("onPause()");
|
||||||
|
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)
|
||||||
|
throw new IllegalStateException("Bluetooth permission not granted");
|
||||||
if (bluetoothAdapter.isDiscovering()) {
|
if (bluetoothAdapter.isDiscovering()) {
|
||||||
bluetoothAdapter.cancelDiscovery();
|
bluetoothAdapter.cancelDiscovery();
|
||||||
}
|
}
|
||||||
@@ -112,6 +116,8 @@ public class SidekickConnectFragment extends Fragment
|
|||||||
|
|
||||||
private void populateList() {
|
private void populateList() {
|
||||||
List<BluetoothInfo> items = new ArrayList<>();
|
List<BluetoothInfo> items = new ArrayList<>();
|
||||||
|
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)
|
||||||
|
throw new IllegalStateException("Bluetooth permission not granted");
|
||||||
for (BluetoothDevice device : bluetoothAdapter.getBondedDevices()) {
|
for (BluetoothDevice device : bluetoothAdapter.getBondedDevices()) {
|
||||||
final int deviceCLass = device.getBluetoothClass().getDeviceClass();
|
final int deviceCLass = device.getBluetoothClass().getDeviceClass();
|
||||||
switch (deviceCLass) {
|
switch (deviceCLass) {
|
||||||
@@ -152,6 +158,8 @@ public class SidekickConnectFragment extends Fragment
|
|||||||
|
|
||||||
// Make sure we're not doing discovery anymore
|
// Make sure we're not doing discovery anymore
|
||||||
if (bluetoothAdapter != null) {
|
if (bluetoothAdapter != null) {
|
||||||
|
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)
|
||||||
|
throw new IllegalStateException("Bluetooth permission not granted");
|
||||||
bluetoothAdapter.cancelDiscovery();
|
bluetoothAdapter.cancelDiscovery();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,6 +167,8 @@ public class SidekickConnectFragment extends Fragment
|
|||||||
@Override
|
@Override
|
||||||
public void onInteraction(final View view, final BluetoothInfo item) {
|
public void onInteraction(final View view, final BluetoothInfo item) {
|
||||||
Timber.d("onInteraction %s", item);
|
Timber.d("onInteraction %s", item);
|
||||||
|
if (ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)
|
||||||
|
throw new IllegalStateException("Bluetooth permission not granted");
|
||||||
bluetoothAdapter.cancelDiscovery();
|
bluetoothAdapter.cancelDiscovery();
|
||||||
|
|
||||||
final BluetoothFragment btFragment = (BluetoothFragment) getChildFragmentManager().findFragmentById(R.id.bt_fragment);
|
final BluetoothFragment btFragment = (BluetoothFragment) getChildFragmentManager().findFragmentById(R.id.bt_fragment);
|
||||||
|
@@ -226,7 +226,7 @@ public class SubaddressFragment extends Fragment implements SubaddressInfoAdapte
|
|||||||
|
|
||||||
// Callbacks from SubaddressInfoAdapter
|
// Callbacks from SubaddressInfoAdapter
|
||||||
@Override
|
@Override
|
||||||
public void onInteraction(final View view, final Subaddress subaddress) {
|
public void onInteraction(final View view, @NonNull final Subaddress subaddress) {
|
||||||
if (managerMode)
|
if (managerMode)
|
||||||
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
|
activityCallback.showSubaddress(view, subaddress.getAddressIndex());
|
||||||
else
|
else
|
||||||
|
@@ -52,7 +52,6 @@ public class SubaddressInfoFragment extends Fragment
|
|||||||
private Subaddress subaddress;
|
private Subaddress subaddress;
|
||||||
|
|
||||||
private TextInputLayout etName;
|
private TextInputLayout etName;
|
||||||
private TextView tvAddress;
|
|
||||||
private TextView tvTxLabel;
|
private TextView tvTxLabel;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -61,7 +60,6 @@ public class SubaddressInfoFragment extends Fragment
|
|||||||
View view = inflater.inflate(R.layout.fragment_subaddressinfo, container, false);
|
View view = inflater.inflate(R.layout.fragment_subaddressinfo, container, false);
|
||||||
|
|
||||||
etName = view.findViewById(R.id.etName);
|
etName = view.findViewById(R.id.etName);
|
||||||
tvAddress = view.findViewById(R.id.tvAddress);
|
|
||||||
tvTxLabel = view.findViewById(R.id.tvTxLabel);
|
tvTxLabel = view.findViewById(R.id.tvTxLabel);
|
||||||
|
|
||||||
final RecyclerView list = view.findViewById(R.id.list);
|
final RecyclerView list = view.findViewById(R.id.list);
|
||||||
@@ -71,11 +69,13 @@ public class SubaddressInfoFragment extends Fragment
|
|||||||
final Wallet wallet = activityCallback.getWallet();
|
final Wallet wallet = activityCallback.getWallet();
|
||||||
|
|
||||||
Bundle b = getArguments();
|
Bundle b = getArguments();
|
||||||
|
assert b != null;
|
||||||
final int subaddressIndex = b.getInt("subaddressIndex");
|
final int subaddressIndex = b.getInt("subaddressIndex");
|
||||||
subaddress = wallet.getSubaddressObject(subaddressIndex);
|
subaddress = wallet.getSubaddressObject(subaddressIndex);
|
||||||
|
|
||||||
etName.getEditText().setText(subaddress.getDisplayLabel());
|
etName.getEditText().setText(subaddress.getDisplayLabel());
|
||||||
tvAddress.setText(getContext().getString(R.string.subbaddress_info_subtitle,
|
final TextView tvAddress = view.findViewById(R.id.tvAddress);
|
||||||
|
tvAddress.setText(requireContext().getString(R.string.subbaddress_info_subtitle,
|
||||||
subaddress.getAddressIndex(), subaddress.getAddress()));
|
subaddress.getAddressIndex(), subaddress.getAddress()));
|
||||||
|
|
||||||
etName.getEditText().setOnFocusChangeListener((v, hasFocus) -> {
|
etName.getEditText().setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
|
@@ -20,7 +20,6 @@ import android.annotation.SuppressLint;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
@@ -34,6 +33,7 @@ import android.widget.ImageView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.transition.Transition;
|
import androidx.transition.Transition;
|
||||||
@@ -44,6 +44,8 @@ import com.m2049r.xmrwallet.data.UserNotes;
|
|||||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||||
import com.m2049r.xmrwallet.model.Transfer;
|
import com.m2049r.xmrwallet.model.Transfer;
|
||||||
import com.m2049r.xmrwallet.model.Wallet;
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
|
import com.m2049r.xmrwallet.service.shift.ShiftService;
|
||||||
|
import com.m2049r.xmrwallet.service.shift.api.ShiftApi;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
@@ -61,6 +63,7 @@ public class TxFragment extends Fragment {
|
|||||||
|
|
||||||
static public final String ARG_INFO = "info";
|
static public final String ARG_INFO = "info";
|
||||||
|
|
||||||
|
@SuppressLint("SimpleDateFormat")
|
||||||
private final SimpleDateFormat TS_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
|
private final SimpleDateFormat TS_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
|
||||||
|
|
||||||
public TxFragment() {
|
public TxFragment() {
|
||||||
@@ -106,6 +109,7 @@ public class TxFragment extends Fragment {
|
|||||||
tvTxAmountBtc = view.findViewById(R.id.tvTxAmountBtc);
|
tvTxAmountBtc = view.findViewById(R.id.tvTxAmountBtc);
|
||||||
tvXmrToSupport = view.findViewById(R.id.tvXmrToSupport);
|
tvXmrToSupport = view.findViewById(R.id.tvXmrToSupport);
|
||||||
tvXmrToKeyLabel = view.findViewById(R.id.tvXmrToKeyLabel);
|
tvXmrToKeyLabel = view.findViewById(R.id.tvXmrToKeyLabel);
|
||||||
|
|
||||||
tvXmrToLogo = view.findViewById(R.id.tvXmrToLogo);
|
tvXmrToLogo = view.findViewById(R.id.tvXmrToLogo);
|
||||||
|
|
||||||
tvAccount = view.findViewById(R.id.tvAccount);
|
tvAccount = view.findViewById(R.id.tvAccount);
|
||||||
@@ -127,18 +131,20 @@ public class TxFragment extends Fragment {
|
|||||||
tvWarning = view.findViewById(R.id.tvWarning);
|
tvWarning = view.findViewById(R.id.tvWarning);
|
||||||
|
|
||||||
tvTxXmrToKey.setOnClickListener(v -> {
|
tvTxXmrToKey.setOnClickListener(v -> {
|
||||||
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
|
Helper.clipBoardCopy(requireActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
|
||||||
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
|
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
info = getArguments().getParcelable(ARG_INFO);
|
final Bundle args = getArguments();
|
||||||
|
assert args != null;
|
||||||
|
info = args.getParcelable(ARG_INFO);
|
||||||
show();
|
show();
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
void shareTxInfo() {
|
void shareTxInfo() {
|
||||||
if (this.info == null) return;
|
if (this.info == null) return;
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
sb.append(getString(R.string.tx_timestamp)).append(":\n");
|
sb.append(getString(R.string.tx_timestamp)).append(":\n");
|
||||||
sb.append(TS_FORMATTER.format(new Date(info.timestamp * 1000))).append("\n\n");
|
sb.append(TS_FORMATTER.format(new Date(info.timestamp * 1000))).append("\n\n");
|
||||||
@@ -216,7 +222,7 @@ public class TxFragment extends Fragment {
|
|||||||
|
|
||||||
private void showSubaddressLabel() {
|
private void showSubaddressLabel() {
|
||||||
final Subaddress subaddress = activityCallback.getWalletSubaddress(info.accountIndex, info.addressIndex);
|
final Subaddress subaddress = activityCallback.getWalletSubaddress(info.accountIndex, info.addressIndex);
|
||||||
final Context ctx = getContext();
|
final Context ctx = requireContext();
|
||||||
Spanned label = Html.fromHtml(ctx.getString(R.string.tx_account_formatted,
|
Spanned label = Html.fromHtml(ctx.getString(R.string.tx_account_formatted,
|
||||||
info.accountIndex, info.addressIndex,
|
info.accountIndex, info.addressIndex,
|
||||||
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
|
Integer.toHexString(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor) & 0xFFFFFF),
|
||||||
@@ -264,16 +270,17 @@ public class TxFragment extends Fragment {
|
|||||||
tvTxFee.setVisibility(View.GONE);
|
tvTxFee.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Context ctx = requireContext();
|
||||||
if (info.isFailed) {
|
if (info.isFailed) {
|
||||||
tvTxAmount.setText(getString(R.string.tx_list_amount_failed, Wallet.getDisplayAmount(info.amount)));
|
tvTxAmount.setText(getString(R.string.tx_list_amount_failed, Wallet.getDisplayAmount(info.amount)));
|
||||||
tvTxFee.setText(getString(R.string.tx_list_failed_text));
|
tvTxFee.setText(getString(R.string.tx_list_failed_text));
|
||||||
setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.neutralColor));
|
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.neutralColor));
|
||||||
} else if (info.isPending) {
|
} else if (info.isPending) {
|
||||||
setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.neutralColor));
|
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.neutralColor));
|
||||||
} else if (info.direction == TransactionInfo.Direction.Direction_In) {
|
} else if (info.direction == TransactionInfo.Direction.Direction_In) {
|
||||||
setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.positiveColor));
|
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.positiveColor));
|
||||||
} else {
|
} else {
|
||||||
setTxColour(ThemeHelper.getThemedColor(getContext(), R.attr.negativeColor));
|
setTxColour(ThemeHelper.getThemedColor(ctx, R.attr.negativeColor));
|
||||||
}
|
}
|
||||||
Set<String> destinations = new HashSet<>();
|
Set<String> destinations = new HashSet<>();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
@@ -328,31 +335,29 @@ public class TxFragment extends Fragment {
|
|||||||
void showBtcInfo() {
|
void showBtcInfo() {
|
||||||
if (userNotes.xmrtoKey != null) {
|
if (userNotes.xmrtoKey != null) {
|
||||||
cvXmrTo.setVisibility(View.VISIBLE);
|
cvXmrTo.setVisibility(View.VISIBLE);
|
||||||
String key = userNotes.xmrtoKey;
|
|
||||||
if ("xmrto".equals(userNotes.xmrtoTag)) { // legacy xmr.to service :(
|
ShiftService service = ShiftService.findWithTag(userNotes.xmrtoTag);
|
||||||
key = "xmrto-" + key;
|
tvXmrToKeyLabel.setText(getString(R.string.label_send_btc_xmrto_key_lb, service.getLabel()));
|
||||||
}
|
if (service.getIconId() == 0)
|
||||||
tvTxXmrToKey.setText(key);
|
tvXmrToLogo.setVisibility(View.GONE);
|
||||||
|
else
|
||||||
|
tvXmrToLogo.setImageResource(service.getLogoId());
|
||||||
|
|
||||||
|
tvTxXmrToKey.setText(userNotes.xmrtoKey);
|
||||||
|
|
||||||
tvDestinationBtc.setText(userNotes.xmrtoDestination);
|
tvDestinationBtc.setText(userNotes.xmrtoDestination);
|
||||||
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " " + userNotes.xmrtoCurrency);
|
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " " + userNotes.xmrtoCurrency);
|
||||||
switch (userNotes.xmrtoTag) {
|
|
||||||
case "xmrto":
|
ShiftApi shiftApi = service.getShiftApi();
|
||||||
tvXmrToSupport.setVisibility(View.GONE);
|
if (shiftApi != null) {
|
||||||
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
|
tvXmrToSupport.setText(getString(R.string.label_send_btc_xmrto_info, service.getLabel()));
|
||||||
tvXmrToLogo.setImageResource(R.drawable.ic_xmrto_logo);
|
tvXmrToSupport.setPaintFlags(tvXmrToSupport.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
||||||
break;
|
tvXmrToSupport.setOnClickListener(v -> {
|
||||||
case "side": // defaults in layout - just add underline
|
startActivity(new Intent(Intent.ACTION_VIEW, shiftApi.getQueryOrderUri(userNotes.xmrtoKey)));
|
||||||
tvXmrToSupport.setPaintFlags(tvXmrToSupport.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
});
|
||||||
tvXmrToSupport.setOnClickListener(v -> {
|
} else {
|
||||||
Uri uri = Uri.parse("https://sideshift.ai/orders/" + userNotes.xmrtoKey);
|
tvXmrToSupport.setVisibility(View.GONE);
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
tvXmrToSupport.setVisibility(View.GONE);
|
|
||||||
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
|
|
||||||
tvXmrToLogo.setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cvXmrTo.setVisibility(View.GONE);
|
cvXmrTo.setVisibility(View.GONE);
|
||||||
@@ -369,7 +374,7 @@ public class TxFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.tx_info_menu, menu);
|
inflater.inflate(R.menu.tx_info_menu, menu);
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
}
|
}
|
||||||
@@ -397,7 +402,7 @@ public class TxFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof TxFragment.Listener) {
|
if (context instanceof TxFragment.Listener) {
|
||||||
this.activityCallback = (TxFragment.Listener) context;
|
this.activityCallback = (TxFragment.Listener) context;
|
||||||
|
@@ -38,7 +38,6 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.appcompat.app.ActionBarDrawerToggle;
|
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.core.view.GravityCompat;
|
import androidx.core.view.GravityCompat;
|
||||||
@@ -348,7 +347,10 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
|||||||
}
|
}
|
||||||
|
|
||||||
Bundle extras = getIntent().getExtras();
|
Bundle extras = getIntent().getExtras();
|
||||||
if (extras == null) finish(); // we need extras!
|
if (extras == null) {
|
||||||
|
finish(); // we need extras!
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String walletId = extras.getString(REQUEST_ID);
|
String walletId = extras.getString(REQUEST_ID);
|
||||||
requestStreetMode = extras.getBoolean(REQUEST_STREETMODE);
|
requestStreetMode = extras.getBoolean(REQUEST_STREETMODE);
|
||||||
@@ -1179,7 +1181,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSubaddressSelected(@Nullable final Subaddress subaddress) {
|
public void onSubaddressSelected(@NonNull final Subaddress subaddress) {
|
||||||
selectedSubaddressIndex = subaddress.getAddressIndex();
|
selectedSubaddressIndex = subaddress.getAddressIndex();
|
||||||
getOnBackPressedDispatcher().onBackPressed();
|
getOnBackPressedDispatcher().onBackPressed();
|
||||||
}
|
}
|
||||||
|
@@ -112,7 +112,7 @@ public class WalletFragment extends Fragment
|
|||||||
flExchange = view.findViewById(R.id.flExchange);
|
flExchange = view.findViewById(R.id.flExchange);
|
||||||
((ProgressBar) view.findViewById(R.id.pbExchange)).getIndeterminateDrawable().
|
((ProgressBar) view.findViewById(R.id.pbExchange)).getIndeterminateDrawable().
|
||||||
setColorFilter(
|
setColorFilter(
|
||||||
ThemeHelper.getThemedColor(getContext(), com.google.android.material.R.attr.colorPrimaryVariant),
|
ThemeHelper.getThemedColor(requireContext(), com.google.android.material.R.attr.colorPrimaryVariant),
|
||||||
android.graphics.PorterDuff.Mode.MULTIPLY);
|
android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||||
|
|
||||||
tvProgress = view.findViewById(R.id.tvProgress);
|
tvProgress = view.findViewById(R.id.tvProgress);
|
||||||
|
@@ -23,7 +23,6 @@ import android.os.Build;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.BuildConfig;
|
|
||||||
import com.m2049r.xmrwallet.model.NetworkType;
|
import com.m2049r.xmrwallet.model.NetworkType;
|
||||||
import com.m2049r.xmrwallet.util.LocaleHelper;
|
import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||||
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
||||||
|
@@ -24,43 +24,38 @@ import java.net.URI;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
@Getter
|
||||||
@ToString
|
@ToString
|
||||||
public class BarcodeData {
|
public class BarcodeData {
|
||||||
|
|
||||||
public enum Security {
|
public enum Security {
|
||||||
NORMAL,
|
NORMAL,
|
||||||
OA_NO_DNSSEC,
|
OA_NO_DNSSEC,
|
||||||
OA_DNSSEC
|
OA_DNSSEC
|
||||||
}
|
}
|
||||||
|
|
||||||
final public Crypto asset;
|
final private Set<Crypto> possibleAssets = new HashSet<>();
|
||||||
final public List<Crypto> ambiguousAssets;
|
private String address = null;
|
||||||
final public String address;
|
private String addressName = null;
|
||||||
final public String addressName;
|
private String amount = null;
|
||||||
final public String amount;
|
private String description = null;
|
||||||
final public String description;
|
private Security security = null;
|
||||||
final public Security security;
|
|
||||||
|
|
||||||
public BarcodeData(List<Crypto> assets, String address) {
|
public BarcodeData(List<Crypto> assets, String address) {
|
||||||
if (assets.isEmpty())
|
if (assets.isEmpty())
|
||||||
throw new IllegalArgumentException("no assets specified");
|
throw new IllegalArgumentException("no assets specified");
|
||||||
this.addressName = null;
|
security = Security.NORMAL;
|
||||||
this.description = null;
|
|
||||||
this.amount = null;
|
|
||||||
this.security = Security.NORMAL;
|
|
||||||
this.address = address;
|
this.address = address;
|
||||||
if (assets.size() == 1) {
|
possibleAssets.addAll(assets);
|
||||||
this.asset = assets.get(0);
|
|
||||||
this.ambiguousAssets = null;
|
|
||||||
} else {
|
|
||||||
this.asset = null;
|
|
||||||
this.ambiguousAssets = assets;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BarcodeData(Crypto asset, String address, String description, String amount) {
|
public BarcodeData(Crypto asset, String address, String description, String amount) {
|
||||||
@@ -68,8 +63,7 @@ public class BarcodeData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BarcodeData(Crypto asset, String address, String addressName, String description, String amount, Security security) {
|
public BarcodeData(Crypto asset, String address, String addressName, String description, String amount, Security security) {
|
||||||
this.ambiguousAssets = null;
|
possibleAssets.add(asset);
|
||||||
this.asset = asset;
|
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.addressName = addressName;
|
this.addressName = addressName;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
@@ -82,7 +76,7 @@ public class BarcodeData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getUriString() {
|
public String getUriString() {
|
||||||
if (asset != Crypto.XMR) throw new IllegalStateException("We can only do XMR stuff!");
|
if (getAsset() != Crypto.XMR) throw new IllegalStateException("We can only do XMR stuff!");
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(Crypto.XMR.getUriScheme())
|
sb.append(Crypto.XMR.getUriScheme())
|
||||||
.append(':')
|
.append(':')
|
||||||
@@ -227,6 +221,20 @@ public class BarcodeData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAmbiguous() {
|
public boolean isAmbiguous() {
|
||||||
return ambiguousAssets != null;
|
return possibleAssets.size() > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Crypto getAsset() {
|
||||||
|
if (possibleAssets.size() == 1) {
|
||||||
|
return possibleAssets.iterator().next();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return true if we still have possible assets
|
||||||
|
public boolean filter(Set<Crypto> assets) {
|
||||||
|
possibleAssets.retainAll(assets);
|
||||||
|
return !possibleAssets.isEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 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.data;
|
package com.m2049r.xmrwallet.data;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -5,35 +21,30 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import com.m2049r.xmrwallet.R;
|
import com.m2049r.xmrwallet.R;
|
||||||
import com.m2049r.xmrwallet.model.Wallet;
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
import com.m2049r.xmrwallet.util.validator.BitcoinAddressType;
|
|
||||||
import com.m2049r.xmrwallet.util.validator.BitcoinAddressValidator;
|
import java.util.regex.Pattern;
|
||||||
import com.m2049r.xmrwallet.util.validator.EthAddressValidator;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public enum Crypto {
|
public enum Crypto {
|
||||||
XMR("XMR", true, "monero:tx_amount:recipient_name:tx_description", R.id.ibXMR, R.drawable.ic_monero, R.drawable.ic_monero_bw, Wallet::isAddressValid),
|
XMR("XMR", "XMR", "XMR", "monero:tx_amount:recipient_name:tx_description", R.id.ibXMR, R.drawable.ic_monero, R.drawable.ic_monero_bw, Pattern.compile("^[48][a-zA-Z|\\d]{94}([a-zA-Z|\\d]{11})?$")),
|
||||||
BTC("BTC", true, "bitcoin:amount:label:message", R.id.ibBTC, R.drawable.ic_xmrto_btc, R.drawable.ic_xmrto_btc_off, address -> {
|
BTC("BTC", "BTC", "BTC", "bitcoin:amount:label:message", R.id.ibBTC, R.drawable.ic_xmrto_btc, R.drawable.ic_xmrto_btc_off, Pattern.compile("^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$|^(bc1q)|(bc1p)[0-9A-Za-z]{37,62}$")),
|
||||||
return BitcoinAddressValidator.validate(address, BitcoinAddressType.BTC);
|
LTC("LTC", "LTC", "LTC", "litecoin:amount:label:message", R.id.ibLTC, R.drawable.ic_xmrto_ltc, R.drawable.ic_xmrto_ltc_off, Pattern.compile("^([LM3])[A-Za-z0-9]{33}$|^(ltc1)[0-9A-Za-z]{39}$")),
|
||||||
}),
|
ETH("ETH", "ETH", "ETH", "ethereum:amount:label:message", R.id.ibETH, R.drawable.ic_xmrto_eth, R.drawable.ic_xmrto_eth_off, Pattern.compile("^(0x)[0-9A-Fa-f]{40}$")),
|
||||||
DASH("DASH", true, "dash:amount:label:message", R.id.ibDASH, R.drawable.ic_xmrto_dash, R.drawable.ic_xmrto_dash_off, address -> {
|
USDT("USDT", "TRX", "USDT(TRC20)", "usdt:amount:label:message", R.id.ibUSDT, R.drawable.ic_xmrto_usdt_trc20, R.drawable.ic_xmrto_usdt_trc20_off, Pattern.compile("^T[1-9A-HJ-NP-Za-km-z]{33}$")),
|
||||||
return BitcoinAddressValidator.validate(address, BitcoinAddressType.DASH);
|
SOLANA("SOL", "SOL", "SOL", "solana:amount:label:message", R.id.ibSOL, R.drawable.ic_xmrto_sol, R.drawable.ic_xmrto_sol_off, Pattern.compile("^[1-9A-HJ-NP-Za-km-z]{32,44}$"));
|
||||||
}),
|
|
||||||
DOGE("DOGE", true, "dogecoin:amount:label:message", R.id.ibDOGE, R.drawable.ic_xmrto_doge, R.drawable.ic_xmrto_doge_off, address -> {
|
|
||||||
return BitcoinAddressValidator.validate(address, BitcoinAddressType.DOGE);
|
|
||||||
}),
|
|
||||||
ETH("ETH", false, "ethereum:amount:label:message", R.id.ibETH, R.drawable.ic_xmrto_eth, R.drawable.ic_xmrto_eth_off, EthAddressValidator::validate),
|
|
||||||
LTC("LTC", true, "litecoin:amount:label:message", R.id.ibLTC, R.drawable.ic_xmrto_ltc, R.drawable.ic_xmrto_ltc_off, address -> {
|
|
||||||
return BitcoinAddressValidator.validate(address, BitcoinAddressType.LTC);
|
|
||||||
});
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@NonNull
|
@NonNull
|
||||||
private final String symbol;
|
private final String symbol;
|
||||||
@Getter
|
@Getter
|
||||||
private final boolean casefull;
|
@NonNull
|
||||||
|
private final String network;
|
||||||
|
@Getter
|
||||||
|
@NonNull
|
||||||
|
private final String label;
|
||||||
@NonNull
|
@NonNull
|
||||||
private final String uriSpec;
|
private final String uriSpec;
|
||||||
@Getter
|
@Getter
|
||||||
@@ -43,7 +54,7 @@ public enum Crypto {
|
|||||||
@Getter
|
@Getter
|
||||||
private final int iconDisabledId;
|
private final int iconDisabledId;
|
||||||
@NonNull
|
@NonNull
|
||||||
private final Validator validator;
|
private final Pattern regex;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Crypto withScheme(@NonNull String scheme) {
|
public static Crypto withScheme(@NonNull String scheme) {
|
||||||
@@ -62,10 +73,6 @@ public enum Crypto {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Validator {
|
|
||||||
boolean validate(String address);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO maybe cache these segments
|
// TODO maybe cache these segments
|
||||||
String getUriScheme() {
|
String getUriScheme() {
|
||||||
return uriSpec.split(":")[0];
|
return uriSpec.split(":")[0];
|
||||||
@@ -83,7 +90,8 @@ public enum Crypto {
|
|||||||
return uriSpec.split(":")[3];
|
return uriSpec.split(":")[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean validate(String address) {
|
public boolean validate(String address) {
|
||||||
return validator.validate(address);
|
if (this == XMR) return Wallet.isAddressValid(address);
|
||||||
|
return regex.matcher(address).find();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 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.data;
|
||||||
|
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
public class CryptoAmount {
|
||||||
|
Crypto crypto;
|
||||||
|
double amount;
|
||||||
|
|
||||||
|
public CryptoAmount newWithAmount(double amount) {
|
||||||
|
return new CryptoAmount(this.crypto, amount);
|
||||||
|
}
|
||||||
|
}
|
@@ -19,23 +19,26 @@ package com.m2049r.xmrwallet.data;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
// Nodes stolen from https://moneroworld.com/#nodes
|
@Getter
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public enum DefaultNodes {
|
public enum DefaultNodes {
|
||||||
MONERUJO("nodex.monerujo.io:18081"),
|
AGORIST("xmr.agor.ist:18089/mainnet/agor.ist"),
|
||||||
XMRTO("node.xmr.to:18081"),
|
BOLDSUCK("xmr-de.boldsuck.org:18080/mainnet/boldsuck.org"),
|
||||||
SUPPORTXMR("node.supportxmr.com:18081"),
|
|
||||||
HASHVAULT("nodes.hashvault.pro:18081"),
|
|
||||||
MONEROWORLD("node.moneroworld.com:18089"),
|
|
||||||
XMRTW("opennode.xmr-tw.org:18089"),
|
|
||||||
ds_jetzt("monero.ds-jetzt.de:18089"),
|
|
||||||
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"),
|
boldsuck("6dsdenp6vjkvqzy4wzsnzn6wixkdzihx3khiumyzieauxuxslmcaeiad.onion:18081/mainnet/boldsuck.onion"),
|
||||||
ds_jetzt_onion("qvlr4w7yhnjrdg3txa72jwtpnjn4ezsrivzvocbnvpfbdo342fahhoad.onion:18089/mainnet/ds-jetzt.onion");
|
CAKE("xmr-node.cakewallet.com:18081/mainnet/cakewallet.com"),
|
||||||
|
DS_JETZT("monero.ds-jetzt.de:18089/mainnet/ds-jetzt.de"),
|
||||||
|
ds_jetzt("qvlr4w7yhnjrdg3txa72jwtpnjn4ezsrivzvocbnvpfbdo342fahhoad.onion:18089/mainnet/ds-jetzt.onion"),
|
||||||
|
MONERODEVS("node.monerodevs.org:18089/mainnet/monerodevs.org"),
|
||||||
|
MONERUJO("nodex.monerujo.io:18081/mainnet/monerujo.io"),
|
||||||
|
monerujo("monerujods7mbghwe6cobdr6ujih6c22zu5rl7zshmizz2udf7v7fsad.onion:18081/mainnet/monerujo.onion"),
|
||||||
|
SETH("node.sethforprivacy.com:18089/mainnet/sethforprivacy.com"),
|
||||||
|
seth("sfpp2p7wnfjv3lrvfan4jmmkvhnbsbimpa3cqyuf7nt6zd24xhcqcsyd.onion/mainnet/sethforprivacy.onion"),
|
||||||
|
STACK("monero.stackwallet.com:18081/mainnet/stackwallet.com"),
|
||||||
|
STORMYCLOUD("xmr.stormycloud.org:18089/mainnet/stormycloud.org"),
|
||||||
|
TENZ("monero.10z.com.ar:18089/mainnet/10z.com.ar"),
|
||||||
|
XMRROCKS("node.xmr.rocks:18089/mainnet/xmr.rocks"),
|
||||||
|
xmrrocks("xqnnz2xmlmtpy2p4cm4cphg2elkwu5oob7b7so5v4wwgt44p6vbx5ryd.onion/mainnet/xmr.rocks.onion"),
|
||||||
|
XMRTW("opennode.xmr-tw.org:18089/mainnet/xmr-tw.org");
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final String uri;
|
private final String uri;
|
||||||
}
|
}
|
||||||
|
@@ -144,7 +144,7 @@ public class Node {
|
|||||||
if ((nodeString == null) || nodeString.isEmpty())
|
if ((nodeString == null) || nodeString.isEmpty())
|
||||||
throw new IllegalArgumentException("daemon is empty");
|
throw new IllegalArgumentException("daemon is empty");
|
||||||
String daemonAddress;
|
String daemonAddress;
|
||||||
String a[] = nodeString.split("@");
|
String[] a = nodeString.split("@");
|
||||||
if (a.length == 1) { // no credentials
|
if (a.length == 1) { // no credentials
|
||||||
daemonAddress = a[0];
|
daemonAddress = a[0];
|
||||||
username = "";
|
username = "";
|
||||||
@@ -169,7 +169,7 @@ public class Node {
|
|||||||
throw new IllegalArgumentException("Too many '/' or too few");
|
throw new IllegalArgumentException("Too many '/' or too few");
|
||||||
|
|
||||||
daemonAddress = daParts[0];
|
daemonAddress = daParts[0];
|
||||||
String da[] = daemonAddress.split(":");
|
String[] da = daemonAddress.split(":");
|
||||||
if ((da.length > 2) || (da.length < 1))
|
if ((da.length > 2) || (da.length < 1))
|
||||||
throw new IllegalArgumentException("Too many ':' or too few");
|
throw new IllegalArgumentException("Too many ':' or too few");
|
||||||
String host = da[0];
|
String host = da[0];
|
||||||
|
@@ -201,8 +201,13 @@ public class NodeInfo extends Node {
|
|||||||
.port(port)
|
.port(port)
|
||||||
.addPathSegment("json_rpc")
|
.addPathSegment("json_rpc")
|
||||||
.build();
|
.build();
|
||||||
final String json = "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"getlastblockheader\"}";
|
|
||||||
return new Request(url, json, getUsername(), getPassword());
|
try {
|
||||||
|
final JSONObject json = new JSONObject("{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"getlastblockheader\"}");
|
||||||
|
return new Request(url, json, getUsername(), getPassword());
|
||||||
|
} catch (JSONException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean testRpcService(int port) {
|
private boolean testRpcService(int port) {
|
||||||
|
@@ -20,22 +20,21 @@ import android.os.Parcel;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.service.shift.ShiftService;
|
||||||
|
import com.m2049r.xmrwallet.service.shift.api.RequestQuote;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
public class TxDataBtc extends TxData {
|
public class TxDataBtc extends TxData {
|
||||||
@Getter
|
private ShiftService shiftService;
|
||||||
@Setter
|
|
||||||
private String btcSymbol; // the actual non-XMR thing we're sending
|
private String btcSymbol; // the actual non-XMR thing we're sending
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
private String xmrtoOrderId; // shown in success screen
|
private String xmrtoOrderId; // shown in success screen
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
private String btcAddress;
|
private String btcAddress;
|
||||||
@Getter
|
private CryptoAmount shiftAmount; // what we want to send
|
||||||
@Setter
|
private String xmrtoQueryOrderToken; // used for queryOrder API
|
||||||
private double btcAmount;
|
|
||||||
|
|
||||||
public TxDataBtc() {
|
public TxDataBtc() {
|
||||||
super();
|
super();
|
||||||
@@ -47,7 +46,9 @@ public class TxDataBtc extends TxData {
|
|||||||
out.writeString(btcSymbol);
|
out.writeString(btcSymbol);
|
||||||
out.writeString(xmrtoOrderId);
|
out.writeString(xmrtoOrderId);
|
||||||
out.writeString(btcAddress);
|
out.writeString(btcAddress);
|
||||||
out.writeDouble(btcAmount);
|
out.writeString(shiftAmount.getCrypto().name());
|
||||||
|
out.writeDouble(shiftAmount.getAmount());
|
||||||
|
out.writeString(xmrtoQueryOrderToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is used to regenerate your object. All Parcelables must have a CREATOR that implements these two methods
|
// this is used to regenerate your object. All Parcelables must have a CREATOR that implements these two methods
|
||||||
@@ -66,7 +67,8 @@ public class TxDataBtc extends TxData {
|
|||||||
btcSymbol = in.readString();
|
btcSymbol = in.readString();
|
||||||
xmrtoOrderId = in.readString();
|
xmrtoOrderId = in.readString();
|
||||||
btcAddress = in.readString();
|
btcAddress = in.readString();
|
||||||
btcAmount = in.readDouble();
|
shiftAmount = new CryptoAmount(Crypto.valueOf(in.readString()), in.readDouble());
|
||||||
|
xmrtoQueryOrderToken = in.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -79,19 +81,33 @@ public class TxDataBtc extends TxData {
|
|||||||
sb.append(btcSymbol);
|
sb.append(btcSymbol);
|
||||||
sb.append(",btcAddress:");
|
sb.append(",btcAddress:");
|
||||||
sb.append(btcAddress);
|
sb.append(btcAddress);
|
||||||
sb.append(",btcAmount:");
|
sb.append(",amount:");
|
||||||
sb.append(btcAmount);
|
sb.append(shiftAmount);
|
||||||
|
sb.append(",xmrtoQueryOrderToken:");
|
||||||
|
sb.append(xmrtoQueryOrderToken);
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean validateAddress(@NonNull String address) {
|
public boolean validateAddress(@NonNull String address) {
|
||||||
if ((btcSymbol == null) || (btcAddress == null)) return false;
|
|
||||||
final Crypto crypto = Crypto.withSymbol(btcSymbol);
|
final Crypto crypto = Crypto.withSymbol(btcSymbol);
|
||||||
if (crypto == null) return false;
|
if (crypto == null) return false;
|
||||||
if (crypto.isCasefull()) { // compare as-is
|
return address.equalsIgnoreCase(btcAddress);
|
||||||
return address.equals(btcAddress);
|
}
|
||||||
} else { // normalize & compare (e.g. ETH with and without checksum capitals
|
|
||||||
return address.toLowerCase().equals(btcAddress.toLowerCase());
|
public double getBtcAmount() {
|
||||||
|
return (shiftAmount.getCrypto() == Crypto.XMR) ? 0 : shiftAmount.getAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getXmrAmount() {
|
||||||
|
return (shiftAmount.getCrypto() == Crypto.XMR) ? shiftAmount.getAmount() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validate(RequestQuote quote) {
|
||||||
|
if (shiftAmount.getCrypto() == Crypto.XMR) {
|
||||||
|
return (quote.getXmrAmount() == getXmrAmount());
|
||||||
|
} else {
|
||||||
|
return (quote.getBtcAmount() == getBtcAmount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package com.m2049r.xmrwallet.data;
|
package com.m2049r.xmrwallet.data;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.service.shift.sideshift.api.CreateOrder;
|
import com.m2049r.xmrwallet.service.shift.api.CreateOrder;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
@@ -61,7 +61,7 @@ public class UserNotes {
|
|||||||
|
|
||||||
public void setXmrtoOrder(CreateOrder order) {
|
public void setXmrtoOrder(CreateOrder order) {
|
||||||
if (order != null) {
|
if (order != null) {
|
||||||
xmrtoTag = order.TAG;
|
xmrtoTag = order.getTag();
|
||||||
xmrtoKey = order.getOrderId();
|
xmrtoKey = order.getOrderId();
|
||||||
xmrtoAmount = Helper.getDisplayAmount(order.getBtcAmount());
|
xmrtoAmount = Helper.getDisplayAmount(order.getBtcAmount());
|
||||||
xmrtoCurrency = order.getBtcCurrency();
|
xmrtoCurrency = order.getBtcCurrency();
|
||||||
|
@@ -20,10 +20,10 @@ import android.app.Dialog;
|
|||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
@@ -57,13 +57,14 @@ public class AboutFragment extends DialogFragment {
|
|||||||
AboutFragment.newInstance().show(ft, TAG);
|
AboutFragment.newInstance().show(ft, TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_about, null);
|
final View view = getLayoutInflater().inflate(R.layout.fragment_about, null);
|
||||||
((TextView) view.findViewById(R.id.tvHelp)).setText(Html.fromHtml(getLicencesHtml()));
|
((TextView) view.findViewById(R.id.tvHelp)).setText(Html.fromHtml(getLicencesHtml()));
|
||||||
((TextView) view.findViewById(R.id.tvVersion)).setText(getString(R.string.about_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
|
((TextView) view.findViewById(R.id.tvVersion)).setText(getString(R.string.about_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
|
||||||
|
|
||||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity())
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity())
|
||||||
.setView(view)
|
.setView(view)
|
||||||
.setNegativeButton(R.string.about_close,
|
.setNegativeButton(R.string.about_close,
|
||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
@@ -77,7 +78,7 @@ public class AboutFragment extends DialogFragment {
|
|||||||
|
|
||||||
private String getLicencesHtml() {
|
private String getLicencesHtml() {
|
||||||
try (BufferedReader reader = new BufferedReader(
|
try (BufferedReader reader = new BufferedReader(
|
||||||
new InputStreamReader(getContext().getAssets().open("licenses.html"), StandardCharsets.UTF_8))) {
|
new InputStreamReader(requireContext().getAssets().open("licenses.html"), StandardCharsets.UTF_8))) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null)
|
while ((line = reader.readLine()) != null)
|
||||||
|
@@ -20,10 +20,10 @@ import android.app.Dialog;
|
|||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
@@ -49,13 +49,14 @@ public class CreditsFragment extends DialogFragment {
|
|||||||
CreditsFragment.newInstance().show(ft, TAG);
|
CreditsFragment.newInstance().show(ft, TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_credits, null);
|
final View view = getLayoutInflater().inflate(R.layout.fragment_credits, null);
|
||||||
|
|
||||||
((TextView) view.findViewById(R.id.tvCredits)).setText(Html.fromHtml(getString(R.string.credits_text)));
|
((TextView) view.findViewById(R.id.tvCredits)).setText(Html.fromHtml(getString(R.string.credits_text)));
|
||||||
|
|
||||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity())
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity())
|
||||||
.setView(view)
|
.setView(view)
|
||||||
.setNegativeButton(R.string.about_close,
|
.setNegativeButton(R.string.about_close,
|
||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
|
@@ -16,13 +16,13 @@
|
|||||||
|
|
||||||
package com.m2049r.xmrwallet.dialog;
|
package com.m2049r.xmrwallet.dialog;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -65,9 +65,10 @@ public class HelpFragment extends DialogFragment {
|
|||||||
|
|
||||||
private Spanned getHtml(String html, double textSize) {
|
private Spanned getHtml(String html, double textSize) {
|
||||||
final Html.ImageGetter imageGetter = source -> {
|
final Html.ImageGetter imageGetter = source -> {
|
||||||
final int imageId = getResources().getIdentifier(source.replace("/", ""), "drawable", requireActivity().getPackageName());
|
@SuppressLint("DiscouragedApi") final int imageId = getResources().getIdentifier(source.replace("/", ""), "drawable", requireActivity().getPackageName());
|
||||||
// Don't die if we don't find the image - use a heart instead
|
// Don't die if we don't find the image - use a heart instead
|
||||||
final Drawable drawable = ContextCompat.getDrawable(requireActivity(), imageId > 0 ? imageId : R.drawable.ic_favorite_24dp);
|
final Drawable drawable = ContextCompat.getDrawable(requireActivity(), imageId > 0 ? imageId : R.drawable.ic_favorite_24dp);
|
||||||
|
assert drawable != null;
|
||||||
final double f = textSize / drawable.getIntrinsicHeight();
|
final double f = textSize / drawable.getIntrinsicHeight();
|
||||||
drawable.setBounds(0, 0, (int) (f * drawable.getIntrinsicWidth()), (int) textSize);
|
drawable.setBounds(0, 0, (int) (f * drawable.getIntrinsicWidth()), (int) textSize);
|
||||||
return drawable;
|
return drawable;
|
||||||
@@ -82,7 +83,7 @@ public class HelpFragment extends DialogFragment {
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_help, null);
|
final View view = getLayoutInflater().inflate(R.layout.fragment_help, null);
|
||||||
|
|
||||||
int helpId = 0;
|
int helpId = 0;
|
||||||
boolean torButton = false;
|
boolean torButton = false;
|
||||||
@@ -100,7 +101,7 @@ public class HelpFragment extends DialogFragment {
|
|||||||
.setView(view);
|
.setView(view);
|
||||||
if (torButton) {
|
if (torButton) {
|
||||||
builder.setNegativeButton(R.string.help_nok,
|
builder.setNegativeButton(R.string.help_nok,
|
||||||
(dialog, id) -> dialog.dismiss())
|
(dialog, id) -> dialog.dismiss())
|
||||||
.setPositiveButton(R.string.help_getorbot,
|
.setPositiveButton(R.string.help_getorbot,
|
||||||
(dialog, id) -> {
|
(dialog, id) -> {
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
|
@@ -18,7 +18,6 @@ package com.m2049r.xmrwallet.dialog;
|
|||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -66,7 +65,7 @@ public class PocketChangeFragment extends DialogFragment implements Slider.OnCha
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_pocketchange_setting, null);
|
final View view = getLayoutInflater().inflate(R.layout.fragment_pocketchange_setting, null);
|
||||||
boolean enabled = false;
|
boolean enabled = false;
|
||||||
int progress = 0;
|
int progress = 0;
|
||||||
Bundle arguments = getArguments();
|
Bundle arguments = getArguments();
|
||||||
|
@@ -20,10 +20,10 @@ import android.app.Dialog;
|
|||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
@@ -49,13 +49,14 @@ public class PrivacyFragment extends DialogFragment {
|
|||||||
PrivacyFragment.newInstance().show(ft, TAG);
|
PrivacyFragment.newInstance().show(ft, TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_privacy_policy, null);
|
final View view = getLayoutInflater().inflate(R.layout.fragment_privacy_policy, null);
|
||||||
|
|
||||||
((TextView) view.findViewById(R.id.tvCredits)).setText(Html.fromHtml(getString(R.string.privacy_policy)));
|
((TextView) view.findViewById(R.id.tvCredits)).setText(Html.fromHtml(getString(R.string.privacy_policy)));
|
||||||
|
|
||||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity())
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity())
|
||||||
.setView(view)
|
.setView(view)
|
||||||
.setNegativeButton(R.string.about_close,
|
.setNegativeButton(R.string.about_close,
|
||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
|
@@ -17,6 +17,7 @@ package com.m2049r.xmrwallet.dialog;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -31,6 +32,7 @@ import com.m2049r.xmrwallet.R;
|
|||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
@@ -56,7 +58,7 @@ public class ProgressDialog extends AlertDialog {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
final View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_ledger_progress, null);
|
@SuppressLint("InflateParams") final View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_ledger_progress, null);
|
||||||
pbCircle = view.findViewById(R.id.pbCircle);
|
pbCircle = view.findViewById(R.id.pbCircle);
|
||||||
tvMessage = view.findViewById(R.id.tvMessage);
|
tvMessage = view.findViewById(R.id.tvMessage);
|
||||||
rlProgressBar = view.findViewById(R.id.rlProgressBar);
|
rlProgressBar = view.findViewById(R.id.rlProgressBar);
|
||||||
@@ -78,7 +80,7 @@ public class ProgressDialog extends AlertDialog {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
if (Helper.preventScreenshot()) {
|
if (Helper.preventScreenshot()) {
|
||||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
Objects.requireNonNull(getWindow()).setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,16 @@
|
|||||||
|
package com.m2049r.xmrwallet.fragment.send;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.data.CryptoAmount;
|
||||||
|
import com.m2049r.xmrwallet.service.shift.api.QueryOrderParameters;
|
||||||
|
|
||||||
|
public interface PreShifter {
|
||||||
|
CryptoAmount getAmount();
|
||||||
|
|
||||||
|
void onOrderParametersError(final Exception ex);
|
||||||
|
|
||||||
|
void onOrderParametersReceived(final QueryOrderParameters orderParameters);
|
||||||
|
|
||||||
|
boolean isActive();
|
||||||
|
|
||||||
|
void showProgress();
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user