mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-04 00:53:36 +02:00
Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
57ddddfce2 | ||
![]() |
ab6069058b | ||
![]() |
d94d6e6925 |
@@ -1,14 +1,14 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion '29.0.2'
|
||||
defaultConfig {
|
||||
applicationId "com.m2049r.xmrwallet"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode 601
|
||||
versionName "1.16.1 'Karmic Nodes'"
|
||||
targetSdkVersion 29
|
||||
versionCode 702
|
||||
versionName "1.17.2 'Druk'"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
@@ -56,6 +56,9 @@ android {
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
}
|
||||
applicationVariants.all { variant ->
|
||||
variant.buildConfigField "String", "ID_A", "\"" + getId("ID_A") + "\""
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
@@ -109,10 +112,16 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
def getId(name) {
|
||||
def Properties props = new Properties()
|
||||
props.load(new FileInputStream(new File('monerujo.id')))
|
||||
return props[name]
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.core:core:1.3.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'com.google.android.material:material:1.3.0-alpha03'
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
|
@@ -13,6 +13,7 @@
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:name=".XmrWalletApplication"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
|
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2018 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.levin.scanner;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2020 m2049r et al.
|
||||
*
|
||||
* 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;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
|
@@ -301,24 +301,21 @@ public class LoginActivity extends BaseActivity
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayShowTitleEnabled(false);
|
||||
|
||||
toolbar.setOnButtonListener(new Toolbar.OnButtonListener() {
|
||||
@Override
|
||||
public void onButton(int type) {
|
||||
switch (type) {
|
||||
case Toolbar.BUTTON_BACK:
|
||||
onBackPressed();
|
||||
break;
|
||||
case Toolbar.BUTTON_CLOSE:
|
||||
finish();
|
||||
break;
|
||||
case Toolbar.BUTTON_CREDITS:
|
||||
CreditsFragment.display(getSupportFragmentManager());
|
||||
break;
|
||||
case Toolbar.BUTTON_NONE:
|
||||
break;
|
||||
default:
|
||||
Timber.e("Button " + type + "pressed - how can this be?");
|
||||
}
|
||||
toolbar.setOnButtonListener(type -> {
|
||||
switch (type) {
|
||||
case Toolbar.BUTTON_BACK:
|
||||
onBackPressed();
|
||||
break;
|
||||
case Toolbar.BUTTON_CLOSE:
|
||||
finish();
|
||||
break;
|
||||
case Toolbar.BUTTON_CREDITS:
|
||||
CreditsFragment.display(getSupportFragmentManager());
|
||||
break;
|
||||
case Toolbar.BUTTON_NONE:
|
||||
break;
|
||||
default:
|
||||
Timber.e("Button " + type + "pressed - how can this be?");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -366,34 +363,31 @@ public class LoginActivity extends BaseActivity
|
||||
public void onWalletDetails(final String walletName) {
|
||||
Timber.d("details for wallet .%s.", walletName);
|
||||
if (checkServiceRunning()) return;
|
||||
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_POSITIVE:
|
||||
final File walletFile = Helper.getWalletFile(LoginActivity.this, walletName);
|
||||
if (WalletManager.getInstance().walletExists(walletFile)) {
|
||||
Helper.promptPassword(LoginActivity.this, walletName, true, new Helper.PasswordAction() {
|
||||
@Override
|
||||
public void act(String walletName, String password, boolean fingerprintUsed) {
|
||||
if (checkDevice(walletName, password))
|
||||
startDetails(walletFile, password, GenerateReviewFragment.VIEW_TYPE_DETAILS);
|
||||
}
|
||||
DialogInterface.OnClickListener dialogClickListener = (dialog, which) -> {
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_POSITIVE:
|
||||
final File walletFile = Helper.getWalletFile(LoginActivity.this, walletName);
|
||||
if (WalletManager.getInstance().walletExists(walletFile)) {
|
||||
Helper.promptPassword(LoginActivity.this, walletName, true, new Helper.PasswordAction() {
|
||||
@Override
|
||||
public void act(String walletName1, String password, boolean fingerprintUsed) {
|
||||
if (checkDevice(walletName1, password))
|
||||
startDetails(walletFile, password, GenerateReviewFragment.VIEW_TYPE_DETAILS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fail(String walletName, String password, boolean fingerprintUsed) {
|
||||
}
|
||||
});
|
||||
} else { // this cannot really happen as we prefilter choices
|
||||
Timber.e("Wallet missing: %s", walletName);
|
||||
Toast.makeText(LoginActivity.this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
break;
|
||||
@Override
|
||||
public void fail(String walletName1, String password, boolean fingerprintUsed) {
|
||||
}
|
||||
});
|
||||
} else { // this cannot really happen as we prefilter choices
|
||||
Timber.e("Wallet missing: %s", walletName);
|
||||
Toast.makeText(LoginActivity.this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
break;
|
||||
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -56,6 +56,7 @@ import com.google.zxing.WriterException;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.qrcode.QRCodeWriter;
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import com.m2049r.xmrwallet.BuildConfig;
|
||||
import com.m2049r.xmrwallet.data.BarcodeData;
|
||||
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
@@ -468,7 +469,7 @@ public class ReceiveFragment extends Fragment {
|
||||
Timber.d("CLEARQR");
|
||||
return;
|
||||
}
|
||||
bcData = new BarcodeData(BarcodeData.Asset.XMR, address, null, notes, xmrAmount);
|
||||
bcData = new BarcodeData(BarcodeData.Asset.XMR, address, notes, xmrAmount);
|
||||
int size = Math.max(ivQrCode.getWidth(), ivQrCode.getHeight());
|
||||
Bitmap qr = generate(bcData.getUriString(), size, size);
|
||||
if (qr != null) {
|
||||
|
@@ -16,8 +16,11 @@
|
||||
|
||||
package com.m2049r.xmrwallet;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Paint;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -25,6 +28,7 @@ import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
@@ -77,6 +81,9 @@ public class TxFragment extends Fragment {
|
||||
private TextView tvTxXmrToKey;
|
||||
private TextView tvDestinationBtc;
|
||||
private TextView tvTxAmountBtc;
|
||||
private TextView tvXmrToSupport;
|
||||
private TextView tvXmrToKeyLabel;
|
||||
private ImageView tvXmrToLogo;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
@@ -88,6 +95,9 @@ public class TxFragment extends Fragment {
|
||||
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
|
||||
tvDestinationBtc = view.findViewById(R.id.tvDestinationBtc);
|
||||
tvTxAmountBtc = view.findViewById(R.id.tvTxAmountBtc);
|
||||
tvXmrToSupport = view.findViewById(R.id.tvXmrToSupport);
|
||||
tvXmrToKeyLabel = view.findViewById(R.id.tvXmrToKeyLabel);
|
||||
tvXmrToLogo = view.findViewById(R.id.tvXmrToLogo);
|
||||
|
||||
tvAccount = view.findViewById(R.id.tvAccount);
|
||||
tvAddress = view.findViewById(R.id.tvAddress);
|
||||
@@ -104,12 +114,9 @@ public class TxFragment extends Fragment {
|
||||
|
||||
etTxNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
|
||||
|
||||
tvTxXmrToKey.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
|
||||
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
tvTxXmrToKey.setOnClickListener(v -> {
|
||||
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
|
||||
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
Bundle args = getArguments();
|
||||
@@ -283,12 +290,36 @@ public class TxFragment extends Fragment {
|
||||
showBtcInfo();
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
void showBtcInfo() {
|
||||
if (userNotes.xmrtoKey != null) {
|
||||
cvXmrTo.setVisibility(View.VISIBLE);
|
||||
tvTxXmrToKey.setText(userNotes.xmrtoKey);
|
||||
String key = userNotes.xmrtoKey;
|
||||
if ("xmrto".equals(userNotes.xmrtoTag)) { // legacy xmr.to service :(
|
||||
key = "xmrto-" + key;
|
||||
}
|
||||
tvTxXmrToKey.setText(key);
|
||||
tvDestinationBtc.setText(userNotes.xmrtoDestination);
|
||||
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " BTC");
|
||||
switch (userNotes.xmrtoTag) {
|
||||
case "xmrto":
|
||||
tvXmrToSupport.setVisibility(View.GONE);
|
||||
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
|
||||
tvXmrToLogo.setImageResource(R.drawable.ic_xmrto_logo);
|
||||
break;
|
||||
case "side": // defaults in layout - just add underline
|
||||
tvXmrToSupport.setPaintFlags(tvXmrToSupport.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
||||
tvXmrToSupport.setOnClickListener(v -> {
|
||||
Uri uri = Uri.parse("https://sideshift.ai/orders/" + userNotes.xmrtoKey);
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
startActivity(intent);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
tvXmrToSupport.setVisibility(View.GONE);
|
||||
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
|
||||
tvXmrToLogo.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
cvXmrTo.setVisibility(View.GONE);
|
||||
}
|
||||
|
@@ -16,14 +16,13 @@
|
||||
|
||||
package com.m2049r.xmrwallet;
|
||||
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
|
||||
import com.m2049r.xmrwallet.BuildConfig;
|
||||
import com.m2049r.xmrwallet.model.NetworkType;
|
||||
import com.m2049r.xmrwallet.util.DayNightMode;
|
||||
import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||
import com.m2049r.xmrwallet.util.NightmodeHelper;
|
||||
|
||||
|
@@ -21,7 +21,6 @@ import android.net.Uri;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.util.BitcoinAddressValidator;
|
||||
import com.m2049r.xmrwallet.util.OpenAliasHelper;
|
||||
import com.m2049r.xmrwallet.util.PaymentProtocolHelper;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
@@ -43,7 +42,6 @@ public class BarcodeData {
|
||||
static final String BTC_SCHEME = "bitcoin";
|
||||
static final String BTC_DESCRIPTION = "message";
|
||||
static final String BTC_AMOUNT = "amount";
|
||||
static final String BTC_BIP70_PARM = "r";
|
||||
|
||||
public enum Asset {
|
||||
XMR, BTC
|
||||
@@ -52,8 +50,7 @@ public class BarcodeData {
|
||||
public enum Security {
|
||||
NORMAL,
|
||||
OA_NO_DNSSEC,
|
||||
OA_DNSSEC,
|
||||
BIP70
|
||||
OA_DNSSEC
|
||||
}
|
||||
|
||||
final public Asset asset;
|
||||
@@ -62,7 +59,6 @@ public class BarcodeData {
|
||||
final public String amount;
|
||||
final public String description;
|
||||
final public Security security;
|
||||
final public String bip70;
|
||||
|
||||
public BarcodeData(Asset asset, String address) {
|
||||
this(asset, address, null, null, null, Security.NORMAL);
|
||||
@@ -80,21 +76,19 @@ public class BarcodeData {
|
||||
this(asset, address, null, description, amount, Security.NORMAL);
|
||||
}
|
||||
|
||||
public BarcodeData(Asset asset, String address, String addressName, String description, String amount, Security security) {
|
||||
this(asset, address, addressName, null, description, amount, security);
|
||||
public BarcodeData(Asset asset, String address, String description, String amount) {
|
||||
this(asset, address, null, description, amount, Security.NORMAL);
|
||||
}
|
||||
|
||||
public BarcodeData(Asset asset, String address, String addressName, String bip70, String description, String amount, Security security) {
|
||||
public BarcodeData(Asset asset, String address, String addressName, String description, String amount, Security security) {
|
||||
this.asset = asset;
|
||||
this.address = address;
|
||||
this.bip70 = bip70;
|
||||
this.addressName = addressName;
|
||||
this.description = description;
|
||||
this.amount = amount;
|
||||
this.security = security;
|
||||
}
|
||||
|
||||
|
||||
public Uri getUri() {
|
||||
return Uri.parse(getUriString());
|
||||
}
|
||||
@@ -127,10 +121,6 @@ public class BarcodeData {
|
||||
if (bcData == null) {
|
||||
bcData = parseBitcoinUri(qrCode);
|
||||
}
|
||||
// check for btc payment uri (like bitpay)
|
||||
if (bcData == null) {
|
||||
bcData = parseBitcoinPaymentUrl(qrCode);
|
||||
}
|
||||
// check for naked btc address
|
||||
if (bcData == null) {
|
||||
bcData = parseBitcoinNaked(qrCode);
|
||||
@@ -195,7 +185,7 @@ public class BarcodeData {
|
||||
Timber.d("address invalid");
|
||||
return null;
|
||||
}
|
||||
return new BarcodeData(Asset.XMR, address, paymentId, description, amount);
|
||||
return new BarcodeData(Asset.XMR, address, description, amount);
|
||||
}
|
||||
|
||||
static public BarcodeData parseMoneroNaked(String address) {
|
||||
@@ -245,17 +235,9 @@ public class BarcodeData {
|
||||
}
|
||||
String description = parms.get(BTC_DESCRIPTION);
|
||||
String address = parts[0]; // no need to decode as there can bo no special characters
|
||||
if (address.isEmpty()) { // possibly a BIP72 uri
|
||||
String bip70 = parms.get(BTC_BIP70_PARM);
|
||||
if (bip70 == null) {
|
||||
Timber.d("no address and can't find pp url");
|
||||
return null;
|
||||
}
|
||||
if (!PaymentProtocolHelper.isHttp(bip70)) {
|
||||
Timber.d("[%s] is not http url", bip70);
|
||||
return null;
|
||||
}
|
||||
return new BarcodeData(BarcodeData.Asset.BTC, null, null, bip70, description, null, Security.NORMAL);
|
||||
if (address.isEmpty()) {
|
||||
Timber.d("no address");
|
||||
return null;
|
||||
}
|
||||
if (!BitcoinAddressValidator.validate(address)) {
|
||||
Timber.d("BTC address (%s) invalid", address);
|
||||
@@ -270,22 +252,7 @@ public class BarcodeData {
|
||||
return null; // we have an amount but its not a number!
|
||||
}
|
||||
}
|
||||
return new BarcodeData(BarcodeData.Asset.BTC, address, null, description, amount);
|
||||
}
|
||||
|
||||
// https://bitpay.com/invoice?id=xxx
|
||||
// https://bitpay.com/i/KbMdd4EhnLXSbpWGKsaeo6
|
||||
static public BarcodeData parseBitcoinPaymentUrl(String url) {
|
||||
Timber.d("parseBitcoinUri=%s", url);
|
||||
|
||||
if (url == null) return null;
|
||||
|
||||
if (!PaymentProtocolHelper.isHttp(url)) {
|
||||
Timber.d("[%s] is not http url", url);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new BarcodeData(Asset.BTC, url);
|
||||
return new BarcodeData(BarcodeData.Asset.BTC, address, description, amount);
|
||||
}
|
||||
|
||||
static public BarcodeData parseBitcoinNaked(String address) {
|
||||
@@ -333,6 +300,10 @@ public class BarcodeData {
|
||||
}
|
||||
|
||||
String paymentId = oaAttrs.get(OpenAliasHelper.OA1_PAYMENTID);
|
||||
if (paymentId != null) {
|
||||
Timber.e("paymentId not supported");
|
||||
return null;
|
||||
}
|
||||
String description = oaAttrs.get(OpenAliasHelper.OA1_DESCRIPTION);
|
||||
if (description == null) {
|
||||
description = oaAttrs.get(OpenAliasHelper.OA1_NAME);
|
||||
@@ -348,13 +319,9 @@ public class BarcodeData {
|
||||
return null; // we have an amount but its not a number!
|
||||
}
|
||||
}
|
||||
if ((paymentId != null) && !Wallet.isPaymentIdValid(paymentId)) {
|
||||
Timber.d("paymentId invalid");
|
||||
return null;
|
||||
}
|
||||
|
||||
Security sec = dnssec ? BarcodeData.Security.OA_DNSSEC : BarcodeData.Security.OA_NO_DNSSEC;
|
||||
|
||||
return new BarcodeData(asset, address, addressName, paymentId, description, amount, sec);
|
||||
return new BarcodeData(asset, address, addressName, description, amount, sec);
|
||||
}
|
||||
}
|
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2020 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.AllArgsConstructor;
|
||||
|
@@ -20,6 +20,7 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
|
||||
// https://stackoverflow.com/questions/2139134/how-to-send-an-object-from-one-android-activity-to-another-using-intents
|
||||
public class TxData implements Parcelable {
|
||||
@@ -52,6 +53,10 @@ public class TxData implements Parcelable {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public double getAmountAsDouble() {
|
||||
return 1.0 * amount / 1000000000000L;
|
||||
}
|
||||
|
||||
public int getMixin() {
|
||||
return mixin;
|
||||
}
|
||||
@@ -68,6 +73,10 @@ public class TxData implements Parcelable {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public void setAmount(double amount) {
|
||||
this.amount = Wallet.getAmountFromDouble(amount);
|
||||
}
|
||||
|
||||
public void setMixin(int mixin) {
|
||||
this.mixin = mixin;
|
||||
}
|
||||
|
@@ -18,11 +18,20 @@ package com.m2049r.xmrwallet.data;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
public class TxDataBtc extends TxData {
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
private String xmrtoUuid;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class TxDataBtc extends TxData {
|
||||
@Getter
|
||||
@Setter
|
||||
private String xmrtoOrderId; // shown in success screen
|
||||
@Getter
|
||||
@Setter
|
||||
private String btcAddress;
|
||||
private String bip70;
|
||||
@Getter
|
||||
@Setter
|
||||
private double btcAmount;
|
||||
|
||||
public TxDataBtc() {
|
||||
@@ -33,44 +42,11 @@ public class TxDataBtc extends TxData {
|
||||
super(txDataBtc);
|
||||
}
|
||||
|
||||
public String getXmrtoUuid() {
|
||||
return xmrtoUuid;
|
||||
}
|
||||
|
||||
public void setXmrtoUuid(String xmrtoUuid) {
|
||||
this.xmrtoUuid = xmrtoUuid;
|
||||
}
|
||||
|
||||
public String getBtcAddress() {
|
||||
return btcAddress;
|
||||
}
|
||||
|
||||
public void setBtcAddress(String btcAddress) {
|
||||
this.btcAddress = btcAddress;
|
||||
}
|
||||
|
||||
public String getBip70() {
|
||||
return bip70;
|
||||
}
|
||||
|
||||
public void setBip70(String bip70) {
|
||||
this.bip70 = bip70;
|
||||
}
|
||||
|
||||
public double getBtcAmount() {
|
||||
return btcAmount;
|
||||
}
|
||||
|
||||
public void setBtcAmount(double btcAmount) {
|
||||
this.btcAmount = btcAmount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
super.writeToParcel(out, flags);
|
||||
out.writeString(xmrtoUuid);
|
||||
out.writeString(xmrtoOrderId);
|
||||
out.writeString(btcAddress);
|
||||
out.writeString(bip70);
|
||||
out.writeDouble(btcAmount);
|
||||
}
|
||||
|
||||
@@ -87,21 +63,19 @@ public class TxDataBtc extends TxData {
|
||||
|
||||
protected TxDataBtc(Parcel in) {
|
||||
super(in);
|
||||
xmrtoUuid = in.readString();
|
||||
xmrtoOrderId = in.readString();
|
||||
btcAddress = in.readString();
|
||||
bip70 = in.readString();
|
||||
btcAmount = in.readDouble();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(",xmrtoUuid:");
|
||||
sb.append(xmrtoUuid);
|
||||
sb.append("xmrtoOrderId:");
|
||||
sb.append(xmrtoOrderId);
|
||||
sb.append(",btcAddress:");
|
||||
sb.append(btcAddress);
|
||||
sb.append(",bip70:");
|
||||
sb.append(bip70);
|
||||
sb.append(",btcAmount:");
|
||||
sb.append(btcAmount);
|
||||
return sb.toString();
|
||||
|
@@ -16,16 +16,16 @@
|
||||
|
||||
package com.m2049r.xmrwallet.data;
|
||||
|
||||
import com.m2049r.xmrwallet.xmrto.api.QueryOrderStatus;
|
||||
import com.m2049r.xmrwallet.service.shift.sideshift.api.CreateOrder;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class UserNotes {
|
||||
public String txNotes = "";
|
||||
public String note = "";
|
||||
public String xmrtoTag = null;
|
||||
public String xmrtoKey = null;
|
||||
public String xmrtoAmount = null; // could be a double - but we are not doing any calculations
|
||||
public String xmrtoDestination = null;
|
||||
@@ -35,13 +35,14 @@ public class UserNotes {
|
||||
return;
|
||||
}
|
||||
this.txNotes = txNotes;
|
||||
Pattern p = Pattern.compile("^\\{(xmrto-\\w{6}),([0-9.]*)BTC,(\\w*)\\} ?(.*)");
|
||||
Pattern p = Pattern.compile("^\\{([a-z]+)-(\\w{6,}),([0-9.]*)BTC,(\\w*)\\} ?(.*)");
|
||||
Matcher m = p.matcher(txNotes);
|
||||
if (m.find()) {
|
||||
xmrtoKey = m.group(1);
|
||||
xmrtoAmount = m.group(2);
|
||||
xmrtoDestination = m.group(3);
|
||||
note = m.group(4);
|
||||
xmrtoTag = m.group(1);
|
||||
xmrtoKey = m.group(2);
|
||||
xmrtoAmount = m.group(3);
|
||||
xmrtoDestination = m.group(4);
|
||||
note = m.group(5);
|
||||
} else {
|
||||
note = txNotes;
|
||||
}
|
||||
@@ -56,12 +57,14 @@ public class UserNotes {
|
||||
txNotes = buildTxNote();
|
||||
}
|
||||
|
||||
public void setXmrtoStatus(QueryOrderStatus xmrtoStatus) {
|
||||
if (xmrtoStatus != null) {
|
||||
xmrtoKey = xmrtoStatus.getUuid();
|
||||
xmrtoAmount = String.valueOf(xmrtoStatus.getBtcAmount());
|
||||
xmrtoDestination = xmrtoStatus.getBtcDestAddress();
|
||||
public void setXmrtoOrder(CreateOrder order) {
|
||||
if (order != null) {
|
||||
xmrtoTag = order.TAG;
|
||||
xmrtoKey = order.getOrderId();
|
||||
xmrtoAmount = Helper.getDisplayAmount(order.getBtcAmount());
|
||||
xmrtoDestination = order.getBtcAddress();
|
||||
} else {
|
||||
xmrtoTag = null;
|
||||
xmrtoKey = null;
|
||||
xmrtoAmount = null;
|
||||
xmrtoDestination = null;
|
||||
@@ -70,11 +73,13 @@ public class UserNotes {
|
||||
}
|
||||
|
||||
private String buildTxNote() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (xmrtoKey != null) {
|
||||
if ((xmrtoAmount == null) || (xmrtoDestination == null))
|
||||
throw new IllegalArgumentException("Broken notes");
|
||||
sb.append("{");
|
||||
sb.append(xmrtoTag);
|
||||
sb.append("-");
|
||||
sb.append(xmrtoKey);
|
||||
sb.append(",");
|
||||
sb.append(xmrtoAmount);
|
||||
|
@@ -27,7 +27,6 @@ import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.m2049r.xmrwallet.BuildConfig;
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
|
||||
|
@@ -34,6 +34,8 @@ import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.data.BarcodeData;
|
||||
@@ -45,9 +47,6 @@ import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.util.BitcoinAddressValidator;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.OpenAliasHelper;
|
||||
import com.m2049r.xmrwallet.util.PaymentProtocolHelper;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToError;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToException;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -92,8 +91,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
private ImageButton bPasteAddress;
|
||||
|
||||
private boolean resolvingOA = false;
|
||||
private boolean resolvingPP = false;
|
||||
private String resolvedPP = null;
|
||||
|
||||
OnScanListener onScanListener;
|
||||
|
||||
@@ -125,20 +122,11 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (!hasFocus) {
|
||||
View next = etAddress;
|
||||
String enteredAddress = etAddress.getEditText().getText().toString().trim();
|
||||
String dnsOA = dnsFromOpenAlias(enteredAddress);
|
||||
Timber.d("OpenAlias is %s", dnsOA);
|
||||
if (dnsOA != null) {
|
||||
processOpenAlias(dnsOA);
|
||||
next = null;
|
||||
} else {
|
||||
// maybe a bip72 or 70 URI
|
||||
final String bip70 = PaymentProtocolHelper.getBip70(enteredAddress);
|
||||
if (bip70 != null) {
|
||||
// looks good - resolve through xmr.to
|
||||
processBip70(bip70);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,8 +135,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
Timber.d("AFTER: %s", editable.toString());
|
||||
if (editable.toString().equals(resolvedPP)) return; // no change required
|
||||
resolvedPP = null;
|
||||
etAddress.setError(null);
|
||||
if (isIntegratedAddress()) {
|
||||
Timber.d("isIntegratedAddress");
|
||||
@@ -156,7 +142,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
tvPaymentIdIntegrated.setVisibility(View.VISIBLE);
|
||||
llXmrTo.setVisibility(View.INVISIBLE);
|
||||
sendListener.setMode(SendFragment.Mode.XMR);
|
||||
} else if (isBitcoinAddress() || (resolvedPP != null)) {
|
||||
} else if (isBitcoinAddress()) {
|
||||
Timber.d("isBitcoinAddress");
|
||||
setBtcMode();
|
||||
} else {
|
||||
@@ -190,14 +176,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
et.setSelection(et.getText().length());
|
||||
etAddress.requestFocus();
|
||||
} else {
|
||||
final String bip70 = PaymentProtocolHelper.getBip70(clip);
|
||||
if (bip70 != null) {
|
||||
final EditText et = etAddress.getEditText();
|
||||
et.setText(clip);
|
||||
et.setSelection(et.getText().length());
|
||||
processBip70(bip70);
|
||||
} else
|
||||
Toast.makeText(getActivity(), getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getActivity(), getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -273,62 +252,10 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
} // else ignore
|
||||
}
|
||||
|
||||
private void processBip70(final String bip70) {
|
||||
Timber.d("RESOLVED PP: %s", resolvedPP);
|
||||
if (resolvingPP) return; // already resolving - just wait
|
||||
resolvingPP = true;
|
||||
sendListener.popBarcodeData();
|
||||
etAddress.setError(getString(R.string.send_address_resolve_bip70));
|
||||
PaymentProtocolHelper.resolve(bip70, new PaymentProtocolHelper.OnResolvedListener() {
|
||||
@Override
|
||||
public void onResolved(BarcodeData.Asset asset, String address, double amount, String resolvedBip70) {
|
||||
resolvingPP = false;
|
||||
if (asset != BarcodeData.Asset.BTC)
|
||||
throw new IllegalArgumentException("only BTC here");
|
||||
|
||||
if (resolvedBip70 == null)
|
||||
throw new IllegalArgumentException("success means we have a pp_url - else die");
|
||||
|
||||
final BarcodeData barcodeData =
|
||||
new BarcodeData(BarcodeData.Asset.BTC, address, null,
|
||||
resolvedBip70, null, String.valueOf(amount),
|
||||
BarcodeData.Security.BIP70);
|
||||
etNotes.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Timber.d("security is %s", barcodeData.security);
|
||||
processScannedData(barcodeData);
|
||||
etNotes.requestFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Exception ex) {
|
||||
resolvingPP = false;
|
||||
etAddress.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int errorMsgId = R.string.send_address_not_bip70;
|
||||
if (ex instanceof XmrToException) {
|
||||
XmrToError error = ((XmrToException) ex).getError();
|
||||
if (error != null) {
|
||||
errorMsgId = error.getErrorMsgId();
|
||||
}
|
||||
}
|
||||
etAddress.setError(getString(errorMsgId));
|
||||
}
|
||||
});
|
||||
Timber.d("PP FAILED");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean checkAddressNoError() {
|
||||
String address = etAddress.getEditText().getText().toString();
|
||||
return Wallet.isAddressValid(address)
|
||||
|| BitcoinAddressValidator.validate(address)
|
||||
|| (resolvedPP != null);
|
||||
|| BitcoinAddressValidator.validate(address);
|
||||
}
|
||||
|
||||
private boolean checkAddress() {
|
||||
@@ -365,11 +292,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
Timber.d("OpenAlias is %s", dnsOA);
|
||||
if (dnsOA != null) {
|
||||
processOpenAlias(dnsOA);
|
||||
} else {
|
||||
String bip70 = PaymentProtocolHelper.getBip70(enteredAddress);
|
||||
if (bip70 != null) {
|
||||
processBip70(bip70);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -377,15 +299,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
if (sendListener != null) {
|
||||
TxData txData = sendListener.getTxData();
|
||||
if (txData instanceof TxDataBtc) {
|
||||
if (resolvedPP != null) {
|
||||
// take the value from the field nonetheless as this is what the user sees
|
||||
// (in case we have a bug somewhere)
|
||||
((TxDataBtc) txData).setBip70(etAddress.getEditText().getText().toString());
|
||||
((TxDataBtc) txData).setBtcAddress(null);
|
||||
} else {
|
||||
((TxDataBtc) txData).setBtcAddress(etAddress.getEditText().getText().toString());
|
||||
((TxDataBtc) txData).setBip70(null);
|
||||
}
|
||||
((TxDataBtc) txData).setBtcAddress(etAddress.getEditText().getText().toString());
|
||||
txData.setDestinationAddress(null);
|
||||
} else {
|
||||
txData.setDestinationAddress(etAddress.getEditText().getText().toString());
|
||||
@@ -398,7 +312,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof OnScanListener) {
|
||||
onScanListener = (OnScanListener) context;
|
||||
@@ -424,21 +338,11 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
}
|
||||
|
||||
public void processScannedData() {
|
||||
resolvedPP = null;
|
||||
BarcodeData barcodeData = sendListener.getBarcodeData();
|
||||
if (barcodeData != null) {
|
||||
Timber.d("GOT DATA");
|
||||
|
||||
if (barcodeData.bip70 != null) {
|
||||
setBtcMode();
|
||||
if (barcodeData.security == BarcodeData.Security.BIP70) {
|
||||
resolvedPP = barcodeData.bip70;
|
||||
etAddress.setError(getString(R.string.send_address_bip70));
|
||||
} else {
|
||||
processBip70(barcodeData.bip70);
|
||||
}
|
||||
etAddress.getEditText().setText(barcodeData.bip70);
|
||||
} else if (barcodeData.address != null) {
|
||||
if (barcodeData.address != null) {
|
||||
etAddress.getEditText().setText(barcodeData.address);
|
||||
if (checkAddress()) {
|
||||
if (barcodeData.security == BarcodeData.Security.OA_NO_DNSSEC)
|
||||
|
@@ -19,7 +19,6 @@ package com.m2049r.xmrwallet.fragment.send;
|
||||
import android.os.Bundle;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -31,15 +30,14 @@ import com.m2049r.xmrwallet.data.TxDataBtc;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.OkHttpHelper;
|
||||
import com.m2049r.xmrwallet.widget.ExchangeEditText;
|
||||
import com.m2049r.xmrwallet.widget.ExchangeOtherEditText;
|
||||
import com.m2049r.xmrwallet.widget.SendProgressView;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToError;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToException;
|
||||
import com.m2049r.xmrwallet.xmrto.api.QueryOrderParameters;
|
||||
import com.m2049r.xmrwallet.xmrto.api.XmrToApi;
|
||||
import com.m2049r.xmrwallet.xmrto.api.XmrToCallback;
|
||||
import com.m2049r.xmrwallet.xmrto.network.XmrToApiImpl;
|
||||
import com.m2049r.xmrwallet.service.shift.ShiftError;
|
||||
import com.m2049r.xmrwallet.service.shift.ShiftException;
|
||||
import com.m2049r.xmrwallet.service.shift.sideshift.api.QueryOrderParameters;
|
||||
import com.m2049r.xmrwallet.service.shift.sideshift.api.SideShiftApi;
|
||||
import com.m2049r.xmrwallet.service.shift.ShiftCallback;
|
||||
import com.m2049r.xmrwallet.service.shift.sideshift.network.SideShiftApiImpl;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
@@ -93,17 +91,22 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
|
||||
@Override
|
||||
public boolean onValidateFields() {
|
||||
Timber.i(maxBtc + "/" + minBtc);
|
||||
if (!etAmount.validate(maxBtc, minBtc)) {
|
||||
return false;
|
||||
}
|
||||
if (orderParameters == null) {
|
||||
return false; // this should never happen
|
||||
}
|
||||
if (sendListener != null) {
|
||||
TxDataBtc txDataBtc = (TxDataBtc) sendListener.getTxData();
|
||||
String btcString = etAmount.getNativeAmount();
|
||||
if (btcString != null) {
|
||||
try {
|
||||
double btc = Double.parseDouble(btcString);
|
||||
Timber.d("setAmount %f", btc);
|
||||
Timber.d("setBtcAmount %f", btc);
|
||||
txDataBtc.setBtcAmount(btc);
|
||||
txDataBtc.setAmount(btc / orderParameters.getPrice());
|
||||
} catch (NumberFormatException ex) {
|
||||
Timber.d(ex.getLocalizedMessage());
|
||||
txDataBtc.setBtcAmount(0);
|
||||
@@ -115,17 +118,6 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setBip70Mode() {
|
||||
TxDataBtc txDataBtc = (TxDataBtc) sendListener.getTxData();
|
||||
if (txDataBtc.getBip70() == null) {
|
||||
etAmount.setEditable(true);
|
||||
Helper.showKeyboard(getActivity());
|
||||
} else {
|
||||
etAmount.setEditable(false);
|
||||
Helper.hideKeyboard(getActivity());
|
||||
}
|
||||
}
|
||||
|
||||
double maxBtc = 0;
|
||||
double minBtc = 0;
|
||||
|
||||
@@ -152,7 +144,6 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
etAmount.setAmount(data.amount);
|
||||
}
|
||||
}
|
||||
setBip70Mode();
|
||||
callXmrTo();
|
||||
}
|
||||
|
||||
@@ -164,45 +155,37 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
|
||||
private void processOrderParms(final QueryOrderParameters orderParameters) {
|
||||
this.orderParameters = orderParameters;
|
||||
getView().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
etAmount.setExchangeRate(1.0d / orderParameters.getPrice());
|
||||
NumberFormat df = NumberFormat.getInstance(Locale.US);
|
||||
df.setMaximumFractionDigits(6);
|
||||
String min = df.format(orderParameters.getLowerLimit());
|
||||
String max = df.format(orderParameters.getUpperLimit());
|
||||
String rate = df.format(orderParameters.getPrice());
|
||||
Spanned xmrParmText = Html.fromHtml(getString(R.string.info_send_xmrto_parms, min, max, rate));
|
||||
if (orderParameters.isZeroConfEnabled()) {
|
||||
String zeroConf = df.format(orderParameters.getZeroConfMaxAmount());
|
||||
Spanned zeroConfText = Html.fromHtml(getString(R.string.info_send_xmrto_zeroconf, zeroConf));
|
||||
xmrParmText = (Spanned) TextUtils.concat(xmrParmText, " ", zeroConfText);
|
||||
}
|
||||
tvXmrToParms.setText(xmrParmText);
|
||||
maxBtc = orderParameters.getUpperLimit();
|
||||
minBtc = orderParameters.getLowerLimit();
|
||||
Timber.d("minBtc=%f / maxBtc=%f", minBtc, maxBtc);
|
||||
getView().post(() -> {
|
||||
final double price = orderParameters.getPrice();
|
||||
etAmount.setExchangeRate(1 / price);
|
||||
maxBtc = price * orderParameters.getUpperLimit();
|
||||
minBtc = price * orderParameters.getLowerLimit();
|
||||
Timber.d("minBtc=%f / maxBtc=%f", minBtc, maxBtc);
|
||||
NumberFormat df = NumberFormat.getInstance(Locale.US);
|
||||
df.setMaximumFractionDigits(6);
|
||||
String min = df.format(minBtc);
|
||||
String max = df.format(maxBtc);
|
||||
String rate = df.format(price);
|
||||
Spanned xmrParmText = Html.fromHtml(getString(R.string.info_send_xmrto_parms, min, max, rate));
|
||||
tvXmrToParms.setText(xmrParmText);
|
||||
|
||||
final long funds = getTotalFunds();
|
||||
double availableXmr = 1.0 * funds / 1000000000000L;
|
||||
maxBtc = Math.min(maxBtc, availableXmr * orderParameters.getPrice());
|
||||
final long funds = getTotalFunds();
|
||||
double availableXmr = 1.0 * funds / 1000000000000L;
|
||||
|
||||
String availBtcString;
|
||||
String availXmrString;
|
||||
if (!sendListener.getActivityCallback().isStreetMode()) {
|
||||
availBtcString = df.format(availableXmr * orderParameters.getPrice());
|
||||
availXmrString = df.format(availableXmr);
|
||||
} else {
|
||||
availBtcString = getString(R.string.unknown_amount);
|
||||
availXmrString = availBtcString;
|
||||
}
|
||||
tvFunds.setText(getString(R.string.send_available_btc,
|
||||
availXmrString,
|
||||
availBtcString));
|
||||
llXmrToParms.setVisibility(View.VISIBLE);
|
||||
evParams.hideProgress();
|
||||
String availBtcString;
|
||||
String availXmrString;
|
||||
if (!sendListener.getActivityCallback().isStreetMode()) {
|
||||
availBtcString = df.format(availableXmr * price);
|
||||
availXmrString = df.format(availableXmr);
|
||||
} else {
|
||||
availBtcString = getString(R.string.unknown_amount);
|
||||
availXmrString = availBtcString;
|
||||
}
|
||||
tvFunds.setText(getString(R.string.send_available_btc,
|
||||
availXmrString,
|
||||
availBtcString));
|
||||
llXmrToParms.setVisibility(View.VISIBLE);
|
||||
evParams.hideProgress();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -212,44 +195,38 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
maxBtc = 0;
|
||||
minBtc = 0;
|
||||
Timber.e(ex);
|
||||
getView().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (ex instanceof XmrToException) {
|
||||
XmrToException xmrEx = (XmrToException) ex;
|
||||
XmrToError xmrErr = xmrEx.getError();
|
||||
if (xmrErr != null) {
|
||||
if (xmrErr.isRetryable()) {
|
||||
evParams.showMessage(xmrErr.getErrorId().toString(), xmrErr.getErrorMsg(),
|
||||
getString(R.string.text_retry));
|
||||
evParams.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
evParams.setOnClickListener(null);
|
||||
callXmrTo();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
evParams.showMessage(xmrErr.getErrorId().toString(), xmrErr.getErrorMsg(),
|
||||
getString(R.string.text_noretry));
|
||||
}
|
||||
getView().post(() -> {
|
||||
if (ex instanceof ShiftException) {
|
||||
ShiftException xmrEx = (ShiftException) ex;
|
||||
ShiftError xmrErr = xmrEx.getError();
|
||||
if (xmrErr != null) {
|
||||
if (xmrErr.isRetryable()) {
|
||||
evParams.showMessage(xmrErr.getErrorType().toString(), xmrErr.getErrorMsg(),
|
||||
getString(R.string.text_retry));
|
||||
evParams.setOnClickListener(v -> {
|
||||
evParams.setOnClickListener(null);
|
||||
callXmrTo();
|
||||
});
|
||||
} else {
|
||||
evParams.showMessage(getString(R.string.label_generic_xmrto_error),
|
||||
getString(R.string.text_generic_xmrto_error, xmrEx.getCode()),
|
||||
evParams.showMessage(xmrErr.getErrorType().toString(), xmrErr.getErrorMsg(),
|
||||
getString(R.string.text_noretry));
|
||||
}
|
||||
} else {
|
||||
evParams.showMessage(getString(R.string.label_generic_xmrto_error),
|
||||
ex.getLocalizedMessage(),
|
||||
getString(R.string.text_generic_xmrto_error, xmrEx.getCode()),
|
||||
getString(R.string.text_noretry));
|
||||
}
|
||||
} else {
|
||||
evParams.showMessage(getString(R.string.label_generic_xmrto_error),
|
||||
ex.getLocalizedMessage(),
|
||||
getString(R.string.text_noretry));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void callXmrTo() {
|
||||
evParams.showProgress(getString(R.string.label_send_progress_queryparms));
|
||||
getXmrToApi().queryOrderParameters(new XmrToCallback<QueryOrderParameters>() {
|
||||
getXmrToApi().queryOrderParameters(new ShiftCallback<QueryOrderParameters>() {
|
||||
@Override
|
||||
public void onSuccess(final QueryOrderParameters orderParameters) {
|
||||
processOrderParms(orderParameters);
|
||||
@@ -262,13 +239,13 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
});
|
||||
}
|
||||
|
||||
private XmrToApi xmrToApi = null;
|
||||
private SideShiftApi xmrToApi = null;
|
||||
|
||||
private XmrToApi getXmrToApi() {
|
||||
private SideShiftApi getXmrToApi() {
|
||||
if (xmrToApi == null) {
|
||||
synchronized (this) {
|
||||
if (xmrToApi == null) {
|
||||
xmrToApi = new XmrToApiImpl(OkHttpHelper.getOkHttpClient(),
|
||||
xmrToApi = new SideShiftApiImpl(OkHttpHelper.getOkHttpClient(),
|
||||
Helper.getXmrToBaseUrl());
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.m2049r.xmrwallet.fragment.send;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Paint;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -29,13 +32,13 @@ import android.widget.Toast;
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.data.PendingTx;
|
||||
import com.m2049r.xmrwallet.data.TxDataBtc;
|
||||
import com.m2049r.xmrwallet.service.shift.ShiftCallback;
|
||||
import com.m2049r.xmrwallet.service.shift.ShiftException;
|
||||
import com.m2049r.xmrwallet.service.shift.sideshift.api.QueryOrderStatus;
|
||||
import com.m2049r.xmrwallet.service.shift.sideshift.api.SideShiftApi;
|
||||
import com.m2049r.xmrwallet.service.shift.sideshift.network.SideShiftApiImpl;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.OkHttpHelper;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToException;
|
||||
import com.m2049r.xmrwallet.xmrto.api.QueryOrderStatus;
|
||||
import com.m2049r.xmrwallet.xmrto.api.XmrToApi;
|
||||
import com.m2049r.xmrwallet.xmrto.api.XmrToCallback;
|
||||
import com.m2049r.xmrwallet.xmrto.network.XmrToApiImpl;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
@@ -52,9 +55,8 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
||||
|
||||
SendSuccessWizardFragment.Listener sendListener;
|
||||
|
||||
public SendBtcSuccessWizardFragment setSendListener(SendSuccessWizardFragment.Listener listener) {
|
||||
public void setSendListener(SendSuccessWizardFragment.Listener listener) {
|
||||
this.sendListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
ImageButton bCopyTxId;
|
||||
@@ -69,6 +71,7 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
||||
private ImageView ivXmrToStatusBig;
|
||||
private ProgressBar pbXmrto;
|
||||
private TextView tvTxXmrToKey;
|
||||
private TextView tvXmrToSupport;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
@@ -101,14 +104,14 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
||||
pbXmrto.getIndeterminateDrawable().setColorFilter(0x61000000, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
|
||||
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
|
||||
tvTxXmrToKey.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
|
||||
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
tvTxXmrToKey.setOnClickListener(v -> {
|
||||
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
|
||||
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
tvXmrToSupport = view.findViewById(R.id.tvXmrToSupport);
|
||||
tvXmrToSupport.setPaintFlags(tvXmrToSupport.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -149,7 +152,12 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
||||
String btcAmount = df.format(btcData.getBtcAmount());
|
||||
tvXmrToAmount.setText(getString(R.string.info_send_xmrto_success_btc, btcAmount));
|
||||
//TODO btcData.getBtcAddress();
|
||||
tvTxXmrToKey.setText(btcData.getXmrtoUuid());
|
||||
tvTxXmrToKey.setText(btcData.getXmrtoOrderId());
|
||||
tvXmrToSupport.setOnClickListener(v -> {
|
||||
Uri orderUri = getXmrToApi().getQueryOrderUri(btcData.getXmrtoOrderId());
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, orderUri);
|
||||
startActivity(intent);
|
||||
});
|
||||
queryOrder();
|
||||
} else {
|
||||
throw new IllegalStateException("btcData is null");
|
||||
@@ -158,33 +166,23 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
||||
sendListener.enableDone();
|
||||
}
|
||||
|
||||
private final int QUERY_INTERVAL = 1000; // ms
|
||||
|
||||
private void processQueryOrder(final QueryOrderStatus status) {
|
||||
Timber.d("processQueryOrder %s for %s", status.getState().toString(), status.getUuid());
|
||||
if (!btcData.getXmrtoUuid().equals(status.getUuid()))
|
||||
Timber.d("processQueryOrder %s for %s", status.getState().toString(), status.getOrderId());
|
||||
if (!btcData.getXmrtoOrderId().equals(status.getOrderId()))
|
||||
throw new IllegalStateException("UUIDs do not match!");
|
||||
if (isResumed && (getView() != null))
|
||||
getView().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
showXmrToStatus(status);
|
||||
if (!status.isTerminal()) {
|
||||
getView().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
queryOrder();
|
||||
}
|
||||
}, QUERY_INTERVAL);
|
||||
}
|
||||
getView().post(() -> {
|
||||
showXmrToStatus(status);
|
||||
if (!status.isTerminal()) {
|
||||
getView().postDelayed(this::queryOrder, SideShiftApi.QUERY_INTERVAL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void queryOrder() {
|
||||
Timber.d("queryOrder(%s)", btcData.getXmrtoUuid());
|
||||
Timber.d("queryOrder(%s)", btcData.getXmrtoOrderId());
|
||||
if (!isResumed) return;
|
||||
getXmrToApi().queryOrderStatus(btcData.getXmrtoUuid(), new XmrToCallback<QueryOrderStatus>() {
|
||||
getXmrToApi().queryOrderStatus(btcData.getXmrtoOrderId(), new ShiftCallback<QueryOrderStatus>() {
|
||||
@Override
|
||||
public void onSuccess(QueryOrderStatus status) {
|
||||
if (!isAdded()) return;
|
||||
@@ -194,38 +192,34 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
||||
@Override
|
||||
public void onError(final Exception ex) {
|
||||
if (!isResumed) return;
|
||||
Timber.e(ex);
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (ex instanceof XmrToException) {
|
||||
Toast.makeText(getActivity(), ((XmrToException) ex).getError().getErrorMsg(), Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
Toast.makeText(getActivity(), ex.getLocalizedMessage(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
Timber.w(ex);
|
||||
getActivity().runOnUiThread(() -> {
|
||||
if (ex instanceof ShiftException) {
|
||||
Toast.makeText(getActivity(), ((ShiftException) ex).getError().getErrorMsg(), Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
Toast.makeText(getActivity(), ex.getLocalizedMessage(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private int statusResource = 0;
|
||||
|
||||
void showXmrToStatus(final QueryOrderStatus status) {
|
||||
int statusResource = 0;
|
||||
if (status.isError()) {
|
||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_error, status.toString()));
|
||||
statusResource = R.drawable.ic_error_red_24dp;
|
||||
pbXmrto.getIndeterminateDrawable().setColorFilter(0xff8b0000, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
} else if (status.isSent()) {
|
||||
} else if (status.isSent() || status.isPaid()) {
|
||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_sent));
|
||||
statusResource = R.drawable.ic_success_green_24dp;
|
||||
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFF417505, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
} else if (status.isWaiting()) {
|
||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_unpaid));
|
||||
statusResource = R.drawable.ic_pending_orange_24dp;
|
||||
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFFFF6105, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
} else if (status.isPending()) {
|
||||
if (status.isPaid()) {
|
||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_paid));
|
||||
} else {
|
||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_unpaid));
|
||||
}
|
||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_paid));
|
||||
statusResource = R.drawable.ic_pending_orange_24dp;
|
||||
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFFFF6105, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
} else {
|
||||
@@ -240,13 +234,13 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
||||
}
|
||||
}
|
||||
|
||||
private XmrToApi xmrToApi = null;
|
||||
private SideShiftApi xmrToApi = null;
|
||||
|
||||
private final XmrToApi getXmrToApi() {
|
||||
private SideShiftApi getXmrToApi() {
|
||||
if (xmrToApi == null) {
|
||||
synchronized (this) {
|
||||
if (xmrToApi == null) {
|
||||
xmrToApi = new XmrToApiImpl(OkHttpHelper.getOkHttpClient(),
|
||||
xmrToApi = new SideShiftApiImpl(OkHttpHelper.getOkHttpClient(),
|
||||
Helper.getXmrToBaseUrl());
|
||||
}
|
||||
}
|
||||
|
@@ -563,22 +563,4 @@ public class SendFragment extends Fragment
|
||||
inflater.inflate(R.menu.send_menu, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
// xmr.to info box
|
||||
private static final String PREF_SHOW_XMRTO_ENABLED = "info_xmrto_enabled_send";
|
||||
|
||||
boolean showXmrtoEnabled = true;
|
||||
|
||||
void loadPrefs() {
|
||||
SharedPreferences sharedPref = activityCallback.getPrefs();
|
||||
showXmrtoEnabled = sharedPref.getBoolean(PREF_SHOW_XMRTO_ENABLED, true);
|
||||
}
|
||||
|
||||
void saveXmrToPrefs() {
|
||||
SharedPreferences sharedPref = activityCallback.getPrefs();
|
||||
SharedPreferences.Editor editor = sharedPref.edit();
|
||||
editor.putBoolean(PREF_SHOW_XMRTO_ENABLED, showXmrtoEnabled);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
*******************************************************************************
|
||||
* BTChip Bitcoin Hardware Wallet Java API
|
||||
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
|
||||
* (c) m2049r
|
||||
* Copyright (c) 2018 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2018 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.ledger;
|
||||
|
||||
import android.content.Context;
|
||||
|
@@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.xmrto.network;
|
||||
package com.m2049r.xmrwallet.service.shift;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
interface NetworkCallback {
|
||||
public interface NetworkCallback {
|
||||
|
||||
void onSuccess(JSONObject jsonObject);
|
||||
|
@@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.xmrto.network;
|
||||
package com.m2049r.xmrwallet.service.shift;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
interface XmrToApiCall {
|
||||
public interface ShiftApiCall {
|
||||
|
||||
void call(@NonNull final String path, @NonNull final NetworkCallback callback);
|
||||
|
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.xmrto.api;
|
||||
package com.m2049r.xmrwallet.service.shift;
|
||||
|
||||
public interface XmrToCallback<T> {
|
||||
public interface ShiftCallback<T> {
|
||||
|
||||
void onSuccess(T t);
|
||||
|
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2021 m2049r et al.
|
||||
*
|
||||
* 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.service.shift;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ShiftError {
|
||||
@Getter
|
||||
private final Error errorType;
|
||||
@Getter
|
||||
private final String errorMsg;
|
||||
|
||||
public enum Error {
|
||||
SERVICE,
|
||||
INFRASTRUCTURE
|
||||
}
|
||||
|
||||
public boolean isRetryable() {
|
||||
return errorType == Error.INFRASTRUCTURE;
|
||||
}
|
||||
|
||||
public ShiftError(final JSONObject jsonObject) throws JSONException {
|
||||
final JSONObject errorObject = jsonObject.getJSONObject("error");
|
||||
errorType = Error.SERVICE;
|
||||
errorMsg = errorObject.getString("message");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return getErrorMsg();
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r et al.
|
||||
* Copyright (c) 2017-2021 m2049r et al.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,29 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.xmrto;
|
||||
package com.m2049r.xmrwallet.service.shift;
|
||||
|
||||
public class XmrToException extends Exception {
|
||||
private int code;
|
||||
private XmrToError error;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
public XmrToException(final int code) {
|
||||
super();
|
||||
@RequiredArgsConstructor
|
||||
public class ShiftException extends Exception {
|
||||
@Getter
|
||||
private final int code;
|
||||
@Getter
|
||||
private final ShiftError error;
|
||||
|
||||
public ShiftException(int code) {
|
||||
this.code = code;
|
||||
this.error = null;
|
||||
}
|
||||
|
||||
public XmrToException(final int code, final XmrToError error) {
|
||||
super();
|
||||
this.code = code;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public XmrToError getError() {
|
||||
return error;
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2021 m2049r et al.
|
||||
*
|
||||
* 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.service.shift.sideshift.api;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public interface CreateOrder {
|
||||
String TAG = "side";
|
||||
|
||||
double getBtcAmount();
|
||||
|
||||
String getBtcAddress();
|
||||
|
||||
String getQuoteId();
|
||||
|
||||
String getOrderId();
|
||||
|
||||
double getXmrAmount();
|
||||
|
||||
String getXmrAddress();
|
||||
|
||||
Date getCreatedAt(); // createdAt
|
||||
|
||||
Date getExpiresAt(); // expiresAt
|
||||
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r et al.
|
||||
* Copyright (c) 2017-2021 m2049r et al.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,17 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.xmrto.api;
|
||||
package com.m2049r.xmrwallet.service.shift.sideshift.api;
|
||||
|
||||
public interface CreateOrder {
|
||||
Double getBtcAmount();
|
||||
public interface QueryOrderParameters {
|
||||
|
||||
String getBtcDestAddress();
|
||||
double getLowerLimit();
|
||||
|
||||
String getBtcBip70();
|
||||
double getPrice();
|
||||
|
||||
String getState();
|
||||
|
||||
String getUuid();
|
||||
double getUpperLimit();
|
||||
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r et al.
|
||||
* Copyright (c) 2017-2021 m2049r et al.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,60 +14,52 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.xmrto.api;
|
||||
package com.m2049r.xmrwallet.service.shift.sideshift.api;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public interface QueryOrderStatus {
|
||||
enum State {
|
||||
UNDEF,
|
||||
TO_BE_CREATED, // order creation pending
|
||||
UNPAID, // waiting for Monero payment by user
|
||||
UNDERPAID, // order partially paid
|
||||
PAID_UNCONFIRMED, // order paid, waiting for enough confirmations
|
||||
PAID, // order paid and sufficiently confirmed
|
||||
BTC_SENT, // bitcoin payment sent
|
||||
TIMED_OUT, // order timed out before payment was complete
|
||||
NOT_FOUND // order wasn’t found in system (never existed or was purged)
|
||||
WAITING, // Waiting for mempool
|
||||
PENDING, // Detected (waiting for confirmations)
|
||||
SETTLING, // Settlement in progress
|
||||
SETTLED, // Settlement completed
|
||||
// no refunding in monerujo so theese are ignored:
|
||||
// REFUND, // Queued for refund
|
||||
// REFUNDING, // Refund in progress
|
||||
// REFUNDED // Refund completed
|
||||
UNDEFINED
|
||||
}
|
||||
|
||||
boolean isCreated();
|
||||
|
||||
boolean isTerminal();
|
||||
|
||||
boolean isWaiting();
|
||||
|
||||
boolean isPending();
|
||||
|
||||
boolean isPaid();
|
||||
|
||||
boolean isSent();
|
||||
|
||||
boolean isPaid();
|
||||
|
||||
boolean isError();
|
||||
|
||||
QueryOrderStatus.State getState();
|
||||
|
||||
double getBtcAmount();
|
||||
|
||||
String getBtcDestAddress();
|
||||
|
||||
String getUuid();
|
||||
|
||||
int getBtcNumConfirmationsThreshold();
|
||||
String getOrderId();
|
||||
|
||||
Date getCreatedAt();
|
||||
|
||||
Date getExpiresAt();
|
||||
|
||||
int getSecondsTillTimeout();
|
||||
double getBtcAmount();
|
||||
|
||||
double getIncomingAmountTotal();
|
||||
String getBtcAddress();
|
||||
|
||||
double getRemainingAmountIncoming();
|
||||
double getXmrAmount();
|
||||
|
||||
int getIncomingNumConfirmationsRemaining();
|
||||
String getXmrAddress();
|
||||
|
||||
double getIncomingPriceBtc();
|
||||
|
||||
String getReceivingSubaddress();
|
||||
|
||||
int getRecommendedMixin();
|
||||
}
|
||||
double getPrice();
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2021 m2049r et al.
|
||||
*
|
||||
* 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.service.shift.sideshift.api;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public interface RequestQuote {
|
||||
|
||||
double getBtcAmount(); // settleAmount
|
||||
|
||||
String getId(); // id
|
||||
|
||||
Date getCreatedAt(); // createdAt
|
||||
|
||||
Date getExpiresAt(); // expiresAt
|
||||
|
||||
double getXmrAmount(); // depositAmount
|
||||
|
||||
double getPrice(); // rate
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2021 m2049r et al.
|
||||
*
|
||||
* 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.service.shift.sideshift.api;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.m2049r.xmrwallet.service.shift.ShiftCallback;
|
||||
|
||||
public interface SideShiftApi {
|
||||
|
||||
String ASSET = "btc";
|
||||
int QUERY_INTERVAL = 5000; // ms
|
||||
|
||||
/**
|
||||
* Queries the order parameter.
|
||||
*
|
||||
* @param callback the callback with the OrderParameter object
|
||||
*/
|
||||
void queryOrderParameters(@NonNull final ShiftCallback<QueryOrderParameters> callback);
|
||||
|
||||
/**
|
||||
* Creates an order
|
||||
*
|
||||
* @param xmrAmount the desired XMR amount
|
||||
*/
|
||||
void requestQuote(final double xmrAmount, @NonNull final ShiftCallback<RequestQuote> callback);
|
||||
|
||||
/**
|
||||
* Creates an order
|
||||
*
|
||||
* @param quoteId the desired XMR amount
|
||||
* @param btcAddress the target bitcoin address
|
||||
*/
|
||||
void createOrder(final String quoteId, @NonNull final String btcAddress, @NonNull final ShiftCallback<CreateOrder> callback);
|
||||
|
||||
/**
|
||||
* Queries the order status for given current order
|
||||
*
|
||||
* @param orderId the order ID
|
||||
* @param callback the callback with the OrderStatus object
|
||||
*/
|
||||
void queryOrderStatus(@NonNull final String orderId, @NonNull final ShiftCallback<QueryOrderStatus> callback);
|
||||
|
||||
/*
|
||||
* Returns the URL for manually querying the order status
|
||||
*
|
||||
* @param orderId the order ID
|
||||
*/
|
||||
Uri getQueryOrderUri(String orderId);
|
||||
}
|
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2021 m2049r et al.
|
||||
*
|
||||
* 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.service.shift.sideshift.network;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.m2049r.xmrwallet.BuildConfig;
|
||||
import com.m2049r.xmrwallet.service.shift.NetworkCallback;
|
||||
import com.m2049r.xmrwallet.service.shift.ShiftApiCall;
|
||||
import com.m2049r.xmrwallet.service.shift.ShiftCallback;
|
||||
import com.m2049r.xmrwallet.service.shift.sideshift.api.CreateOrder;
|
||||
import com.m2049r.xmrwallet.service.shift.sideshift.api.SideShiftApi;
|
||||
import com.m2049r.xmrwallet.util.DateHelper;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
class CreateOrderImpl implements CreateOrder {
|
||||
@Getter
|
||||
private final double btcAmount;
|
||||
@Getter
|
||||
private final String btcAddress;
|
||||
@Getter
|
||||
private final String quoteId;
|
||||
@Getter
|
||||
private final String orderId;
|
||||
@Getter
|
||||
private final double xmrAmount;
|
||||
@Getter
|
||||
private final String xmrAddress;
|
||||
@Getter
|
||||
private final Date createdAt;
|
||||
@Getter
|
||||
private final Date expiresAt;
|
||||
|
||||
CreateOrderImpl(final JSONObject jsonObject) throws JSONException {
|
||||
// sanity checks
|
||||
final String depositMethod = jsonObject.getString("depositMethodId");
|
||||
final String settleMethod = jsonObject.getString("settleMethodId");
|
||||
if (!"xmr".equals(depositMethod) || !SideShiftApi.ASSET.equals(settleMethod))
|
||||
throw new IllegalStateException();
|
||||
|
||||
btcAmount = jsonObject.getDouble("settleAmount");
|
||||
JSONObject settleAddress = jsonObject.getJSONObject("settleAddress");
|
||||
btcAddress = settleAddress.getString("address");
|
||||
|
||||
xmrAmount = jsonObject.getDouble("depositAmount");
|
||||
JSONObject depositAddress = jsonObject.getJSONObject("depositAddress");
|
||||
xmrAddress = depositAddress.getString("address");
|
||||
|
||||
quoteId = jsonObject.getString("quoteId");
|
||||
|
||||
orderId = jsonObject.getString("orderId");
|
||||
|
||||
try {
|
||||
final String created = jsonObject.getString("createdAtISO");
|
||||
createdAt = DateHelper.parse(created);
|
||||
final String expires = jsonObject.getString("expiresAtISO");
|
||||
expiresAt = DateHelper.parse(expires);
|
||||
} catch (ParseException ex) {
|
||||
throw new JSONException(ex.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static void call(@NonNull final ShiftApiCall api, final String quoteId, @NonNull final String btcAddress,
|
||||
@NonNull final ShiftCallback<CreateOrder> callback) {
|
||||
try {
|
||||
final JSONObject request = createRequest(quoteId, btcAddress);
|
||||
api.call("orders", request, new NetworkCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject jsonObject) {
|
||||
try {
|
||||
callback.onSuccess(new CreateOrderImpl(jsonObject));
|
||||
} catch (JSONException ex) {
|
||||
callback.onError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
callback.onError(ex);
|
||||
}
|
||||
});
|
||||
} catch (JSONException ex) {
|
||||
callback.onError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static JSONObject createRequest(final String quoteId, final String address) throws JSONException {
|
||||
final JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("type", "fixed");
|
||||
jsonObject.put("quoteId", quoteId);
|
||||
jsonObject.put("settleAddress", address);
|
||||
if (!BuildConfig.ID_A.isEmpty() && !"null".equals(BuildConfig.ID_A)) {
|
||||
jsonObject.put("affiliateId", BuildConfig.ID_A);
|
||||
}
|
||||
return jsonObject;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user