parent
d855328c52
commit
f8e701aa07
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.data;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||
|
||||
public class TxDataBtc extends TxData {
|
||||
|
||||
private String xmrtoUuid;
|
||||
private String btcAddress;
|
||||
private double btcAmount;
|
||||
|
||||
public TxDataBtc() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TxDataBtc(TxDataBtc txDataBtc) {
|
||||
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 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(btcAddress);
|
||||
out.writeDouble(btcAmount);
|
||||
}
|
||||
|
||||
// this is used to regenerate your object. All Parcelables must have a CREATOR that implements these two methods
|
||||
public static final Creator<TxDataBtc> CREATOR = new Creator<TxDataBtc>() {
|
||||
public TxDataBtc createFromParcel(Parcel in) {
|
||||
return new TxDataBtc(in);
|
||||
}
|
||||
|
||||
public TxDataBtc[] newArray(int size) {
|
||||
return new TxDataBtc[size];
|
||||
}
|
||||
};
|
||||
|
||||
protected TxDataBtc(Parcel in) {
|
||||
super(in);
|
||||
xmrtoUuid = in.readString();
|
||||
btcAddress = in.readString();
|
||||
btcAmount = in.readDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(",xmrtoUuid:");
|
||||
sb.append(xmrtoUuid);
|
||||
sb.append(",btcAddress:");
|
||||
sb.append(btcAddress);
|
||||
sb.append(",btcAmount:");
|
||||
sb.append(btcAmount);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.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;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.data.BarcodeData;
|
||||
import com.m2049r.xmrwallet.data.TxDataBtc;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.OkHttpClientSingleton;
|
||||
import com.m2049r.xmrwallet.widget.ExchangeBtcTextView;
|
||||
import com.m2049r.xmrwallet.widget.NumberPadView;
|
||||
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 java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
|
||||
public static SendBtcAmountWizardFragment newInstance(SendAmountWizardFragment.Listener listener) {
|
||||
SendBtcAmountWizardFragment instance = new SendBtcAmountWizardFragment();
|
||||
instance.setSendListener(listener);
|
||||
return instance;
|
||||
}
|
||||
|
||||
SendAmountWizardFragment.Listener sendListener;
|
||||
|
||||
public SendBtcAmountWizardFragment setSendListener(SendAmountWizardFragment.Listener listener) {
|
||||
this.sendListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
private TextView tvFunds;
|
||||
private ExchangeBtcTextView evAmount;
|
||||
private NumberPadView numberPad;
|
||||
|
||||
private TextView tvXmrToParms;
|
||||
private SendProgressView evParams;
|
||||
private View llXmrToParms;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
|
||||
Timber.d("onCreateView() %s", (String.valueOf(savedInstanceState)));
|
||||
|
||||
sendListener = (SendAmountWizardFragment.Listener) getParentFragment();
|
||||
|
||||
View view = inflater.inflate(R.layout.fragment_send_btc_amount, container, false);
|
||||
|
||||
tvFunds = (TextView) view.findViewById(R.id.tvFunds);
|
||||
|
||||
evParams = (SendProgressView) view.findViewById(R.id.evXmrToParms);
|
||||
llXmrToParms = view.findViewById(R.id.llXmrToParms);
|
||||
|
||||
tvXmrToParms = (TextView) view.findViewById(R.id.tvXmrToParms);
|
||||
|
||||
evAmount = (ExchangeBtcTextView) view.findViewById(R.id.evAmount);
|
||||
numberPad = (NumberPadView) view.findViewById(R.id.numberPad);
|
||||
numberPad.setListener(evAmount);
|
||||
|
||||
Helper.hideKeyboard(getActivity());
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onValidateFields() {
|
||||
if (!evAmount.validate(maxBtc, minBtc)) {
|
||||
return false;
|
||||
}
|
||||
if (sendListener != null) {
|
||||
TxDataBtc txDataBtc = (TxDataBtc) sendListener.getTxData();
|
||||
String btcString = evAmount.getAmount();
|
||||
if (btcString != null) {
|
||||
try {
|
||||
double btc = Double.parseDouble(btcString);
|
||||
Timber.d("setAmount %f", btc);
|
||||
txDataBtc.setBtcAmount(btc);
|
||||
} catch (NumberFormatException ex) {
|
||||
Timber.d(ex.getLocalizedMessage());
|
||||
txDataBtc.setBtcAmount(0);
|
||||
}
|
||||
} else {
|
||||
txDataBtc.setBtcAmount(0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
double maxBtc = 0;
|
||||
double minBtc = 0;
|
||||
|
||||
@Override
|
||||
public void onPauseFragment() {
|
||||
llXmrToParms.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResumeFragment() {
|
||||
super.onResumeFragment();
|
||||
Timber.d("onResumeFragment()");
|
||||
Helper.hideKeyboard(getActivity());
|
||||
final long funds = getTotalFunds();
|
||||
tvFunds.setText(getString(R.string.send_available,
|
||||
Wallet.getDisplayAmount(funds)));
|
||||
if ((evAmount.getAmount() == null) || evAmount.getAmount().isEmpty()) {
|
||||
final BarcodeData data = sendListener.popBarcodeData();
|
||||
if ((data != null) && (data.amount != null)) {
|
||||
evAmount.setAmount(data.amount);
|
||||
}
|
||||
}
|
||||
callXmrTo();
|
||||
}
|
||||
|
||||
long getTotalFunds() {
|
||||
return sendListener.getActivityCallback().getTotalFunds();
|
||||
}
|
||||
|
||||
private QueryOrderParameters orderParameters = null;
|
||||
|
||||
private void processOrderParms(final QueryOrderParameters orderParameters) {
|
||||
this.orderParameters = orderParameters;
|
||||
getView().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
evAmount.setRate(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);
|
||||
|
||||
final long funds = getTotalFunds();
|
||||
double availableXmr = 1.0 * funds / 1000000000000L;
|
||||
maxBtc = Math.min(maxBtc, availableXmr * orderParameters.getPrice());
|
||||
|
||||
String availBtcString = df.format(availableXmr * orderParameters.getPrice());
|
||||
String availXmrString = df.format(availableXmr);
|
||||
tvFunds.setText(getString(R.string.send_available_btc,
|
||||
availXmrString,
|
||||
availBtcString));
|
||||
llXmrToParms.setVisibility(View.VISIBLE);
|
||||
evParams.hideProgress();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void processOrderParmsError(final Exception ex) {
|
||||
evAmount.setRate(0);
|
||||
orderParameters = null;
|
||||
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));
|
||||
}
|
||||
} else {
|
||||
evParams.showMessage(getString(R.string.label_generic_xmrto_error),
|
||||
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>() {
|
||||
@Override
|
||||
public void onSuccess(final QueryOrderParameters orderParameters) {
|
||||
processOrderParms(orderParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Exception e) {
|
||||
processOrderParmsError(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private XmrToApi xmrToApi = null;
|
||||
|
||||
private final XmrToApi getXmrToApi() {
|
||||
if (xmrToApi == null) {
|
||||
synchronized (this) {
|
||||
if (xmrToApi == null) {
|
||||
xmrToApi = new XmrToApiImpl(OkHttpClientSingleton.getOkHttpClient(),
|
||||
Helper.getXmrToBaseUrl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return xmrToApi;
|
||||
}
|
||||
}
|
@ -0,0 +1,680 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.fragment.send;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.design.widget.TextInputLayout;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.data.TxData;
|
||||
import com.m2049r.xmrwallet.data.TxDataBtc;
|
||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.OkHttpClientSingleton;
|
||||
import com.m2049r.xmrwallet.widget.SendProgressView;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToError;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToException;
|
||||
import com.m2049r.xmrwallet.xmrto.api.CreateOrder;
|
||||
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;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class SendBtcConfirmWizardFragment extends SendWizardFragment implements SendConfirm {
|
||||
private final int QUERY_INTERVAL = 500;//ms
|
||||
|
||||
public static SendBtcConfirmWizardFragment newInstance(SendConfirmWizardFragment.Listener listener) {
|
||||
SendBtcConfirmWizardFragment instance = new SendBtcConfirmWizardFragment();
|
||||
instance.setSendListener(listener);
|
||||
return instance;
|
||||
}
|
||||
|
||||
SendConfirmWizardFragment.Listener sendListener;
|
||||
|
||||
public SendBtcConfirmWizardFragment setSendListener(SendConfirmWizardFragment.Listener listener) {
|
||||
this.sendListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
private View llStageA;
|
||||
private SendProgressView evStageA;
|
||||
private View llStageB;
|
||||
private SendProgressView evStageB;
|
||||
private View llStageC;
|
||||
private SendProgressView evStageC;
|
||||
private TextView tvTxBtcAmount;
|
||||
private TextView tvTxBtcRate;
|
||||
private TextView tvTxBtcAddress;
|
||||
private TextView tvTxXmrToKey;
|
||||
private TextView tvTxFee;
|
||||
private TextView tvTxTotal;
|
||||
private View llConfirmSend;
|
||||
private Button bSend;
|
||||
private View pbProgressSend;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
|
||||
Timber.d("onCreateView(%s)", (String.valueOf(savedInstanceState)));
|
||||
|
||||
View view = inflater.inflate(
|
||||
R.layout.fragment_send_btc_confirm, container, false);
|
||||
|
||||
tvTxBtcAddress = (TextView) view.findViewById(R.id.tvTxBtcAddress);
|
||||
tvTxBtcAmount = ((TextView) view.findViewById(R.id.tvTxBtcAmount));
|
||||
tvTxBtcRate = (TextView) view.findViewById(R.id.tvTxBtcRate);
|
||||
tvTxXmrToKey = (TextView) view.findViewById(R.id.tvTxXmrToKey);
|
||||
|
||||
tvTxFee = (TextView) view.findViewById(R.id.tvTxFee);
|
||||
tvTxTotal = (TextView) view.findViewById(R.id.tvTxTotal);
|
||||
|
||||
|
||||
llStageA = view.findViewById(R.id.llStageA);
|
||||
evStageA = (SendProgressView) view.findViewById(R.id.evStageA);
|
||||
llStageB = view.findViewById(R.id.llStageB);
|
||||
evStageB = (SendProgressView) view.findViewById(R.id.evStageB);
|
||||
llStageC = view.findViewById(R.id.llStageC);
|
||||
evStageC = (SendProgressView) view.findViewById(R.id.evStageC);
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
llConfirmSend = view.findViewById(R.id.llConfirmSend);
|
||||
pbProgressSend = view.findViewById(R.id.pbProgressSend);
|
||||
|
||||
bSend = (Button) view.findViewById(R.id.bSend);
|
||||
bSend.setEnabled(false);
|
||||
|
||||
bSend.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Timber.d("bSend.setOnClickListener");
|
||||
bSend.setEnabled(false);
|
||||
preSend();
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
int inProgress = 0;
|
||||
final static int STAGE_X = 0;
|
||||
final static int STAGE_A = 1;
|
||||
final static int STAGE_B = 2;
|
||||
final static int STAGE_C = 3;
|
||||
|
||||
private void showProgress(int stage, String progressText) {
|
||||
Timber.d("showProgress(%d)", stage);
|
||||
inProgress = stage;
|
||||
switch (stage) {
|
||||
case STAGE_A:
|
||||
evStageA.showProgress(progressText);
|
||||
break;
|
||||
case STAGE_B:
|
||||
evStageB.showProgress(progressText);
|
||||
break;
|
||||
case STAGE_C:
|
||||
evStageC.showProgress(progressText);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("unknown stage " + stage);
|
||||
}
|
||||
}
|
||||
|
||||
public void hideProgress() {
|
||||
Timber.d("hideProgress(%d)", inProgress);
|
||||
switch (inProgress) {
|
||||
case STAGE_A:
|
||||
evStageA.hideProgress();
|
||||
llStageA.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
case STAGE_B:
|
||||
evStageB.hideProgress();
|
||||
llStageB.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
case STAGE_C:
|
||||
evStageC.hideProgress();
|
||||
llStageC.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("unknown stage " + inProgress);
|
||||
}
|
||||
inProgress = STAGE_X;
|
||||
}
|
||||
|
||||
public void showStageError(String code, String message, String solution) {
|
||||
switch (inProgress) {
|
||||
case STAGE_A:
|
||||
evStageA.showMessage(code, message, solution);
|
||||
break;
|
||||
case STAGE_B:
|
||||
evStageB.showMessage(code, message, solution);
|
||||
break;
|
||||
case STAGE_C:
|
||||
evStageC.showMessage(code, message, solution);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("unknown stage");
|
||||
}
|
||||
inProgress = STAGE_X;
|
||||
}
|
||||
|
||||
PendingTransaction pendingTransaction = null;
|
||||
|
||||
void send() {
|
||||
Timber.d("SEND @%d", sendCountdown);
|
||||
if (sendCountdown <= 0) {
|
||||
Timber.i("User waited too long in password dialog.");
|
||||
Toast.makeText(getContext(), getString(R.string.send_xmrto_timeout), Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
sendListener.getTxData().getUserNotes().setXmrtoStatus(xmrtoStatus);
|
||||
((TxDataBtc) sendListener.getTxData()).setXmrtoUuid(xmrtoStatus.getUuid());
|
||||
// TODO make method in TxDataBtc to set both of the above in one go
|
||||
sendListener.commitTransaction();
|
||||
pbProgressSend.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFailed() {
|
||||
Timber.e("SEND FAILED");
|
||||
pbProgressSend.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
// callback from wallet when PendingTransaction created (started by prepareSend() here
|
||||
public void transactionCreated(final String txTag, final PendingTransaction pendingTransaction) {
|
||||
if (isResumed
|
||||
&& (inProgress == STAGE_C)
|
||||
&& (xmrtoStatus != null)
|
||||
&& (xmrtoStatus.isCreated()
|
||||
&& (xmrtoStatus.getUuid().equals(txTag)))) {
|
||||
this.pendingTransaction = pendingTransaction;
|
||||
getView().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
hideProgress();
|
||||
tvTxFee.setText(Wallet.getDisplayAmount(pendingTransaction.getFee()));
|
||||
tvTxTotal.setText(Wallet.getDisplayAmount(
|
||||
pendingTransaction.getFee() + pendingTransaction.getAmount()));
|
||||
updateSendButton();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.pendingTransaction = null;
|
||||
sendListener.disposeTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createTransactionFailed(String errorText) {
|
||||
Timber.e("CREATE TX FAILED");
|
||||
if (pendingTransaction != null) {
|
||||
throw new IllegalStateException("pendingTransaction is not null");
|
||||
}
|
||||
showStageError(getString(R.string.send_create_tx_error_title),
|
||||
errorText,
|
||||
getString(R.string.text_noretry_monero));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onValidateFields() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isResumed = false;
|
||||
|
||||
@Override
|
||||
public void onPauseFragment() {
|
||||
isResumed = false;
|
||||
stopSendTimer();
|
||||
sendListener.disposeTransaction();
|
||||
pendingTransaction = null;
|
||||
inProgress = STAGE_X;
|
||||
updateSendButton();
|
||||
super.onPauseFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResumeFragment() {
|
||||
super.onResumeFragment();
|
||||
Timber.d("onResumeFragment()");
|
||||
if (sendListener.getMode() != SendFragment.Mode.BTC) {
|
||||
throw new IllegalStateException("Mode is not BTC!");
|
||||
}
|
||||
Helper.hideKeyboard(getActivity());
|
||||
llStageA.setVisibility(View.INVISIBLE);
|
||||
evStageA.hideProgress();
|
||||
llStageB.setVisibility(View.INVISIBLE);
|
||||
evStageB.hideProgress();
|
||||
llStageC.setVisibility(View.INVISIBLE);
|
||||
evStageC.hideProgress();
|
||||
isResumed = true;
|
||||
if ((pendingTransaction == null) && (inProgress == STAGE_X)) {
|
||||
createOrder();
|
||||
} // otherwise just sit there blank
|
||||
// TODO: don't sit there blank - can this happen? should we just die?
|
||||
}
|
||||
|
||||
private int sendCountdown = 0;
|
||||
private static final int XMRTO_COUNTDOWN = 10 * 60; // 10 minutes
|
||||
private static final int XMRTO_COUNTDOWN_STEP = 1; // 1 second
|
||||
|
||||
Runnable updateRunnable = null;
|
||||
|
||||
void startSendTimer() {
|
||||
Timber.d("startSendTimer()");
|
||||
sendCountdown = XMRTO_COUNTDOWN;
|
||||
updateRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isAdded())
|
||||
return;
|
||||
Timber.d("updateTimer()");
|
||||
if (sendCountdown <= 0) {
|
||||
bSend.setEnabled(false);
|
||||
sendCountdown = 0;
|
||||
Toast.makeText(getContext(), getString(R.string.send_xmrto_timeout), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
int minutes = sendCountdown / 60;
|
||||
int seconds = sendCountdown % 60;
|
||||
String t = String.format("%d:%02d", minutes, seconds);
|
||||
bSend.setText(getString(R.string.send_send_timed_label, t));
|
||||
if (sendCountdown > 0) {
|
||||
sendCountdown -= XMRTO_COUNTDOWN_STEP;
|
||||
getView().postDelayed(this, XMRTO_COUNTDOWN_STEP * 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
getView().post(updateRunnable);
|
||||
}
|
||||
|
||||
void stopSendTimer() {
|
||||
getView().removeCallbacks(updateRunnable);
|
||||
}
|
||||
|
||||
void updateSendButton() {
|
||||
Timber.d("updateSendButton()");
|
||||
if (pendingTransaction != null) {
|
||||
llConfirmSend.setVisibility(View.VISIBLE);
|
||||
bSend.setEnabled(sendCountdown > 0);
|
||||
} else {
|
||||
llConfirmSend.setVisibility(View.GONE);
|
||||
bSend.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void preSend() {
|
||||
final Activity activity = getActivity();
|
||||
View promptsView = getLayoutInflater().inflate(R.layout.prompt_password, null);
|
||||
android.app.AlertDialog.Builder alertDialogBuilder = new android.app.AlertDialog.Builder(activity);
|
||||
alertDialogBuilder.setView(promptsView);
|
||||
|
||||
final TextInputLayout etPassword = (TextInputLayout) promptsView.findViewById(R.id.etPassword);
|
||||
etPassword.setHint(getString(R.string.prompt_send_password));
|
||||
|
||||
etPassword.getEditText().addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (etPassword.getError() != null) {
|
||||
etPassword.setError(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start,
|
||||
int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start,
|
||||
int before, int count) {
|
||||
}
|
||||
});
|
||||
|
||||
alertDialogBuilder
|
||||
.setCancelable(false)
|
||||
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
String pass = etPassword.getEditText().getText().toString();
|
||||
if (getActivityCallback().verifyWalletPassword(pass)) {
|
||||
dialog.dismiss();
|
||||
Helper.hideKeyboardAlways(activity);
|
||||
send();
|
||||
} else {
|
||||
etPassword.setError(getString(R.string.bad_password));
|
||||
}
|
||||
}
|
||||
})
|
||||
.setNegativeButton("Cancel",
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
Helper.hideKeyboardAlways(activity);
|
||||
dialog.cancel();
|
||||
bSend.setEnabled(sendCountdown > 0); // allow to try again
|
||||
}
|
||||
});
|
||||
|
||||
final android.app.AlertDialog passwordDialog = alertDialogBuilder.create();
|
||||
passwordDialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||
@Override
|
||||
public void onShow(DialogInterface dialog) {
|
||||
Button button = ((android.app.AlertDialog) dialog).getButton(android.app.AlertDialog.BUTTON_POSITIVE);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
String pass = etPassword.getEditText().getText().toString();
|
||||
if (getActivityCallback().verifyWalletPassword(pass)) {
|
||||
Helper.hideKeyboardAlways(activity);
|
||||
passwordDialog.dismiss();
|
||||
send();
|
||||
} else {
|
||||
etPassword.setError(getString(R.string.bad_password));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Helper.showKeyboard(passwordDialog);
|
||||
|
||||
// accept keyboard "ok"
|
||||
etPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) {
|
||||
String pass = etPassword.getEditText().getText().toString();
|
||||
if (getActivityCallback().verifyWalletPassword(pass)) {
|
||||
Helper.hideKeyboardAlways(activity);
|
||||
passwordDialog.dismiss();
|
||||
send();
|
||||
} else {
|
||||
etPassword.setError(getString(R.string.bad_password));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
passwordDialog.show();
|
||||
}
|
||||
|
||||
// creates a pending transaction and calls us back with transactionCreated()
|
||||
// or createTransactionFailed()
|
||||
void prepareSend() {
|
||||
if (!isResumed) return;
|
||||
if ((xmrtoStatus == null)) {
|
||||
throw new IllegalStateException("xmrtoStatus is null");
|
||||
}
|
||||
if ((!xmrtoStatus.isCreated())) {
|
||||
throw new IllegalStateException("order is not created");
|
||||
}
|
||||
showProgress(3, getString(R.string.label_send_progress_create_tx));
|
||||
TxData txData = sendListener.getTxData();
|
||||
txData.setDestinationAddress(xmrtoStatus.getXmrReceivingAddress());
|
||||
txData.setPaymentId(xmrtoStatus.getXmrRequiredPaymentIdShort());
|
||||
txData.setAmount(Wallet.getAmountFromDouble(xmrtoStatus.getXmrAmountTotal()));
|
||||
getActivityCallback().onPrepareSend(xmrtoStatus.getUuid(), txData);
|
||||
}
|
||||
|
||||
SendFragment.Listener getActivityCallback() {
|
||||
return sendListener.getActivityCallback();
|
||||
}
|
||||
|
||||
private CreateOrder xmrtoOrder = null;
|
||||
|
||||
private void processCreateOrder(final CreateOrder createOrder) {
|
||||
Timber.d("processCreateOrder %s", createOrder.getUuid());
|
||||
xmrtoOrder = createOrder;
|
||||
if (QueryOrderStatus.State.TO_BE_CREATED.toString().equals(createOrder.getState())) {
|
||||
getView().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
tvTxXmrToKey.setText(createOrder.getUuid());
|
||||
tvTxBtcAddress.setText(createOrder.getBtcDestAddress());
|
||||
hideProgress();
|
||||
}
|
||||
});
|
||||
queryOrder(createOrder.getUuid());
|
||||
} else {
|
||||
throw new IllegalStateException("Create Order is not TO_BE_CREATED");
|
||||
}
|
||||
}
|
||||
|
||||
private void processCreateOrderError(final Exception ex) {
|
||||
Timber.e("processCreateOrderError %s", ex.getLocalizedMessage());
|
||||
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()) {
|
||||
showStageError(xmrErr.getErrorId().toString(), xmrErr.getErrorMsg(),
|
||||
getString(R.string.text_retry));
|
||||
evStageA.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
evStageA.setOnClickListener(null);
|
||||
createOrder();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showStageError(xmrErr.getErrorId().toString(), xmrErr.getErrorMsg(),
|
||||
getString(R.string.text_noretry));
|
||||
}
|
||||
} else {
|
||||
showStageError(getString(R.string.label_generic_xmrto_error),
|
||||
getString(R.string.text_generic_xmrto_error, xmrEx.getCode()),
|
||||
getString(R.string.text_noretry));
|
||||
}
|
||||
} else {
|
||||
evStageA.showMessage(getString(R.string.label_generic_xmrto_error),
|
||||
ex.getLocalizedMessage(),
|
||||
getString(R.string.text_noretry));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createOrder() {
|
||||
if (!isResumed) return;
|
||||
Timber.d("createOrder");
|
||||
xmrtoOrder = null;
|
||||
xmrtoStatus = null;
|
||||
showProgress(1, getString(R.string.label_send_progress_xmrto_create));
|
||||
TxDataBtc txDataBtc = (TxDataBtc) sendListener.getTxData();
|
||||
double btcAmount = txDataBtc.getBtcAmount();
|
||||
getXmrToApi().createOrder(btcAmount, txDataBtc.getBtcAddress(), new XmrToCallback<CreateOrder>() {
|
||||
@Override
|
||||
public void onSuccess(CreateOrder createOrder) {
|
||||
if (!isResumed) return;
|
||||
if (xmrtoOrder != null) {
|
||||
Timber.w("another ongoing create order request");
|
||||
return;
|
||||
}
|
||||
processCreateOrder(createOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
if (!isResumed) return;
|
||||
if (xmrtoOrder != null) {
|
||||
Timber.w("another ongoing create order request");
|
||||
return;
|
||||
}
|
||||
processCreateOrderError(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private QueryOrderStatus xmrtoStatus = null;
|
||||
|
||||
private void processQueryOrder(final QueryOrderStatus status) {
|
||||
Timber.d("processQueryOrder %s for %s", status.getState().toString(), status.getUuid());
|
||||
xmrtoStatus = status;
|
||||
if (status.isCreated()) {
|
||||
getView().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
NumberFormat df = NumberFormat.getInstance(Locale.US);
|
||||
df.setMaximumFractionDigits(12);
|
||||
String btcAmount = df.format(status.getBtcAmount());
|
||||
String xmrAmountTotal = df.format(status.getXmrAmountTotal());
|
||||
tvTxBtcAmount.setText(getString(R.string.text_send_btc_amount, btcAmount, xmrAmountTotal));
|
||||
String xmrPriceBtc = df.format(status.getXmrPriceBtc());
|
||||
tvTxBtcRate.setText(getString(R.string.text_send_btc_rate, xmrPriceBtc));
|
||||
|
||||
double calcRate = status.getBtcAmount() / status.getXmrPriceBtc();
|
||||
Timber.i("Rates: %f / %f", calcRate, status.getXmrPriceBtc());
|
||||
|
||||
tvTxBtcAddress.setText(status.getBtcDestAddress()); // TODO test if this is different?
|
||||
|
||||
Timber.i("Expires @ %s, in %s seconds", status.getExpiresAt().toString(), status.getSecondsTillTimeout());
|
||||
|
||||
Timber.i("Status = %s", status.getState().toString());
|
||||
tvTxXmrToKey.setText(status.getUuid());
|
||||
|
||||
Timber.d("AmountRemaining=%f, XmrAmountTotal=%f", status.getXmrAmountRemaining(), status.getXmrAmountTotal());
|
||||
hideProgress();
|
||||
startSendTimer();
|
||||
prepareSend();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Timber.d("try again!");
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
queryOrder(status.getUuid());
|
||||
}
|
||||
}, QUERY_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
Handler handler = new Handler();
|
||||
|
||||
private void processQueryOrderError(final Exception ex) {
|
||||
Timber.e("processQueryOrderError %s", ex.getLocalizedMessage());
|
||||
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()) {
|
||||
showStageError(xmrErr.getErrorId().toString(), xmrErr.getErrorMsg(),
|
||||
getString(R.string.text_retry));
|
||||
evStageB.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
evStageB.setOnClickListener(null);
|
||||
queryOrder(xmrtoOrder.getUuid());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showStageError(xmrErr.getErrorId().toString(), xmrErr.getErrorMsg(),
|
||||
getString(R.string.text_noretry));
|
||||
}
|
||||
} else {
|
||||
showStageError(getString(R.string.label_generic_xmrto_error),
|
||||
getString(R.string.text_generic_xmrto_error, xmrEx.getCode()),
|
||||
getString(R.string.text_noretry));
|
||||
}
|
||||
} else {
|
||||
evStageB.showMessage(getString(R.string.label_generic_xmrto_error),
|
||||
ex.getLocalizedMessage(),
|
||||
getString(R.string.text_noretry));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void queryOrder(final String uuid) {
|
||||
Timber.d("queryOrder(%s)", uuid);
|
||||
if (!isResumed) return;
|
||||
getView().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
xmrtoStatus = null;
|
||||
showProgress(2, getString(R.string.label_send_progress_xmrto_query));
|
||||
getXmrToApi().queryOrderStatus(uuid, new XmrToCallback<QueryOrderStatus>() {
|
||||
@Override
|
||||
public void onSuccess(QueryOrderStatus status) {
|
||||
if (!isResumed) return;
|
||||
if (xmrtoOrder == null) return;
|
||||
if (!status.getUuid().equals(xmrtoOrder.getUuid())) {
|
||||
Timber.d("Query UUID does not match");
|
||||
// ignore (we got a response to a stale request)
|
||||
return;
|
||||
}
|
||||
if (xmrtoStatus != null)
|
||||
throw new IllegalStateException("xmrtoStatus must be null here!");
|
||||
processQueryOrder(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
if (!isResumed) return;
|
||||
processQueryOrderError(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private XmrToApi xmrToApi = null;
|
||||
|
||||
private final XmrToApi getXmrToApi() {
|
||||
if (xmrToApi == null) {
|
||||
synchronized (this) {
|
||||
if (xmrToApi == null) {
|
||||
xmrToApi = new XmrToApiImpl(OkHttpClientSingleton.getOkHttpClient(),
|
||||
Helper.getXmrToBaseUrl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return xmrToApi;
|
||||
}
|
||||
}
|
@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.fragment.send;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
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.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.OkHttpClientSingleton;
|
||||
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;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
||||
|
||||
public static SendBtcSuccessWizardFragment newInstance(SendSuccessWizardFragment.Listener listener) {
|
||||
SendBtcSuccessWizardFragment instance = new SendBtcSuccessWizardFragment();
|
||||
instance.setSendListener(listener);
|
||||
return instance;
|
||||
}
|
||||
|
||||
SendSuccessWizardFragment.Listener sendListener;
|
||||
|
||||
public SendBtcSuccessWizardFragment setSendListener(SendSuccessWizardFragment.Listener listener) {
|
||||
this.sendListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
ImageButton bCopyTxId;
|
||||
private TextView tvTxId;
|
||||
private TextView tvTxAddress;
|
||||
private TextView tvTxPaymentId;
|
||||
private TextView tvTxAmount;
|
||||
private TextView tvTxFee;
|
||||
private TextView tvXmrToAmount;
|
||||
private TextView tvXmrToStatus;
|
||||
private ImageView ivXmrToStatus;
|
||||
private ImageView ivXmrToStatusBig;
|
||||
private ProgressBar pbXmrto;
|
||||
private TextView tvTxXmrToKey;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
|
||||
Timber.d("onCreateView() %s", (String.valueOf(savedInstanceState)));
|
||||
|
||||
View view = inflater.inflate(
|
||||
R.layout.fragment_send_btc_success, container, false);
|
||||
|
||||
bCopyTxId = (ImageButton) view.findViewById(R.id.bCopyTxId);
|
||||
bCopyTxId.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Helper.clipBoardCopy(getActivity(), getString(R.string.label_send_txid), tvTxId.getText().toString());
|
||||
Toast.makeText(getActivity(), getString(R.string.message_copy_txid), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
tvXmrToAmount = (TextView) view.findViewById(R.id.tvXmrToAmount);
|
||||
tvXmrToStatus = (TextView) view.findViewById(R.id.tvXmrToStatus);
|
||||
ivXmrToStatus = (ImageView) view.findViewById(R.id.ivXmrToStatus);
|
||||
ivXmrToStatusBig = (ImageView) view.findViewById(R.id.ivXmrToStatusBig);
|
||||
|
||||
tvTxId = (TextView) view.findViewById(R.id.tvTxId);
|
||||
tvTxAddress = (TextView) view.findViewById(R.id.tvTxAddress);
|
||||
tvTxPaymentId = (TextView) view.findViewById(R.id.tvTxPaymentId);
|
||||
tvTxAmount = ((TextView) view.findViewById(R.id.tvTxAmount));
|
||||
tvTxFee = (TextView) view.findViewById(R.id.tvTxFee);
|
||||
|
||||
pbXmrto = (ProgressBar) view.findViewById(R.id.pbXmrto);
|
||||
pbXmrto.getIndeterminateDrawable().setColorFilter(0x61000000, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
|
||||
tvTxXmrToKey = (TextView) 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();
|
||||
;
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onValidateFields() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isResumed = false;
|
||||
|
||||
@Override
|
||||
public void onPauseFragment() {
|
||||
isResumed = false;
|
||||
super.onPauseFragment();
|
||||
}
|
||||
|
||||
TxDataBtc btcData = null;
|
||||
|
||||
@Override
|
||||
public void onResumeFragment() {
|
||||
super.onResumeFragment();
|
||||
Timber.d("onResumeFragment()");
|
||||
Helper.hideKeyboard(getActivity());
|
||||
isResumed = true;
|
||||
|
||||
btcData = (TxDataBtc) sendListener.getTxData();
|
||||
tvTxAddress.setText(btcData.getDestinationAddress());
|
||||
String paymentId = btcData.getPaymentId();
|
||||
if ((paymentId != null) && (!paymentId.isEmpty())) {
|
||||
tvTxPaymentId.setText(btcData.getPaymentId());
|
||||
} else {
|
||||
tvTxPaymentId.setText("-");
|
||||
}
|
||||
|
||||
final PendingTx committedTx = sendListener.getCommittedTx();
|
||||
if (committedTx != null) {
|
||||
tvTxId.setText(committedTx.txId);
|
||||
bCopyTxId.setEnabled(true);
|
||||
bCopyTxId.setImageResource(R.drawable.ic_content_copy_black_24dp);
|
||||
tvTxAmount.setText(getString(R.string.send_amount, Helper.getDisplayAmount(committedTx.amount)));
|
||||
tvTxFee.setText(getString(R.string.send_fee, Helper.getDisplayAmount(committedTx.fee)));
|
||||
if (btcData != null) {
|
||||
NumberFormat df = NumberFormat.getInstance(Locale.US);
|
||||
df.setMaximumFractionDigits(12);
|
||||
String btcAmount = df.format(btcData.getBtcAmount());
|
||||
tvXmrToAmount.setText(getString(R.string.info_send_xmrto_success_btc, btcAmount));
|
||||
//TODO btcData.getBtcAddress();
|
||||
tvTxXmrToKey.setText(btcData.getXmrtoUuid());
|
||||
queryOrder();
|
||||
} else {
|
||||
throw new IllegalStateException("btcData is null");
|
||||
}
|
||||
}
|
||||
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()))
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void queryOrder() {
|
||||
Timber.d("queryOrder(%s)", btcData.getXmrtoUuid());
|
||||
if (!isResumed) return;
|
||||
getXmrToApi().queryOrderStatus(btcData.getXmrtoUuid(), new XmrToCallback<QueryOrderStatus>() {
|
||||
@Override
|
||||
public void onSuccess(QueryOrderStatus status) {
|
||||
if (!isAdded()) return;
|
||||
processQueryOrder(status);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private int statusResource = 0;
|
||||
|
||||
void showXmrToStatus(final QueryOrderStatus status) {
|
||||
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()) {
|
||||
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.isPending()) {
|
||||
if (status.isPaid()) {
|
||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_paid));
|
||||
} else {
|
||||
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 {
|
||||
throw new IllegalStateException("status is broken: " + status.toString());
|
||||
}
|
||||
ivXmrToStatus.setImageResource(statusResource);
|
||||
if (status.isTerminal()) {
|
||||
pbXmrto.setVisibility(View.INVISIBLE);
|
||||
ivXmrToStatus.setVisibility(View.GONE);
|
||||
ivXmrToStatusBig.setImageResource(statusResource);
|
||||
ivXmrToStatusBig.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private XmrToApi xmrToApi = null;
|
||||
|
||||
private final XmrToApi getXmrToApi() {
|
||||
if (xmrToApi == null) {
|
||||
synchronized (this) {
|
||||
if (xmrToApi == null) {
|
||||
xmrToApi = new XmrToApiImpl(OkHttpClientSingleton.getOkHttpClient(),
|
||||
Helper.getXmrToBaseUrl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return xmrToApi;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.fragment.send;
|
||||
|
||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||
|
||||
interface SendConfirm {
|
||||
void sendFailed();
|
||||
|
||||
void createTransactionFailed(String errorText);
|
||||
|
||||
void transactionCreated(String txTag, PendingTransaction pendingTransaction);
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r er 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.util;
|
||||
|
||||
// based on https://rosettacode.org/wiki/Bitcoin/address_validation#Java
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class BitcoinAddressValidator {
|
||||
|
||||
private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
public static boolean validate(String addrress, boolean testnet) {
|
||||
if (addrress.length() < 26 || addrress.length() > 35)
|
||||
return false;
|
||||
byte[] decoded = decodeBase58To25Bytes(addrress);
|
||||
if (decoded == null)
|
||||
return false;
|
||||
|
||||
if (!testnet) {
|
||||
if ((decoded[0] != 0x00) && (decoded[0] != 0x05)) return false;
|
||||
} else {
|
||||
if ((decoded[0] != 0x6f) && (decoded[0] != 0xc4)) return false;
|
||||
}
|
||||
|
||||
byte[] hash1 = sha256(Arrays.copyOfRange(decoded, 0, 21));
|
||||
byte[] hash2 = sha256(hash1);
|
||||
|
||||
return Arrays.equals(Arrays.copyOfRange(hash2, 0, 4), Arrays.copyOfRange(decoded, 21, 25));
|
||||
}
|
||||
|
||||
private static byte[] decodeBase58To25Bytes(String input) {
|
||||
BigInteger num = BigInteger.ZERO;
|
||||
for (char t : input.toCharArray()) {
|
||||
int p = ALPHABET.indexOf(t);
|
||||
if (p == -1)
|
||||
return null;
|
||||
num = num.multiply(BigInteger.valueOf(58)).add(BigInteger.valueOf(p));
|
||||
}
|
||||
|
||||
byte[] result = new byte[25];
|
||||
byte[] numBytes = num.toByteArray();
|
||||
System.arraycopy(numBytes, 0, result, result.length - numBytes.length, numBytes.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static byte[] sha256(byte[] data) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
md.update(data);
|
||||
return md.digest();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.util;
|
||||
|
||||
import com.m2049r.xmrwallet.xmrto.api.QueryOrderStatus;
|
||||
|
||||
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 xmrtoKey = null;
|
||||
public String xmrtoAmount = null; // could be a double - but we are not doing any calculations
|
||||
public String xmrtoDestination = null;
|
||||
|
||||
public UserNotes(final String txNotes) {
|
||||
if (txNotes == null) {
|
||||
return;
|
||||
}
|
||||
this.txNotes = txNotes;
|
||||
Pattern p = Pattern.compile("^\\{(xmrto-\\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);
|
||||
} else {
|
||||
note = txNotes;
|
||||
}
|
||||
}
|
||||
|
||||
public void setNote(String newNote) {
|
||||
if (newNote != null) {
|
||||
note = newNote;
|
||||
} else {
|
||||
note = "";
|
||||
}
|
||||
txNotes = buildTxNote();
|
||||
}
|
||||
|
||||
public void setXmrtoStatus(QueryOrderStatus xmrtoStatus) {
|
||||
if (xmrtoStatus != null) {
|
||||
xmrtoKey = xmrtoStatus.getUuid();
|
||||
xmrtoAmount = String.valueOf(xmrtoStatus.getBtcAmount());
|
||||
xmrtoDestination = xmrtoStatus.getBtcDestAddress();
|
||||
} else {
|
||||
xmrtoKey = null;
|
||||
xmrtoAmount = null;
|
||||
xmrtoDestination = null;
|
||||
}
|
||||
txNotes = buildTxNote();
|
||||
}
|
||||
|
||||
private String buildTxNote() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
if (xmrtoKey != null) {
|
||||
if ((xmrtoAmount == null) || (xmrtoDestination == null))
|
||||
throw new IllegalArgumentException("Broken notes");
|
||||
sb.append("{");
|
||||
sb.append(xmrtoKey);
|
||||
sb.append(",");
|
||||
sb.append(xmrtoAmount);
|
||||
sb.append("BTC,");
|
||||
sb.append(xmrtoDestination);
|
||||
sb.append("}");
|
||||
if ((note != null) && (!note.isEmpty()))
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(note);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// based on https://code.tutsplus.com/tutorials/creating-compound-views-on-android--cms-22889
|
||||
|
||||
package com.m2049r.xmrwallet.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class ExchangeBtcTextView extends LinearLayout
|
||||
implements NumberPadView.NumberPadListener {
|
||||
|
||||
String btcAmount = null;
|
||||
String xmrAmount = null;
|
||||
|
||||
private boolean validate(String amount, double max, double min) {
|
||||
boolean ok = true;
|
||||
if (amount != null) {
|
||||
try {
|
||||
double x = Double.parseDouble(amount);
|
||||
if ((x < min) || (x > max)) {
|
||||
ok = false;
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
Timber.e(ex.getLocalizedMessage());
|
||||
ok = false;
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
public boolean validate(double maxBtc, double minBtc) {
|
||||
Timber.d("validate(maxBtc=%f,minBtc=%f)", maxBtc, minBtc);
|
||||
boolean ok = true;
|
||||
if (!validate(btcAmount, maxBtc, minBtc)) {
|
||||
Timber.d("btcAmount invalid %s", btcAmount);
|
||||
shakeAmountField();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void shakeAmountField() {
|
||||
tvAmountA.startAnimation(Helper.getShakeAnimation(getContext()));
|
||||
}
|
||||
|
||||
void shakeExchangeField() {
|
||||
tvAmountB.startAnimation(Helper.getShakeAnimation(getContext()));
|
||||
}
|
||||
|
||||
public void setRate(double xmrBtcRate) {
|
||||
this.xmrBtcRate = xmrBtcRate;
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
exchange();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setAmount(String btcAmount) {
|
||||
this.btcAmount = btcAmount;
|
||||
tvAmountA.setText(btcAmount);
|
||||
xmrAmount = null;
|
||||
exchange();
|
||||
}
|
||||
|
||||
public String getAmount() {
|
||||
return btcAmount;
|
||||
}
|
||||
|
||||
TextView tvAmountA;
|
||||
TextView tvAmountB;
|
||||
Spinner sCurrencyA;
|
||||
Spinner sCurrencyB;
|
||||
|
||||
public ExchangeBtcTextView(Context context) {
|
||||
super(context);
|
||||
initializeViews(context);
|
||||
}
|
||||
|
||||
public ExchangeBtcTextView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initializeViews(context);
|
||||
}
|
||||
|
||||
public ExchangeBtcTextView(Context context,
|
||||
AttributeSet attrs,
|
||||
int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
initializeViews(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the views in the layout.
|
||||
*
|
||||
* @param context the current context for the view.
|
||||
*/
|
||||
private void initializeViews(Context context) {
|
||||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.view_exchange_btc_text, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
tvAmountA = (TextView) findViewById(R.id.tvAmountA);
|
||||
tvAmountB = (TextView) findViewById(R.id.tvAmountB);
|
||||
sCurrencyA = (Spinner) findViewById(R.id.sCurrencyA);
|
||||
sCurrencyB = (Spinner) findViewById(R.id.sCurrencyB);
|
||||
|
||||
ArrayAdapter<String> btcAdapter = new ArrayAdapter<String>(getContext(),
|
||||
android.R.layout.simple_spinner_item,
|
||||
new String[]{"BTC"});
|
||||
sCurrencyA.setAdapter(btcAdapter);
|
||||
sCurrencyA.setEnabled(false);
|
||||
ArrayAdapter<String> xmrAdapter = new ArrayAdapter<String>(getContext(),
|
||||
android.R.layout.simple_spinner_item,
|
||||
new String[]{"XMR"});
|
||||
sCurrencyB.setAdapter(xmrAdapter);
|
||||
sCurrencyB.setEnabled(false);
|
||||
}
|
||||
|
||||
double xmrBtcRate = 0;
|
||||
|
||||
public void exchange() {
|
||||
btcAmount = tvAmountA.getText().toString();
|
||||
if (!btcAmount.isEmpty() && (xmrBtcRate > 0)) {
|
||||
double xmr = xmrBtcRate * Double.parseDouble(btcAmount);
|
||||
xmrAmount = Helper.getFormattedAmount(xmr, true);
|
||||
} else {
|
||||
xmrAmount = "";
|
||||
}
|
||||
tvAmountB.setText(getResources().getString(R.string.send_amount_btc_xmr, xmrAmount));
|
||||
Timber.d("%s BTC =%f> %s XMR", btcAmount, xmrBtcRate, xmrAmount);
|
||||
}
|
||||
|
||||
// deal with attached numpad
|
||||
@Override
|
||||
public void onDigitPressed(final int digit) {
|
||||
tvAmountA.append(String.valueOf(digit));
|
||||
exchange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPointPressed() {
|
||||
//TODO locale?
|
||||
if (tvAmountA.getText().toString().indexOf('.') == -1) {
|
||||
if (tvAmountA.getText().toString().isEmpty()) {
|
||||
tvAmountA.append("0");
|
||||
}
|
||||
tvAmountA.append(".");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackSpacePressed() {
|
||||
String entry = tvAmountA.getText().toString();
|
||||
int length = entry.length();
|
||||
if (length > 0) {
|
||||
tvAmountA.setText(entry.substring(0, entry.length() - 1));
|
||||
exchange();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClearAll() {
|
||||
tvAmountA.setText(null);
|
||||
exchange();
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
|
||||
public class SendProgressView extends LinearLayout {
|
||||
|
||||
public SendProgressView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initializeViews(context);
|
||||
}
|
||||
|
||||
public SendProgressView(Context context,
|
||||
AttributeSet attrs,
|
||||
int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
initializeViews(context);
|
||||
}
|
||||
|
||||
private void initializeViews(Context context) {
|
||||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.view_send_progress, this);
|
||||
}
|
||||
|
||||
|
||||
View pbProgress;
|
||||
View llMessage;
|
||||
TextView tvCode;
|
||||
TextView tvMessage;
|
||||
TextView tvSolution;
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
pbProgress = findViewById(R.id.pbProgress);
|
||||
llMessage = findViewById(R.id.llMessage);
|
||||
tvCode = (TextView) findViewById(R.id.tvCode);
|
||||
tvMessage = (TextView) findViewById(R.id.tvMessage);
|
||||
tvSolution = (TextView) findViewById(R.id.tvSolution);
|
||||
}
|
||||
|
||||
public void showProgress(String progressText) {
|
||||
pbProgress.setVisibility(VISIBLE);
|
||||
tvCode.setVisibility(INVISIBLE);
|
||||
tvMessage.setText(progressText);
|
||||
llMessage.setVisibility(VISIBLE);
|
||||
tvSolution.setVisibility(INVISIBLE);
|
||||
}
|
||||
|
||||
public void hideProgress() {
|
||||
pbProgress.setVisibility(INVISIBLE);
|
||||
llMessage.setVisibility(INVISIBLE);
|
||||
}
|
||||
|
||||
public void showMessage(String code, String message, String solution) {
|
||||
tvCode.setText(code);
|
||||
tvMessage.setText(message);
|
||||
tvSolution.setText(solution);
|
||||
tvCode.setVisibility(VISIBLE);
|
||||
llMessage.setVisibility(VISIBLE);
|
||||
tvSolution.setVisibility(VISIBLE);
|
||||
pbProgress.setVisibility(INVISIBLE);
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class XmrToError {
|
||||
private final Error errorId;
|
||||
private final String errorMsg;
|
||||
|
||||
public enum Error {
|
||||
XMRTO_ERROR_UNDEF,
|
||||
XMRTO_ERROR_001, // (503) internal services not available try again later
|
||||
XMRTO_ERROR_002, // (400) malformed bitcoin address check address validity
|
||||
XMRTO_ERROR_003, // (400) invalid bitcoin amount check amount data type
|
||||
XMRTO_ERROR_004, // (400) bitcoin amount out of bounds check min and max amount
|
||||
XMRTO_ERROR_005, // (400) unexpected validation error contact support
|
||||
XMRTO_ERROR_006, // (404) requested order not found check order UUID
|
||||
XMRTO_ERROR_007, // (503) third party service not available try again later
|
||||
XMRTO_ERROR_008, // (503) insufficient funds available try again later
|
||||
XMRTO_ERROR_009 // (400) invalid request check request parameters
|
||||
}
|
||||
|
||||
public boolean isRetryable() {
|
||||
return (errorId == Error.XMRTO_ERROR_001)
|
||||
|| (errorId == Error.XMRTO_ERROR_007)
|
||||
|| (errorId == Error.XMRTO_ERROR_008);
|
||||
}
|
||||
|
||||
public static String getErrorIdString(Error anError) {
|
||||
return anError.toString().replace('_', '-');
|
||||
}
|
||||
|
||||
// mostly for testing
|
||||
public XmrToError(String errorId, String message) {
|
||||
Error error;
|
||||
try {
|
||||
error = Error.valueOf(errorId.replace('-', '_'));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
error = Error.XMRTO_ERROR_UNDEF;
|
||||
}
|
||||
this.errorId = error;
|
||||
this.errorMsg = message;
|
||||
}
|
||||
|
||||
public XmrToError(final JSONObject jsonObject) throws JSONException {
|
||||
final String errorId = jsonObject.getString("error");
|
||||
Error error;
|
||||
try {
|
||||
error = Error.valueOf(errorId.replace('-', '_'));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
error = Error.XMRTO_ERROR_UNDEF;
|
||||
}
|
||||
this.errorId = error;
|
||||
this.errorMsg = jsonObject.getString("error_msg");
|
||||
}
|
||||
|
||||
public Error getErrorId() {
|
||||
return errorId;
|
||||
}
|
||||
|
||||
public String getErrorMsg() {
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getErrorIdString(getErrorId()) + ": " + getErrorMsg();
|
||||
}
|
||||
|
||||
/* Errors from Query Parameters
|
||||
HTTP code XMR.TO error code Error message/reason Solution
|
||||
503 XMRTO-ERROR-001 internal services not available try again later
|
||||
503 XMRTO-ERROR-007 third party service not available try again later
|
||||
503 XMRTO-ERROR-008 insufficient funds available try again later
|
||||
*/
|
||||
|
||||
/* Errors from Create Order
|
||||
HTTP code XMR.TO error code Error message/reason Solution
|
||||
503 XMRTO-ERROR-001 internal services not available try again later
|
||||
400 XMRTO-ERROR-002 malformed bitcoin address check address validity
|
||||
400 XMRTO-ERROR-003 invalid bitcoin amount check amount data type
|
||||
503 XMRTO-ERROR-004 bitcoin amount out of bounds check min and max amount
|
||||
400 XMRTO-ERROR-005 unexpected validation error contact support
|
||||
*/
|
||||
|
||||
/* Errors from Query Order
|
||||
HTTP code XMR.TO error code Error message/reason Solution
|
||||
400 XMRTO-ERROR-009 invalid request check request parameters
|
||||
404 XMRTO-ERROR-006 requested order not found check order UUID
|
||||
*/
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto;
|
||||
|
||||
public class XmrToException extends Exception {
|
||||
private int code;
|
||||
private XmrToError error;
|
||||
|
||||
public XmrToException(final int code) {
|
||||
super();
|
||||
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,28 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto.api;
|
||||
|
||||
public interface CreateOrder {
|
||||
Double getBtcAmount();
|
||||
|
||||
String getBtcDestAddress();
|
||||
|
||||
String getState();
|
||||
|
||||
String getUuid();
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto.api;
|
||||
|
||||
public interface QueryOrderParameters {
|
||||
|
||||
double getLowerLimit(); // "lower_limit": <lower_order_limit_in_btc_as_float>,
|
||||
|
||||
double getPrice(); // "price": <price_of_1_btc_in_xmr_as_offered_by_service_as_float>,
|
||||
|
||||
double getUpperLimit(); // "upper_limit": <upper_order_limit_in_btc_as_float>,
|
||||
|
||||
boolean isZeroConfEnabled(); // "zero_conf_enabled": <true_if_zero_conf_is_enabled_as_boolean>,
|
||||
|
||||
double getZeroConfMaxAmount(); // "zero_conf_max_amount": <up_to_this_amount_zero_conf_is_possible_as_float>
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto.api;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
public interface XmrToApi {
|
||||
|
||||
/**
|
||||
* Queries the order parameter.
|
||||
*
|
||||
* @param callback the callback with the OrderParameter object
|
||||
*/
|
||||
void queryOrderParameters(@NonNull final XmrToCallback<QueryOrderParameters> callback);
|
||||
|
||||
/**
|
||||
* Creates an order
|
||||
*
|
||||
* @param amount the desired amount
|
||||
* @param address the target bitcoin address
|
||||
*/
|
||||
void createOrder(final double amount, @NonNull final String address, @NonNull final XmrToCallback<CreateOrder> callback);
|
||||
|
||||
/**
|
||||
* Queries the order status for given current order
|
||||
*
|
||||
* @param uuid the order uuid
|
||||
* @param callback the callback with the OrderStatus object
|
||||
*/
|
||||
void queryOrderStatus(@NonNull final String uuid, @NonNull final XmrToCallback<QueryOrderStatus> callback);
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto.api;
|
||||
|
||||
public interface XmrToCallback<T> {
|
||||
|
||||
void onSuccess(T t);
|
||||
|
||||
void onError(Exception ex);
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto.network;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.m2049r.xmrwallet.xmrto.api.XmrToCallback;
|
||||
import com.m2049r.xmrwallet.xmrto.api.CreateOrder;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
class CreateOrderImpl implements CreateOrder {
|
||||
|
||||
private final String state;
|
||||
private final double btcAmount;
|
||||
private final String btcDestAddress;
|
||||
private final String uuid;
|
||||
|
||||
public Double getBtcAmount() {
|
||||
return btcAmount;
|
||||
}
|
||||
|
||||
public String getBtcDestAddress() {
|
||||
return btcDestAddress;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
CreateOrderImpl(final JSONObject jsonObject) throws JSONException {
|
||||
this.state = jsonObject.getString("state");
|
||||
this.btcAmount = jsonObject.getDouble("btc_amount");
|
||||
this.btcDestAddress = jsonObject.getString("btc_dest_address");
|
||||
this.uuid = jsonObject.getString("uuid");
|
||||
}
|
||||
|
||||
public static void call(@NonNull final XmrToApiCall api, final double amount, @NonNull final String address,
|
||||
@NonNull final XmrToCallback<CreateOrder> callback) {
|
||||
try {
|
||||
final JSONObject request = createRequest(amount, address);
|
||||
api.call("order_create", 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 double amount, final String address) throws JSONException {
|
||||
final JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("btc_amount", amount);
|
||||
jsonObject.put("btc_dest_address", address);
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto.network;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
interface NetworkCallback {
|
||||
|
||||
void onSuccess(JSONObject jsonObject);
|
||||
|
||||
void onError(Exception ex);
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto.network;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.m2049r.xmrwallet.xmrto.api.XmrToCallback;
|
||||
import com.m2049r.xmrwallet.xmrto.api.QueryOrderParameters;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
class QueryOrderParametersImpl implements QueryOrderParameters {
|
||||
|
||||
private double lowerLimit; // "lower_limit": <lower_order_limit_in_btc_as_float>,
|
||||
private double price; // "price": <price_of_1_btc_in_xmr_as_offered_by_service_as_float>,
|
||||
private double upperLimit; // "upper_limit": <upper_order_limit_in_btc_as_float>,
|
||||
private boolean isZeroConfEnabled; // "zero_conf_enabled": <true_if_zero_conf_is_enabled_as_boolean>,
|
||||
private double zeroConfMaxAmount; // "zero_conf_max_amount": <up_to_this_amount_zero_conf_is_possible_as_float>
|
||||
|
||||
public double getLowerLimit() {
|
||||
return lowerLimit;
|
||||
}
|
||||
|
||||
public double getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public double getUpperLimit() {
|
||||
return upperLimit;
|
||||
}
|
||||
|
||||
public boolean isZeroConfEnabled() {
|
||||
return isZeroConfEnabled;
|
||||
}
|
||||
|
||||
public double getZeroConfMaxAmount() {
|
||||
return zeroConfMaxAmount;
|
||||
}
|
||||
|
||||
QueryOrderParametersImpl(final JSONObject jsonObject) throws JSONException {
|
||||
lowerLimit = jsonObject.getDouble("lower_limit");
|
||||
price = jsonObject.getDouble("price");
|
||||
upperLimit = jsonObject.getDouble("upper_limit");
|
||||
isZeroConfEnabled = jsonObject.getBoolean("zero_conf_enabled");
|
||||
zeroConfMaxAmount = jsonObject.getDouble("zero_conf_max_amount");
|
||||
}
|
||||
|
||||
public static void call(@NonNull final XmrToApiCall api,
|
||||
@NonNull final XmrToCallback<QueryOrderParameters> callback) {
|
||||
api.call("order_parameter_query", new NetworkCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject jsonObject) {
|
||||
try {
|
||||
callback.onSuccess(new QueryOrderParametersImpl(jsonObject));
|
||||
} catch (JSONException ex) {
|
||||
callback.onError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
callback.onError(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
//{"zero_conf_enabled":true,"price":0.015537,"upper_limit":20.0,"lower_limit":0.001,"zero_conf_max_amount":0.1}
|
||||
}
|
@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto.network;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.m2049r.xmrwallet.xmrto.api.XmrToCallback;
|
||||
import com.m2049r.xmrwallet.xmrto.api.QueryOrderStatus;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
class QueryOrderStatusImpl implements QueryOrderStatus {
|
||||
public static final SimpleDateFormat DATETIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
|
||||
|
||||
public static Date parseDate(String dateString) throws ParseException {
|
||||
return DATETIME_FORMATTER.parse(dateString.replaceAll("Z$", "+0000"));
|
||||
}
|
||||
|
||||
private QueryOrderStatus.State state; // "state": "<order_state_as_string>",
|
||||
private double btcAmount; // "btc_amount": <requested_amount_in_btc_as_float>,
|
||||
private String btcDestAddress; // "btc_dest_address": "<requested_destination_address_as_string>",
|
||||
private String uuid; // "uuid": "<unique_order_identifier_as_12_character_string>"
|
||||
// the following are only returned if the state is "after" TO_BE_CREATED
|
||||
private int btcNumConfirmations; // "btc_num_confirmations": <btc_num_confirmations_as_integer>,
|
||||
private int btcNumConfirmationsBeforePurge; // "btc_num_confirmations_before_purge": <btc_num_confirmations_before_purge_as_integer>,
|
||||
private String btcTransactionId; // "btc_transaction_id": "<btc_transaction_id_as_string>",
|
||||
private Date createdAt; // "created_at": "<timestamp_as_string>",
|
||||
private Date expiresAt; // "expires_at": "<timestamp_as_string>",
|
||||
private int secondsTillTimeout; // "seconds_till_timeout": <seconds_till_timeout_as_integer>,
|
||||
private double xmrAmountTotal; // "xmr_amount_total": <amount_in_xmr_for_this_order_as_float>,
|
||||
private double xmrAmountRemaining; // "xmr_amount_remaining": <amount_in_xmr_that_the_user_must_still_send_as_float>,
|
||||
private int xmrNumConfirmationsRemaining; // "xmr_num_confirmations_remaining": <num_xmr_confirmations_remaining_before_bitcoins_will_be_sent_as_integer>,
|
||||
private double xmrPriceBtc; // "xmr_price_btc": <price_of_1_btc_in_xmr_as_offered_by_service_as_float>,
|
||||
private String xmrReceivingAddress; // "xmr_receiving_address": "xmr_old_style_address_user_can_send_funds_to_as_string",
|
||||
private String xmrReceivingIntegratedAddress; // "xmr_receiving_integrated_address": "xmr_integrated_address_user_needs_to_send_funds_to_as_string",
|
||||
private int xmrRecommendedMixin; // "xmr_recommended_mixin": <xmr_recommended_mixin_as_integer>,
|
||||
private double xmrRequiredAmount; // "xmr_required_amount": <xmr_amount_user_needs_to_send_as_float>,
|
||||
private String xmrRequiredPaymentIdLong; // "xmr_required_payment_id_long": "xmr_payment_id_user_needs_to_include_when_using_old_stlye_address_as_string"
|
||||
private String xmrRequiredPaymentIdShort; // "xmr_required_payment_id_short": "xmr_payment_id_included_in_integrated_address_as_string"
|
||||
|
||||
public QueryOrderStatus.State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public double getBtcAmount() {
|
||||
return btcAmount;
|
||||
}
|
||||
|
||||
public String getBtcDestAddress() {
|
||||
return btcDestAddress;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public int getBtcNumConfirmations() {
|
||||
return btcNumConfirmations;
|
||||
}
|
||||
|
||||
public int getBtcNumConfirmationsBeforePurge() {
|
||||
return btcNumConfirmationsBeforePurge;
|
||||
}
|
||||
|
||||
public String getBtcTransactionId() {
|
||||
return btcTransactionId;
|
||||
}
|
||||
|
||||
public Date getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public Date getExpiresAt() {
|
||||
return expiresAt;
|
||||
}
|
||||
|
||||
public int getSecondsTillTimeout() {
|
||||
return secondsTillTimeout;
|
||||
}
|
||||
|
||||
public double getXmrAmountTotal() {
|
||||
return xmrAmountTotal;
|
||||
}
|
||||
|
||||
public double getXmrAmountRemaining() {
|
||||
return xmrAmountRemaining;
|
||||
}
|
||||
|
||||
public int getXmrNumConfirmationsRemaining() {
|
||||
return xmrNumConfirmationsRemaining;
|
||||
}
|
||||
|
||||
public double getXmrPriceBtc() {
|
||||
return xmrPriceBtc;
|
||||
}
|
||||
|
||||
public String getXmrReceivingAddress() {
|
||||
return xmrReceivingAddress;
|
||||
}
|
||||
|
||||
public String getXmrReceivingIntegratedAddress() {
|
||||
return xmrReceivingIntegratedAddress;
|
||||
}
|
||||
|
||||
public int getXmrRecommendedMixin() {
|
||||
return xmrRecommendedMixin;
|
||||
}
|
||||
|
||||
public double getXmrRequiredAmount() {
|
||||
return xmrRequiredAmount;
|
||||
}
|
||||
|
||||
public String getXmrRequiredPaymentIdLong() {
|
||||
return xmrRequiredPaymentIdLong;
|
||||
}
|
||||
|
||||
public String getXmrRequiredPaymentIdShort() {
|
||||
return xmrRequiredPaymentIdShort;
|
||||
}
|
||||
|
||||
public boolean isCreated() {
|
||||
return (state.equals(State.UNPAID) ||
|
||||
state.equals(State.BTC_SENT) ||
|
||||
state.equals(State.PAID) ||
|
||||
state.equals(State.PAID_UNCONFIRMED) ||
|
||||
state.equals(State.UNDERPAID));
|
||||
}
|
||||
|
||||
public boolean isTerminal() {
|
||||
return (state.equals(State.BTC_SENT) || isError());
|
||||
}
|
||||
|
||||
|
||||
public boolean isError() {
|
||||
return (state.equals(State.UNDEF) ||
|
||||
state.equals(State.NOT_FOUND) ||
|
||||
state.equals(State.TIMED_OUT));
|
||||
}
|
||||
|
||||
public boolean isPending() {
|
||||
return (state.equals(State.TO_BE_CREATED) ||
|
||||
state.equals(State.UNPAID) ||
|
||||
state.equals(State.UNDERPAID) ||
|
||||
state.equals(State.PAID_UNCONFIRMED) ||
|
||||
state.equals(State.PAID));
|
||||
}
|
||||
|
||||
public boolean isPaid() {
|
||||
return (state.equals(State.PAID_UNCONFIRMED) ||
|
||||
state.equals(State.PAID));
|
||||
}
|
||||
|
||||
public boolean isSent() {
|
||||
return state.equals(State.BTC_SENT);
|
||||
}
|
||||
|
||||
|
||||
QueryOrderStatusImpl(final JSONObject jsonObject) throws JSONException {
|
||||
Timber.d(jsonObject.toString(4));
|
||||
String stateName = jsonObject.getString("state"); // "state": "<order_state_as_string>",
|
||||
State state;
|
||||
try {
|
||||
state = State.valueOf(stateName);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
state = State.UNDEF; //TODO: throws IllegalArgumentException?
|
||||
}
|
||||
this.state = state;
|
||||
|
||||
btcAmount = jsonObject.getDouble("btc_amount"); // "btc_amount": <requested_amount_in_btc_as_float>,
|
||||
btcDestAddress = jsonObject.getString("btc_dest_address"); // "btc_dest_address": "<requested_destination_address_as_string>",
|
||||
uuid = jsonObject.getString("uuid"); // "uuid": "<unique_order_identifier_as_12_character_string>"
|
||||
|
||||
if (isCreated()) {
|
||||
btcNumConfirmations = jsonObject.getInt("btc_num_confirmations"); // "btc_num_confirmations": <btc_num_confirmations_as_integer>,
|
||||
btcNumConfirmationsBeforePurge = jsonObject.getInt("btc_num_confirmations_before_purge"); // "btc_num_confirmations_before_purge": <btc_num_confirmations_before_purge_as_integer>,
|
||||
btcTransactionId = jsonObject.getString("btc_transaction_id"); // "btc_transaction_id": "<btc_transaction_id_as_string>",
|
||||
try {
|
||||
String created = jsonObject.getString("created_at"); // "created_at": "<timestamp_as_string>",
|
||||
createdAt = parseDate(created);
|
||||
String expires = jsonObject.getString("expires_at"); // "expires_at": "<timestamp_as_string>",
|
||||
expiresAt = parseDate(expires);
|
||||
} catch (ParseException ex) {
|
||||
throw new JSONException(ex.getLocalizedMessage());
|
||||
}
|
||||
secondsTillTimeout = jsonObject.getInt("seconds_till_timeout"); // "seconds_till_timeout": <seconds_till_timeout_as_integer>,
|
||||
xmrAmountTotal = jsonObject.getDouble("xmr_amount_total"); // "xmr_amount_total": <amount_in_xmr_for_this_order_as_float>,
|
||||
xmrAmountRemaining = jsonObject.getDouble("xmr_amount_remaining"); // "xmr_amount_remaining": <amount_in_xmr_that_the_user_must_still_send_as_float>,
|
||||
xmrNumConfirmationsRemaining = jsonObject.getInt("xmr_num_confirmations_remaining"); // "xmr_num_confirmations_remaining": <num_xmr_confirmations_remaining_before_bitcoins_will_be_sent_as_integer>,
|
||||
xmrPriceBtc = jsonObject.getDouble("xmr_price_btc"); // "xmr_price_btc": <price_of_1_btc_in_xmr_as_offered_by_service_as_float>,
|
||||
xmrReceivingAddress = jsonObject.getString("xmr_receiving_address"); // "xmr_receiving_address": "xmr_old_style_address_user_can_send_funds_to_as_string",
|
||||
xmrReceivingIntegratedAddress = jsonObject.getString("xmr_receiving_integrated_address"); // "xmr_receiving_integrated_address": "xmr_integrated_address_user_needs_to_send_funds_to_as_string",
|
||||
xmrRecommendedMixin = jsonObject.getInt("xmr_recommended_mixin"); // "xmr_recommended_mixin": <xmr_recommended_mixin_as_integer>,
|
||||
xmrRequiredAmount = jsonObject.getDouble("xmr_required_amount"); // "xmr_required_amount": <xmr_amount_user_needs_to_send_as_float>,
|
||||
xmrRequiredPaymentIdLong = jsonObject.getString("xmr_required_payment_id_long"); // "xmr_required_payment_id_long": "xmr_payment_id_user_needs_to_include_when_using_old_stlye_address_as_string"
|
||||
xmrRequiredPaymentIdShort = jsonObject.getString("xmr_required_payment_id_short"); // "xmr_required_payment_id_short": "xmr_payment_id_included_in_integrated_address_as_string"
|
||||
}
|
||||
}
|
||||
|
||||
public static void call(@NonNull final XmrToApiCall api, @NonNull final String uuid,
|
||||
@NonNull final XmrToCallback<QueryOrderStatus> callback) {
|
||||
try {
|
||||
final JSONObject request = createRequest(uuid);
|
||||
api.call("order_status_query", request, new NetworkCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject jsonObject) {
|
||||
try {
|
||||
callback.onSuccess(new QueryOrderStatusImpl(jsonObject));
|
||||
} catch (JSONException ex) {
|
||||
callback.onError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
callback.onError(ex);
|
||||
}
|
||||
});
|
||||
} catch (JSONException ex) {
|
||||
callback.onError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create JSON request object
|
||||
*
|
||||
* @param uuid unique_order_identifier_as_12_character_string
|
||||
*/
|
||||
|
||||
static JSONObject createRequest(final String uuid) throws JSONException {
|
||||
final JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("uuid", uuid);
|
||||
return jsonObject;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto.network;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
interface XmrToApiCall {
|
||||
|
||||
void call(@NonNull final String path, @NonNull final NetworkCallback callback);
|
||||
|
||||
void call(@NonNull final String path, final JSONObject request, @NonNull final NetworkCallback callback);
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto.network;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
|
||||
import com.m2049r.xmrwallet.xmrto.api.XmrToCallback;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToError;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToException;
|
||||
import com.m2049r.xmrwallet.xmrto.api.CreateOrder;
|
||||
import com.m2049r.xmrwallet.xmrto.api.QueryOrderParameters;
|
||||
import com.m2049r.xmrwallet.xmrto.api.QueryOrderStatus;
|
||||
import com.m2049r.xmrwallet.xmrto.api.XmrToApi;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class XmrToApiImpl implements XmrToApi, XmrToApiCall {
|
||||
|
||||
@NonNull
|
||||
private final OkHttpClient okHttpClient;
|
||||
|
||||
private final HttpUrl baseUrl;
|
||||
|
||||
public XmrToApiImpl(@NonNull final OkHttpClient okHttpClient, final HttpUrl baseUrl) {
|
||||
this.okHttpClient = okHttpClient;
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public XmrToApiImpl(@NonNull final OkHttpClient okHttpClient) {
|
||||
this(okHttpClient, HttpUrl.parse("https://xmr.to/api/v2/xmr2btc/"));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void createOrder(final double amount, @NonNull final String address,
|
||||
@NonNull final XmrToCallback<CreateOrder> callback) {
|
||||
CreateOrderImpl.call(this, amount, address, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryOrderStatus(@NonNull final String uuid,
|
||||
@NonNull final XmrToCallback<QueryOrderStatus> callback) {
|
||||
QueryOrderStatusImpl.call(this, uuid, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryOrderParameters(@NonNull final XmrToCallback<QueryOrderParameters> callback) {
|
||||
QueryOrderParametersImpl.call(this, callback);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void call(@NonNull final String path, @NonNull final NetworkCallback callback) {
|
||||
call(path, null, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void call(@NonNull final String path, final JSONObject request, @NonNull final NetworkCallback callback) {
|
||||
final HttpUrl url = baseUrl.newBuilder()
|
||||
.addPathSegment(path)
|
||||
.addPathSegment("") // xmr.to needs a trailing slash!
|
||||
.build();
|
||||
|
||||
Timber.d(url.toString());
|
||||
final Request httpRequest = createHttpRequest(request, url);
|
||||
Timber.d(httpRequest.toString());
|
||||
Timber.d(request == null ? "null request" : request.toString());
|
||||
|
||||
okHttpClient.newCall(httpRequest).enqueue(new okhttp3.Callback() {
|
||||
@Override
|
||||
public void onFailure(final Call call, final IOException ex) {
|
||||
Timber.d("A");
|
||||
callback.onError(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(final Call call, final Response response) throws IOException {
|
||||
Timber.d("onResponse code=%d", response.code());
|
||||
if (response.isSuccessful()) {
|
||||
try {
|
||||
final JSONObject json = new JSONObject(response.body().string());
|
||||
callback.onSuccess(json);
|
||||
} catch (JSONException ex) {
|
||||
callback.onError(ex);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
final JSONObject json = new JSONObject(response.body().string());
|
||||
final XmrToError error = new XmrToError(json);
|
||||
Timber.e("xmr.to says %d/%s", response.code(), error.toString());
|
||||
callback.onError(new XmrToException(response.code(), error));
|
||||
} catch (JSONException ex) {
|
||||
callback.onError(new XmrToException(response.code()));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Request createHttpRequest(final JSONObject request, final HttpUrl url) {
|
||||
if (request != null) {
|
||||
final RequestBody body = RequestBody.create(
|
||||
MediaType.parse("application/json"), request.toString());
|
||||
|
||||
return new Request.Builder()
|
||||
.url(url)
|
||||
.post(body)
|
||||
.build();
|
||||
} else {
|
||||
return new Request.Builder()
|
||||
.url(url)
|
||||
.get()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="58.0"
|
||||
android:viewportWidth="58.0">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M17.11,23.49L13.05,27.55L26.1,40.6L55.1,11.6L51.04,7.54L26.1,32.48L17.11,23.49L17.11,23.49ZM52.2,29C52.2,41.76 41.76,52.2 29,52.2C16.24,52.2 5.8,41.76 5.8,29C5.8,16.24 16.24,5.8 29,5.8C31.32,5.8 33.35,6.09 35.38,6.67L40.02,2.03C36.54,0.87 32.77,0 29,0C13.05,0 0,13.05 0,29C0,44.95 13.05,58 29,58C44.95,58 58,44.95 58,29L52.2,29L52.2,29Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="58.0"
|
||||
android:endY="6.439293516E-15"
|
||||
android:startX="2.1746516868E-31"
|
||||
android:startY="58.0"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FFF0006B"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#FFFF6600"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFE"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M20,13C20,18.52 15.52,23 10,23C4.48,23 -0,18.52 -0,13C-0,7.48 4.48,3.01 10,3.01C15.52,3.01 20,7.48 20,13"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
<path
|
||||
android:fillColor="#FF6105"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M10,3.01C4.48,3.01 -0.01,7.49 0,13C0,14.11 0.18,15.17 0.51,16.16L3.5,16.16L3.5,7.75L10,14.25L16.49,7.75L16.49,16.16L19.49,16.16C19.82,15.17 19.99,14.11 19.99,13C20,7.48 15.52,3.01 10,3.01L10,3.01Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
<path
|
||||
android:fillColor="#464543"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M8.5,15.74L5.67,12.91L5.67,18.2L4.58,18.2L3.5,18.2L1.45,18.2C3.21,21.08 6.38,23 10,23C13.62,23 16.79,21.08 18.54,18.2L16.49,18.2L14.56,18.2L14.33,18.2L14.33,12.91L11.49,15.74L10,17.23L8.5,15.74L8.5,15.74Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
</vector>
|
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="@color/gradientOrange"
|
||||
android:pathData="M8.59,16.34l4.58,-4.59 -4.58,-4.59L10,5.75l6,6 -6,6z" />
|
||||
</vector>
|
@ -0,0 +1,26 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="58.0"
|
||||
android:viewportWidth="58.0">
|
||||
<path
|
||||
android:fillColor="@color/gradientPink"
|
||||
android:pathData="M17.11,23.49L13.05,27.55L26.1,40.6L55.1,11.6L51.04,7.54L26.1,32.48L17.11,23.49L17.11,23.49ZM52.2,29C52.2,41.76 41.76,52.2 29,52.2C16.24,52.2 5.8,41.76 5.8,29C5.8,16.24 16.24,5.8 29,5.8C31.32,5.8 33.35,6.09 35.38,6.67L40.02,2.03C36.54,0.87 32.77,0 29,0C13.05,0 0,13.05 0,29C0,44.95 13.05,58 29,58C44.95,58 58,44.95 58,29L52.2,29L52.2,29Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
<path
|
||||
android:fillColor="#FFFFFE"
|
||||
android:pathData="M20,13C20,18.52 15.52,23 10,23C4.48,23 -0,18.52 -0,13C-0,7.48 4.48,3.01 10,3.01C15.52,3.01 20,7.48 20,13"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
<path
|
||||
android:fillColor="#FF6105"
|
||||
android:pathData="M10,3.01C4.48,3.01 -0.01,7.49 0,13C0,14.11 0.18,15.17 0.51,16.16L3.5,16.16L3.5,7.75L10,14.25L16.49,7.75L16.49,16.16L19.49,16.16C19.82,15.17 19.99,14.11 19.99,13C20,7.48 15.52,3.01 10,3.01L10,3.01Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
<path
|
||||
android:fillColor="#464543"
|
||||
android:pathData="M8.5,15.74L5.67,12.91L5.67,18.2L4.58,18.2L3.5,18.2L1.45,18.2C3.21,21.08 6.38,23 10,23C13.62,23 16.79,21.08 18.54,18.2L16.49,18.2L14.56,18.2L14.33,18.2L14.33,12.91L11.49,15.74L10,17.23L8.5,15.74L8.5,15.74Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
</vector>
|
@ -1,9 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="@color/moneroBlack"
|
||||
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z" />
|
||||
</vector>
|
||||
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFffffff"
|
||||
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
|
||||
</vector>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#ff8b0000"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13L7,13v-2h10v2z" />
|
||||
</vector>
|
@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M8.59,16.34l4.58,-4.59 -4.58,-4.59L10,5.75l6,6 -6,6z"/>
|
||||
</vector>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="@color/give"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM6.5,9L10,5.5 13.5,9L11,9v4L9,13L9,9L6.5,9zM17.5,15L14,18.5 10.5,15L13,15v-4h2v4h2.5z" />
|
||||
</vector>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="@color/take"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z" />
|
||||
</vector>
|
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvFunds"
|
||||
style="@style/MoneroText.Funds"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center"
|
||||
tools:text="@string/send_available_btc" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.m2049r.xmrwallet.widget.SendProgressView
|
||||
android:id="@+id/evXmrToParms"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llXmrToParms"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="invisible">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
android:paddingTop="8dp"
|
||||
android:src="@drawable/ic_xmrto_32dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvXmrToParms"
|
||||
style="@style/MoneroText.Info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|top"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:singleLine="false"
|
||||
tools:text="@string/info_send_xmrto_parms" />
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<com.m2049r.xmrwallet.widget.ExchangeBtcTextView
|
||||
android:id="@+id/evAmount"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<com.m2049r.xmrwallet.widget.NumberPadView
|
||||
android:id="@+id/numberPad"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white"
|
||||
android:gravity="center" />
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,252 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
card_view:cardCornerRadius="2dp"
|
||||
card_view:cardElevation="8dp"
|
||||
card_view:contentPadding="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.m2049r.xmrwallet.widget.SendProgressView
|
||||
android:id="@+id/evStageA"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llStageA"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="visible">
|
||||
|
||||
<TextView
|
||||
style="@style/MoneroText.Confirm.Label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:drawablePadding="8dp"
|
||||
android:drawableStart="@drawable/ic_xmrto_32dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/label_send_btc_xmrto_key" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTxXmrToKey"
|
||||
style="@style/MoneroText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/dotGray"
|
||||
android:drawableEnd="@drawable/ic_content_copy_white_24dp"
|
||||
android:drawablePadding="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingTop="8dp"
|
||||
android:textColor="@color/white"
|
||||
tools:text="XMR.TO-d2KQ" />
|
||||
|
||||
<TextView
|
||||
style="@style/MoneroText.Medium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/label_send_btc_xmrto_info" />
|
||||
|
||||
<TextView
|
||||
style="@style/MoneroText.Confirm.Label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/label_send_btc_address"
|
||||
android:textAlignment="textStart" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTxBtcAddress"
|
||||
style="@style/MoneroText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="textStart"
|
||||
tools:text="mpQ84J43EURZHkCnXbyQ4PpNDLLBqdsMW2" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:color/darker_gray" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.m2049r.xmrwallet.widget.SendProgressView
|
||||
android:id="@+id/evStageB"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llStageB"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="invisible">
|
||||
|
||||
<TextView
|
||||
style="@style/MoneroText.Confirm.Label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/label_send_btc_amount"
|
||||
android:textAlignment="textStart" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTxBtcAmount"
|
||||
style="@style/MoneroText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="textStart"
|
||||
tools:text="1.75 BTC = 84.118438761777 XMR" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTxBtcRate"
|
||||
style="@style/MoneroLabel.Gray"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="textStart"
|
||||
android:textSize="16sp"
|
||||
tools:text="(Rate: 0.020804 BTC/XMR)" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</android.support.v7.widget.CardView>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginStart="16dp">
|
||||
|
||||
<com.m2049r.xmrwallet.widget.SendProgressView
|
||||
android:id="@+id/evStageC"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llStageC"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="invisible">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="3">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTxFeeLabel"
|
||||
style="@style/MoneroLabel.Gray"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/send_fee_btc_label"
|
||||
android:textAlignment="textStart"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTxFee"
|
||||
style="@style/MoneroText.Gray"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:textAlignment="textEnd"
|
||||
android:textSize="16sp"
|
||||
tools:text="0.006817000000" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="3">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTxTotalLabel"
|
||||
style="@style/MoneroLabel.Caps.Black"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/send_total_btc_label"
|
||||
android:textAlignment="textStart"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTxTotal"
|
||||
style="@style/MoneroText.Black"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:textAlignment="textEnd"
|
||||
android:textSize="16sp"
|
||||
tools:text="143.014817000000" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llConfirmSend"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="vertical"
|
||||
android:visibility="invisible">
|
||||
|
||||
<Button
|
||||
android:id="@+id/bSend"
|
||||
style="@style/MoneroButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:enabled="true"
|
||||
android:padding="8dp"
|
||||
android:text="@string/send_send_label" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pbProgressSend"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:indeterminate="true"
|
||||
android:visibility="invisible" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,260 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="72dp"
|
||||
android:layout_gravity="top"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:padding="6dp"
|
||||
android:src="@drawable/ic_check_circle_xmr" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
style="@style/MoneroText.Success"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text="@string/label_send_success" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTxAmount"
|
||||
style="@style/MoneroText.Balance.Orange"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:textAlignment="textEnd"
|
||||
tools:text="143.008000 XMR" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTxFee"
|
||||
style="@style/MoneroText.Gray.SuccessFee"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:textAlignment="textEnd"
|
||||
tools:text="+0.006817 Fee" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
style="@style/MoneroText.Confirm.Label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/label_send_txid"
|
||||
android:textAlignment="textStart" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/bCopyTxId"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:enabled="false"
|
||||
android:src="@drawable/ic_content_nocopy_black_24dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTxId"
|
||||
style="@style/MoneroText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textAlignment="textStart"
|
||||
tools:text="fcb12cbe9f43d4e8b9ee54f48d450a89a6937946db856506820df0539571801d" />
|
||||
|
||||
<TextView
|
||||
style="@style/MoneroText.Confirm.Label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/label_send_address"
|
||||
android:textAlignment="textStart" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTxAddress"
|
||||
style="@style/MoneroText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textAlignment="textStart"
|
||||
tools:text="4AdkPJoxn7JCvAby9szgnt93MSEwdnxdhaASxbTBm6x5dCwmsDep2UYN4FhStDn5i11nsJbpU7oj59ahg8gXb1Mg3viqCuk" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/labelPaymentId"
|
||||
style="@style/MoneroText.Confirm.Label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/label_send_payment_id" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTxPaymentId"
|
||||
style="@style/MoneroText.Confirm"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
tools:text="d666a38d4a28fb38" />
|
||||
</LinearLayout>
|
||||
|
||||
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/cvXmrTo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
card_view:cardCornerRadius="2dp"
|
||||
card_view:cardElevation="8dp"
|
||||
card_view:contentPadding="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/flXmrtoProgress"
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pbXmrto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="72dp"
|
||||
android:layout_gravity="center"
|
||||
android:backgroundTintMode="src_in"
|
||||
android:indeterminate="true"
|
||||
android:indeterminateTint="@color/moneroFab" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivXmrToStatus"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_pending_orange_24dp"
|
||||
android:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivXmrToStatusBig"
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="72dp"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="top|start"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:src="@drawable/ic_xmrto_whitestroke_24px" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvXmrToStatus"
|
||||
style="@style/MoneroText"
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/flXmrtoProgress"
|
||||
android:gravity="center"
|
||||
android:text="@string/info_send_xmrto_query"
|
||||
android:textSize="10sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="Confirmation Pending" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
style="@style/MoneroText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/info_send_xmrto_success_order_label"
|
||||
android:textAllCaps="true"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvXmrToAmount"
|
||||
style="@style/MoneroText.Balance.Orange"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:gravity="center"
|
||||
tools:text="0.001 BTC" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTxXmrToKey"
|
||||
style="@style/MoneroText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/dotGray"
|
||||
android:drawableEnd="@drawable/ic_content_copy_white_24dp"
|
||||
android:drawablePadding="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:textColor="@color/white"
|
||||
tools:text="XMR.TO-d2KQ" />
|
||||
|
||||
<TextView
|
||||
style="@style/MoneroText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginTop="8dp"
|
||||
android:drawablePadding="4dp"
|
||||
android:drawableStart="@drawable/ic_info_outline_gray_24dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/label_send_btc_xmrto_info"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</android.support.v7.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:showIn="LinearLayout">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/sCurrencyA"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAmountA"
|
||||
style="@style/MoneroText.Balance.Orange"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center|start"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_weight="3"
|
||||
android:hint="@string/send_amount_hint"
|
||||
android:padding="4dp"
|
||||
android:singleLine="true"
|
||||
tools:text="87.00000" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/sCurrencyB"
|
||||
android:layout_width="56sp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAmountB"
|
||||
style="@style/MoneroText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center|start"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_weight="3"
|
||||
android:hint="@string/send_amount_hint"
|
||||
android:padding="4dp"
|
||||
android:singleLine="true"
|
||||
tools:text="87.00000" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</merge>
|
||||
|
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:showIn="LinearLayout">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pbProgress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:indeterminate="true"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="visible">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCode"
|
||||
style="@style/MoneroText.Confirm.Label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
tools:text="XMRTO-ERROR-001" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvMessage"
|
||||
style="@style/MoneroText.Confirm"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
tools:text="internal services not available" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSolution"
|
||||
style="@style/MoneroText.Confirm.Label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
tools:text="Touch to retry" />
|
||||
</LinearLayout>
|
||||
</merge>
|
||||
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
||||
public class BitcoinAddressValidatorTest {
|
||||
|
||||
@Test
|
||||
public void validateBTC_shouldValidate() {
|
||||
assertTrue(BitcoinAddressValidator.validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", false));
|
||||
assertTrue(BitcoinAddressValidator.validate("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9", false));
|
||||
assertTrue(BitcoinAddressValidator.validate("3R2MPpTNQLCNs13qnHz89Rm82jQ27bAwft", false));
|
||||
assertTrue(BitcoinAddressValidator.validate("34QjytsE8GVRbUBvYNheftqJ5CHfDHvQRD", false));
|
||||
assertTrue(BitcoinAddressValidator.validate("3GsAUrD4dnCqtaTTUzzsQWoymHNkEFrgGF", false));
|
||||
assertTrue(BitcoinAddressValidator.validate("3NagLCvw8fLwtoUrK7s2mJPy9k6hoyWvTU", false));
|
||||
assertTrue(BitcoinAddressValidator.validate("mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9", true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateBTC_shouldNotValidate() {
|
||||
assertTrue(BitcoinAddressValidator.validate("3NagLCvw8fLwtoUrK7s2mJPy9k6hoyWvTU", false));
|
||||
assertTrue(BitcoinAddressValidator.validate("mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9", true));
|
||||
assertTrue(!BitcoinAddressValidator.validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j", true));
|
||||
assertTrue(!BitcoinAddressValidator.validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X", true));
|
||||
assertTrue(!BitcoinAddressValidator.validate("1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", true));
|
||||
assertTrue(!BitcoinAddressValidator.validate("1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", true));
|
||||
assertTrue(!BitcoinAddressValidator.validate("BZbvjr", true));
|
||||
assertTrue(!BitcoinAddressValidator.validate("i55j", true));
|
||||
assertTrue(!BitcoinAddressValidator.validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!", true));
|
||||
assertTrue(!BitcoinAddressValidator.validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz", false));
|
||||
assertTrue(!BitcoinAddressValidator.validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz", false));
|
||||
assertTrue(!BitcoinAddressValidator.validate("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9", false));
|
||||
assertTrue(!BitcoinAddressValidator.validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I", false));
|
||||
assertTrue(!BitcoinAddressValidator.validate("3NagLCvw8fLwtoUrK7s2mJPy9k6hoyWvTU ", false));
|
||||
assertTrue(!BitcoinAddressValidator.validate(" 3NagLCvw8fLwtoUrK7s2mJPy9k6hoyWvTU ", false));
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2017 m2049r
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class UserNoteTest {
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_noNote() {
|
||||
UserNotes userNotes = new UserNotes("{xmrto-iyrpxU,0.009BTC,mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9}");
|
||||
assertTrue("xmrto-iyrpxU".equals(userNotes.xmrtoKey));
|
||||
assertTrue("0.009".equals(userNotes.xmrtoAmount));
|
||||
assertTrue("mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9".equals(userNotes.xmrtoDestination));
|
||||
assertTrue(userNotes.note.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_withNote() {
|
||||
UserNotes userNotes = new UserNotes("{xmrto-iyrpxU,0.009BTC,mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9} aNote");
|
||||
assertTrue("xmrto-iyrpxU".equals(userNotes.xmrtoKey));
|
||||
assertTrue("0.009".equals(userNotes.xmrtoAmount));
|
||||
assertTrue("mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9".equals(userNotes.xmrtoDestination));
|
||||
assertTrue("aNote".equals(userNotes.note));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_withNoteNoSpace() {
|
||||
UserNotes userNotes = new UserNotes("{xmrto-iyrpxU,0.009BTC,mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9}aNote");
|
||||
assertTrue("xmrto-iyrpxU".equals(userNotes.xmrtoKey));
|
||||
assertTrue("0.009".equals(userNotes.xmrtoAmount));
|
||||
assertTrue("mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9".equals(userNotes.xmrtoDestination));
|
||||
assertTrue("aNote".equals(userNotes.note));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_brokenA() {
|
||||
String brokenNote = "{mrto-iyrpxU,0.009BTC,mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9}";
|
||||
UserNotes userNotes = new UserNotes(brokenNote);
|
||||
assertNull(userNotes.xmrtoKey);
|
||||
assertNull(userNotes.xmrtoAmount);
|
||||
assertNull(userNotes.xmrtoDestination);
|
||||
assertTrue(brokenNote.equals(userNotes.note));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_brokenB() {
|
||||
String brokenNote = "{xmrto-iyrpxU,0.009BTC,mjn127C5wRQCULksMYMFHLp9UTdQuCfbZ9";
|
||||
UserNotes userNotes = new UserNotes(brokenNote);
|
||||
assertNull(userNotes.xmrtoKey);
|
||||
assertNull(userNotes.xmrtoAmount);
|
||||
assertNull(userNotes.xmrtoDestination);
|
||||
assertTrue(brokenNote.equals(userNotes.note));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_normal() {
|
||||
String aNote = "aNote";
|
||||
UserNotes userNotes = new UserNotes(aNote);
|
||||
assertNull(userNotes.xmrtoKey);
|
||||
assertNull(userNotes.xmrtoAmount);
|
||||
assertNull(userNotes.xmrtoDestination);
|
||||
assertTrue(aNote.equals(userNotes.note));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_empty() {
|
||||
String aNote = "";
|
||||
UserNotes userNotes = new UserNotes(aNote);
|
||||
assertNull(userNotes.xmrtoKey);
|
||||
assertNull(userNotes.xmrtoAmount);
|
||||
assertNull(userNotes.xmrtoDestination);
|
||||
assertTrue(aNote.equals(userNotes.note));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromTxNote_null() {
|
||||
UserNotes userNotes = new UserNotes(null);
|
||||
assertNull(userNotes.xmrtoKey);
|
||||
assertNull(userNotes.xmrtoAmount);
|
||||
assertNull(userNotes.xmrtoDestination);
|
||||
assertNotNull(userNotes.note);
|
||||
assertTrue(userNotes.note.isEmpty());
|
||||
}
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto.network;
|
||||
|
||||
import com.m2049r.xmrwallet.xmrto.api.XmrToCallback;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToError;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToException;
|
||||
import com.m2049r.xmrwallet.xmrto.api.CreateOrder;
|
||||
import com.m2049r.xmrwallet.xmrto.api.XmrToApi;
|
||||
|
||||
import net.jodah.concurrentunit.Waiter;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class XmrToApiCreateOrderTest {
|
||||
|
||||
private MockWebServer mockWebServer;
|
||||
|
||||
private XmrToApi xmrToApi;
|
||||
|
||||
private OkHttpClient okHttpClient = new OkHttpClient();
|
||||
private Waiter waiter;
|
||||
|
||||
@Mock
|
||||
XmrToCallback<CreateOrder> mockOrderXmrToCallback;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mockWebServer = new MockWebServer();
|
||||
mockWebServer.start();
|
||||
|
||||
waiter = new Waiter();
|
||||
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
xmrToApi = new XmrToApiImpl(okHttpClient, mockWebServer.url("/"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
mockWebServer.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createOrder_shouldBePostMethod()
|
||||
throws InterruptedException, TimeoutException {
|
||||
|
||||
xmrToApi.createOrder(0.5, "btcsomething", mockOrderXmrToCallback);
|
||||
|
||||
RecordedRequest request = mockWebServer.takeRequest();
|
||||
assertEquals("POST", request.getMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createOrder_shouldBeContentTypeJson()
|
||||
throws InterruptedException, TimeoutException {
|
||||
|
||||
xmrToApi.createOrder(0.5, "19y91nJyzXsLEuR7Nj9pc3o5SeHNc8A9RW", mockOrderXmrToCallback);
|
||||
|
||||
RecordedRequest request = mockWebServer.takeRequest();
|
||||
assertEquals("application/json; charset=utf-8", request.getHeader("Content-Type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createOrder_shouldContainValidBody()
|
||||
throws InterruptedException, TimeoutException {
|
||||
|
||||
final String validBody = "{\"btc_amount\":0.1,\"btc_dest_address\":\"19y91nJyzXsLEuR7Nj9pc3o5SeHNc8A9RW\"}";
|
||||
|
||||
xmrToApi.createOrder(0.1, "19y91nJyzXsLEuR7Nj9pc3o5SeHNc8A9RW", mockOrderXmrToCallback);
|
||||
|
||||
RecordedRequest request = mockWebServer.takeRequest();
|
||||
String body = request.getBody().readUtf8();
|
||||
assertEquals(validBody, body);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createOrder_wasSuccessfulShouldRespondWithOrder()
|
||||
throws InterruptedException, JSONException, TimeoutException {
|
||||
final double amount = 1.23456789;
|
||||
final String address = "19y91nJyzXsLEuR7Nj9pc3o5SeHNc8A9RW";
|
||||
final String uuid = "xmrto-abcdef";
|
||||
final String state = "TO_BE_CREATED";
|
||||
MockResponse jsonMockResponse = new MockResponse().setBody(
|
||||
createMockCreateOrderResponse(amount, address, uuid, state));
|
||||
mockWebServer.enqueue(jsonMockResponse);
|
||||
|
||||
xmrToApi.createOrder(amount, address, new XmrToCallback<CreateOrder>() {
|
||||
@Override
|
||||
public void onSuccess(final CreateOrder order) {
|
||||
waiter.assertEquals(order.getBtcAmount(), amount);
|
||||
waiter.assertEquals(order.getBtcDestAddress(), address);
|
||||
waiter.assertEquals(order.getState(), state);
|
||||
waiter.assertEquals(order.getUuid(), uuid);
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Exception e) {
|
||||
waiter.fail(e);
|
||||
waiter.resume();
|
||||
}
|
||||
});
|
||||
waiter.await();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createOrder_wasNotSuccessfulShouldCallOnError()
|
||||
throws InterruptedException, JSONException, TimeoutException {
|
||||
mockWebServer.enqueue(new MockResponse().setResponseCode(500));
|
||||
xmrToApi.createOrder(0.5, "19y91nJyzXsLEuR7Nj9pc3o5SeHNc8A9RW", new XmrToCallback<CreateOrder>() {
|
||||
@Override
|
||||
public void onSuccess(final CreateOrder order) {
|
||||
waiter.fail();
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Exception e) {
|
||||
waiter.assertTrue(e instanceof XmrToException);
|
||||
waiter.assertTrue(((XmrToException) e).getCode() == 500);
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
});
|
||||
waiter.await();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createOrder_malformedAddressShouldCallOnError()
|
||||
throws InterruptedException, JSONException, TimeoutException {
|
||||
mockWebServer.enqueue(new MockResponse().
|
||||
setResponseCode(400).
|
||||
setBody("{\"error_msg\":\"malformed bitcoin address\",\"error\":\"XMRTO-ERROR-002\"}"));
|
||||
xmrToApi.createOrder(0.5, "xxx", new XmrToCallback<CreateOrder>() {
|
||||
@Override
|
||||
public void onSuccess(final CreateOrder order) {
|
||||
waiter.fail();
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Exception e) {
|
||||
waiter.assertTrue(e instanceof XmrToException);
|
||||
XmrToException xmrEx = (XmrToException) e;
|
||||
waiter.assertTrue(xmrEx.getCode() == 400);
|
||||
waiter.assertNotNull(xmrEx.getError());
|
||||
waiter.assertEquals(xmrEx.getError().getErrorId(), XmrToError.Error.XMRTO_ERROR_002);
|
||||
waiter.assertEquals(xmrEx.getError().getErrorMsg(), "malformed bitcoin address");
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
});
|
||||
waiter.await();
|
||||
}
|
||||
|
||||
private String createMockCreateOrderResponse(final double amount, final String address,
|
||||
final String uuid, final String state) {
|
||||
return "{\n"
|
||||
+ " \"state\": \"" + state + "\",\n"
|
||||
+ " \"btc_amount\": \"" + amount + "\",\n"
|
||||
+ " \"btc_dest_address\": \"" + address + "\",\n"
|
||||
+ " \"uuid\": \"" + uuid + "\"\n"
|
||||
+ "}";
|
||||
}
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto.network;
|
||||
|
||||
import com.m2049r.xmrwallet.xmrto.api.XmrToCallback;
|
||||
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 net.jodah.concurrentunit.Waiter;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class XmrToApiOrderParameterTest {
|
||||
|
||||
private MockWebServer mockWebServer;
|
||||
|
||||
private XmrToApi xmrToApi;
|
||||
|
||||
private OkHttpClient okHttpClient = new OkHttpClient();
|
||||
private Waiter waiter;
|
||||
|
||||
@Mock
|
||||
XmrToCallback<QueryOrderParameters> mockParametersXmrToCallback;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mockWebServer = new MockWebServer();
|
||||
mockWebServer.start();
|
||||
|
||||
waiter = new Waiter();
|
||||
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
xmrToApi = new XmrToApiImpl(okHttpClient, mockWebServer.url("/"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
mockWebServer.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderParameter_shouldBeGetMethod()
|
||||
throws InterruptedException, TimeoutException {
|
||||
|
||||
xmrToApi.queryOrderParameters(mockParametersXmrToCallback);
|
||||
|
||||
RecordedRequest request = mockWebServer.takeRequest();
|
||||
assertEquals("GET", request.getMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderParameter_wasSuccessfulShouldRespondWithParameters()
|
||||
throws InterruptedException, JSONException, TimeoutException {
|
||||
final boolean isZeroConfEnabled = true;
|
||||
final double price = 0.015537;
|
||||
final double upperLimit = 20.0;
|
||||
final double lowerLimit = 0.001;
|
||||
final double zeroConfMaxAmount = 0.1;
|
||||
|
||||
MockResponse jsonMockResponse = new MockResponse().setBody(
|
||||
createMockOrderParameterResponse(isZeroConfEnabled, price, upperLimit, lowerLimit, zeroConfMaxAmount));
|
||||
mockWebServer.enqueue(jsonMockResponse);
|
||||
|
||||
xmrToApi.queryOrderParameters(new XmrToCallback<QueryOrderParameters>() {
|
||||
@Override
|
||||
public void onSuccess(final QueryOrderParameters orderParameter) {
|
||||
waiter.assertEquals(orderParameter.getLowerLimit(), lowerLimit);
|
||||
waiter.assertEquals(orderParameter.getUpperLimit(), upperLimit);
|
||||
waiter.assertEquals(orderParameter.getPrice(), price);
|
||||
waiter.assertEquals(orderParameter.getZeroConfMaxAmount(), zeroConfMaxAmount);
|
||||
waiter.assertEquals(orderParameter.isZeroConfEnabled(), isZeroConfEnabled);
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Exception e) {
|
||||
waiter.fail(e);
|
||||
waiter.resume();
|
||||
}
|
||||
});
|
||||
waiter.await();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderParameter_wasNotSuccessfulShouldCallOnError()
|
||||
throws InterruptedException, JSONException, TimeoutException {
|
||||
mockWebServer.enqueue(new MockResponse().setResponseCode(500));
|
||||
xmrToApi.queryOrderParameters(new XmrToCallback<QueryOrderParameters>() {
|
||||
@Override
|
||||
public void onSuccess(final QueryOrderParameters orderParameter) {
|
||||
waiter.fail();
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Exception e) {
|
||||
waiter.assertTrue(e instanceof XmrToException);
|
||||
waiter.assertTrue(((XmrToException) e).getCode() == 500);
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
});
|
||||
waiter.await();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderParameter_thirdPartyServiceNotAvailableShouldCallOnError()
|
||||
throws InterruptedException, JSONException, TimeoutException {
|
||||
mockWebServer.enqueue(new MockResponse().
|
||||
setResponseCode(503).
|
||||
setBody("{\"error_msg\":\"third party service not available\",\"error\":\"XMRTO-ERROR-007\"}"));
|
||||
xmrToApi.queryOrderParameters(new XmrToCallback<QueryOrderParameters>() {
|
||||
@Override
|
||||
public void onSuccess(final QueryOrderParameters orderParameter) {
|
||||
waiter.fail();
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Exception e) {
|
||||
waiter.assertTrue(e instanceof XmrToException);
|
||||
XmrToException xmrEx = (XmrToException) e;
|
||||
waiter.assertTrue(xmrEx.getCode() == 503);
|
||||
waiter.assertNotNull(xmrEx.getError());
|
||||
waiter.assertEquals(xmrEx.getError().getErrorId(), XmrToError.Error.XMRTO_ERROR_007);
|
||||
waiter.assertEquals(xmrEx.getError().getErrorMsg(), "third party service not available");
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
});
|
||||
waiter.await();
|
||||
}
|
||||
|
||||
private String createMockOrderParameterResponse(
|
||||
final boolean isZeroConfEnabled,
|
||||
final double price,
|
||||
final double upperLimit,
|
||||
final double lowerLimit,
|
||||
final double zeroConfMaxAmount) {
|
||||
return "{\n"
|
||||
+ " \"zero_conf_enabled\": \"" + isZeroConfEnabled + "\",\n"
|
||||
+ " \"price\": \"" + price + "\",\n"
|
||||
+ " \"upper_limit\": \"" + upperLimit + "\",\n"
|
||||
+ " \"lower_limit\": \"" + lowerLimit + "\",\n"
|
||||
+ " \"zero_conf_max_amount\": \"" + zeroConfMaxAmount + "\"\n"
|
||||
+ "}";
|
||||
}
|
||||
}
|
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.xmrto.network;
|
||||
|
||||
import com.m2049r.xmrwallet.xmrto.api.XmrToCallback;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToError;
|
||||
import com.m2049r.xmrwallet.xmrto.XmrToException;
|
||||
import com.m2049r.xmrwallet.xmrto.api.QueryOrderStatus;
|
||||
import com.m2049r.xmrwallet.xmrto.api.XmrToApi;
|
||||
|
||||
import net.jodah.concurrentunit.Waiter;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class XmrToApiQueryOrderTest {
|
||||
|
||||
static final SimpleDateFormat DATETIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
|
||||
|
||||
static Date ParseDate(String dateString) throws ParseException {
|
||||
return DATETIME_FORMATTER.parse(dateString.replaceAll("Z$", "+0000"));
|
||||
}
|
||||
|
||||
|
||||
private MockWebServer mockWebServer;
|
||||
|
||||
private XmrToApi xmrToApi;
|
||||
|
||||
private OkHttpClient okHttpClient = new OkHttpClient();
|
||||
private Waiter waiter;
|
||||
|
||||
@Mock
|
||||
XmrToCallback<QueryOrderStatus> mockQueryXmrToCallback;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mockWebServer = new MockWebServer();
|
||||
mockWebServer.start();
|
||||
|
||||
waiter = new Waiter();
|
||||
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
xmrToApi = new XmrToApiImpl(okHttpClient, mockWebServer.url("/"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
mockWebServer.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderStatus_shouldBePostMethod()
|
||||
throws InterruptedException, TimeoutException {
|
||||
|
||||
xmrToApi.queryOrderStatus("xmrto - efMsiU", mockQueryXmrToCallback);
|
||||
|
||||
RecordedRequest request = mockWebServer.takeRequest();
|
||||
assertEquals("POST", request.getMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderStatus_shouldBeContentTypeJson()
|
||||
throws InterruptedException, TimeoutException {
|
||||
|
||||
xmrToApi.queryOrderStatus("xmrto - efMsiU", mockQueryXmrToCallback);
|
||||
|
||||
RecordedRequest request = mockWebServer.takeRequest();
|
||||
assertEquals("application/json; charset=utf-8", request.getHeader("Content-Type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderStatus_shouldContainValidBody()
|
||||
throws InterruptedException, TimeoutException {
|
||||
|
||||
final String validBody = "{\"uuid\":\"xmrto - efMsiU\"}";
|
||||
|
||||
xmrToApi.queryOrderStatus("xmrto - efMsiU", mockQueryXmrToCallback);
|
||||
|
||||
RecordedRequest request = mockWebServer.takeRequest();
|
||||
String body = request.getBody().readUtf8();
|
||||
assertEquals(validBody, body);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderStatus_wasSuccessfulShouldRespondWithOrder()
|
||||
throws InterruptedException, JSONException, TimeoutException {
|
||||
|
||||
//TODO: state enum
|
||||
// TODO dates are dates
|
||||
final String state = "UNPAID";
|
||||
final double btcAmount = 0.1;
|
||||
final String btcDestAddress = "1FhnVJi2V1k4MqXm2nHoEbY5LV7FPai7bb";
|
||||
final String uuid = "xmrto - efMsiU";
|
||||
final int btcNumConfirmations = 0;
|
||||
final int btcNumConfirmationsBeforePurge = 144;
|
||||
final String btcTransactionId = "";
|
||||
final String createdAt = "2017-11-17T12:20:02Z";
|
||||
final String expiresAt = "2017-11-17T12:35:02Z";
|
||||
final int secondsTillTimeout = 882;
|
||||
final double xmrAmountTotal = 6.464;
|
||||
final double xmrAmountRemaining = 6.464;
|
||||
final int xmrNumConfirmationsRemaining = -1;
|
||||
final double xmrPriceBtc = 0.0154703;
|
||||
final String xmrReceivingAddress = "44TVPcCSHebEQp4LnapPkhb2pondb2Ed7GJJLc6TkKwtSyumUnQ6QzkCCkojZycH2MRfLcujCM7QR1gdnRULRraV4UpB5n4";
|
||||
final String xmrReceivingIntegratedAddress = "4EAAQR1vtv7EQp4LnapPkhb2pondb2Ed7GJJLc6TkKwtSyumUnQ6QzkCCkojZycH2MRfLcujCM7QR1gdnRULRraV6B5rRtHLeXGQSECXy9";
|
||||
final int xmrRecommendedMixin = 5;
|
||||
final double xmrRequiredAmount = 6.464;
|
||||
final String xmrRequiredPaymentIdLong = "56beabc3ca6d52a78c9a44cefebeb870054d8b367cc7065bff1bdb553caca85c";
|
||||
final String xmrRequiredPaymentIdShort = "eeb6086436b267cf";
|
||||
|
||||
MockResponse jsonMockResponse = new MockResponse().setBody(
|
||||
createMockQueryOrderResponse(
|
||||
state,
|
||||
btcAmount,
|
||||
btcDestAddress,
|
||||
uuid,
|
||||
btcNumConfirmations,
|
||||
btcNumConfirmationsBeforePurge,
|
||||
btcTransactionId,
|
||||
createdAt,
|
||||
expiresAt,
|
||||
secondsTillTimeout,
|
||||
xmrAmountTotal,
|
||||
xmrAmountRemaining,
|
||||
xmrNumConfirmationsRemaining,
|
||||
xmrPriceBtc,
|
||||
xmrReceivingAddress,
|
||||
xmrReceivingIntegratedAddress,
|
||||
xmrRecommendedMixin,
|
||||
xmrRequiredAmount,
|
||||
xmrRequiredPaymentIdLong,
|
||||
xmrRequiredPaymentIdShort));
|
||||
mockWebServer.enqueue(jsonMockResponse);
|
||||
|
||||
xmrToApi.queryOrderStatus(uuid, new XmrToCallback<QueryOrderStatus>() {
|
||||
@Override
|
||||
public void onSuccess(final QueryOrderStatus orderStatus) {
|
||||
waiter.assertEquals(orderStatus.getState().toString(), state);
|
||||
waiter.assertEquals(orderStatus.getBtcAmount(), btcAmount);
|
||||
waiter.assertEquals(orderStatus.getBtcDestAddress(), btcDestAddress);
|
||||
waiter.assertEquals(orderStatus.getUuid(), uuid);
|
||||
waiter.assertEquals(orderStatus.getBtcNumConfirmations(), btcNumConfirmations);
|
||||
waiter.assertEquals(orderStatus.getBtcNumConfirmationsBeforePurge(), btcNumConfirmationsBeforePurge);
|
||||
waiter.assertEquals(orderStatus.getBtcTransactionId(), btcTransactionId);
|
||||
try {
|
||||
waiter.assertEquals(orderStatus.getCreatedAt(), ParseDate(createdAt));
|
||||
waiter.assertEquals(orderStatus.getExpiresAt(), ParseDate(expiresAt));
|
||||
} catch (ParseException ex) {
|
||||
waiter.fail(ex);
|
||||
}
|
||||
waiter.assertEquals(orderStatus.getSecondsTillTimeout(), secondsTillTimeout);
|
||||
waiter.assertEquals(orderStatus.getXmrAmountTotal(), xmrAmountTotal);
|
||||
waiter.assertEquals(orderStatus.getXmrAmountRemaining(), xmrAmountRemaining);
|
||||
waiter.assertEquals(orderStatus.getXmrNumConfirmationsRemaining(), xmrNumConfirmationsRemaining);
|
||||
waiter.assertEquals(orderStatus.getXmrPriceBtc(), xmrPriceBtc);
|
||||
waiter.assertEquals(orderStatus.getXmrReceivingAddress(), xmrReceivingAddress);
|
||||
waiter.assertEquals(orderStatus.getXmrReceivingIntegratedAddress(), xmrReceivingIntegratedAddress);
|
||||
waiter.assertEquals(orderStatus.getXmrRecommendedMixin(), xmrRecommendedMixin);
|
||||
waiter.assertEquals(orderStatus.getXmrRequiredAmount(), xmrRequiredAmount);
|
||||
waiter.assertEquals(orderStatus.getXmrRequiredPaymentIdLong(), xmrRequiredPaymentIdLong);
|
||||
waiter.assertEquals(orderStatus.getXmrRequiredPaymentIdShort(), xmrRequiredPaymentIdShort);
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Exception e) {
|
||||
waiter.fail(e);
|
||||
waiter.resume();
|
||||
}
|
||||
});
|
||||
waiter.await();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderStatus_wasNotSuccessfulShouldCallOnError()
|
||||
throws InterruptedException, JSONException, TimeoutException {
|
||||
mockWebServer.enqueue(new MockResponse().setResponseCode(500));
|
||||
xmrToApi.queryOrderStatus("xmrto - efMsiU", new XmrToCallback<QueryOrderStatus>() {
|
||||
@Override
|
||||
public void onSuccess(final QueryOrderStatus orderStatus) {
|
||||
waiter.fail();
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Exception e) {
|
||||
waiter.assertTrue(e instanceof XmrToException);
|
||||
waiter.assertTrue(((XmrToException) e).getCode() == 500);
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
});
|
||||
waiter.await();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderStatus_orderNotFoundShouldCallOnError()
|
||||
throws InterruptedException, JSONException, TimeoutException {
|
||||
mockWebServer.enqueue(new MockResponse().
|
||||
setResponseCode(404).
|
||||
setBody("{\"error_msg\":\"requested order not found\",\"error\":\"XMRTO-ERROR-006\"}"));
|
||||
xmrToApi.queryOrderStatus("xmrto - efMsiU", new XmrToCallback<QueryOrderStatus>() {
|
||||
@Override
|
||||
public void onSuccess(final QueryOrderStatus orderStatus) {
|
||||
waiter.fail();
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Exception e) {
|
||||
waiter.assertTrue(e instanceof XmrToException);
|
||||
XmrToException xmrEx = (XmrToException) e;
|
||||
waiter.assertTrue(xmrEx.getCode() == 404);
|
||||
waiter.assertNotNull(xmrEx.getError());
|
||||
waiter.assertEquals(xmrEx.getError().getErrorId(), XmrToError.Error.XMRTO_ERROR_006);
|
||||
waiter.assertEquals(xmrEx.getError().getErrorMsg(), "requested order not found");
|
||||
waiter.resume();
|
||||
}
|
||||
|
||||
});
|
||||
waiter.await();
|
||||
}
|
||||
|
||||
private String createMockQueryOrderResponse(
|
||||
final String state,
|
||||
final double btcAmount,
|
||||
final String btcDestAddress,
|
||||
final String uuid,
|
||||
final int btcNumConfirmations,
|
||||
final int btcNumConfirmationsBeforePurge,
|
||||
final String btcTransactionId,
|
||||
final String createdAt,
|
||||
final String expiresAt,
|
||||
final int secondsTillTimeout,
|
||||
final double xmrAmountTotal,
|
||||
final double xmrAmountRemaining,
|
||||
final int xmrNumConfirmationsRemaining,
|
||||
final double xmrPriceBtc,
|
||||
final String xmrReceivingAddress,
|
||||
final String xmrReceivingIntegratedAddress,
|
||||
final int xmrRecommendedMixin,
|
||||
final double xmrRequiredAmount,
|
||||
final String xmrRequiredPaymentIdLong,
|
||||
final String xmrRequiredPaymentIdShort
|
||||
) {
|
||||
return "{\n" +
|
||||
" \"xmr_price_btc\": \"" + xmrPriceBtc + "\",\n" +
|
||||
" \"uuid\":\"" + uuid + "\",\n" +
|
||||
" \"state\":\"" + state + "\",\n" +
|
||||
" \"btc_amount\":\"" + btcAmount + "\",\n" +
|
||||
" \"btc_dest_address\":\"" + btcDestAddress + "\",\n" +
|
||||
" \"xmr_required_amount\":\"" + xmrRequiredAmount + "\",\n" +
|
||||
" \"xmr_receiving_address\":\"" + xmrReceivingAddress + "\",\n" +
|
||||
" \"xmr_receiving_integrated_address\":\"" + xmrReceivingIntegratedAddress + "\",\n" +
|
||||
" \"xmr_required_payment_id_long\":\"" + xmrRequiredPaymentIdLong + "\",\n" +
|
||||
" \"xmr_required_payment_id_short\":\"" + xmrRequiredPaymentIdShort + "\",\n" +
|
||||
" \"created_at\":\"" + createdAt + "\",\n" +
|
||||
" \"expires_at\":\"" + expiresAt + "\",\n" +
|
||||
" \"seconds_till_timeout\":\"" + secondsTillTimeout + "\",\n" +
|
||||
" \"xmr_amount_total\":\"" + xmrAmountTotal + "\",\n" +
|
||||
" \"xmr_amount_remaining\":\"" + xmrAmountRemaining + "\",\n" +
|
||||
" \"xmr_num_confirmations_remaining\":\"" + xmrNumConfirmationsRemaining + "\",\n" +
|
||||
" \"xmr_recommended_mixin\":\"" + xmrRecommendedMixin + "\",\n" +
|
||||
" \"btc_num_confirmations_before_purge\":\"" + btcNumConfirmationsBeforePurge + "\",\n" +
|
||||
" \"btc_num_confirmations\":\"" + btcNumConfirmations + "\",\n" +
|
||||
" \"btc_transaction_id\":\"" + btcTransactionId + "\""
|
||||
+ "}";
|
||||
}
|
||||
}
|
Loading…
Reference in new issue