mirror of https://github.com/m2049r/xmrwallet.git
use yadio for pricing "exotic" fiat (#921)
* yadio api * add more currencies * fix es typo * bump version & fix cicleci build
This commit is contained in:
parent
17df7c3faf
commit
a6e9d0e77c
|
@ -3,7 +3,7 @@ jobs:
|
||||||
build:
|
build:
|
||||||
working_directory: ~/code
|
working_directory: ~/code
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/android:2022.03-ndk
|
- image: cimg/android:2023.12-ndk
|
||||||
environment:
|
environment:
|
||||||
JVM_OPTS: -Xmx3200m
|
JVM_OPTS: -Xmx3200m
|
||||||
steps:
|
steps:
|
||||||
|
|
|
@ -8,8 +8,8 @@ android {
|
||||||
compileSdk 33
|
compileSdk 33
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 33
|
targetSdkVersion 33
|
||||||
versionCode 3308
|
versionCode 3310
|
||||||
versionName "3.3.8 'Pocket Change'"
|
versionName "3.3.10 'Argentina'"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
|
|
|
@ -22,18 +22,24 @@ import android.content.res.Configuration;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.OptIn;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.fragment.app.FragmentStateManagerControl;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.model.NetworkType;
|
import com.m2049r.xmrwallet.model.NetworkType;
|
||||||
import com.m2049r.xmrwallet.util.LocaleHelper;
|
import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||||
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
||||||
import com.m2049r.xmrwallet.util.NightmodeHelper;
|
import com.m2049r.xmrwallet.util.NightmodeHelper;
|
||||||
|
import com.m2049r.xmrwallet.util.ServiceHelper;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class XmrWalletApplication extends Application {
|
public class XmrWalletApplication extends Application {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@OptIn(markerClass = FragmentStateManagerControl.class)
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
FragmentManager.enableNewStateManager(false);
|
FragmentManager.enableNewStateManager(false);
|
||||||
|
|
|
@ -33,5 +33,6 @@ public interface ExchangeApi {
|
||||||
void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final String quoteCurrency,
|
void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final String quoteCurrency,
|
||||||
@NonNull final ExchangeCallback callback);
|
@NonNull final ExchangeCallback callback);
|
||||||
|
|
||||||
|
String getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,9 +67,9 @@ public class ExchangeApiImpl implements ExchangeApi {
|
||||||
// data is daily and is refreshed around 16:00 CET every working day
|
// data is daily and is refreshed around 16:00 CET every working day
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSameDay(Calendar calendar, Calendar anotherCalendar) {
|
@Override
|
||||||
return (calendar.get(Calendar.YEAR) == anotherCalendar.get(Calendar.YEAR)) &&
|
public String getName() {
|
||||||
(calendar.get(Calendar.DAY_OF_YEAR) == anotherCalendar.get(Calendar.DAY_OF_YEAR));
|
return "ecb";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -121,12 +121,12 @@ public class ExchangeApiImpl implements ExchangeApi {
|
||||||
final NetCipherHelper.Request httpRequest = new NetCipherHelper.Request(baseUrl);
|
final NetCipherHelper.Request httpRequest = new NetCipherHelper.Request(baseUrl);
|
||||||
httpRequest.enqueue(new okhttp3.Callback() {
|
httpRequest.enqueue(new okhttp3.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(final Call call, final IOException ex) {
|
public void onFailure(@NonNull final Call call, @NonNull final IOException ex) {
|
||||||
callback.onError(ex);
|
callback.onError(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(final Call call, final Response response) throws IOException {
|
public void onResponse(@NonNull final Call call, @NonNull final Response response) throws IOException {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
try {
|
try {
|
||||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
||||||
|
@ -178,11 +178,12 @@ public class ExchangeApiImpl implements ExchangeApi {
|
||||||
Element cube = (Element) node;
|
Element cube = (Element) node;
|
||||||
if (cube.hasAttribute("time")) { // a time Cube
|
if (cube.hasAttribute("time")) { // a time Cube
|
||||||
final Date time = DATE_FORMAT.parse(cube.getAttribute("time"));
|
final Date time = DATE_FORMAT.parse(cube.getAttribute("time"));
|
||||||
|
assert time != null;
|
||||||
date.setTime(time);
|
date.setTime(time);
|
||||||
} else if (cube.hasAttribute("currency")
|
} else if (cube.hasAttribute("currency")
|
||||||
&& cube.hasAttribute("rate")) { // a rate Cube
|
&& cube.hasAttribute("rate")) { // a rate Cube
|
||||||
String currency = cube.getAttribute("currency");
|
String currency = cube.getAttribute("currency");
|
||||||
double rate = Double.valueOf(cube.getAttribute("rate"));
|
double rate = Double.parseDouble(cube.getAttribute("rate"));
|
||||||
entries.put(currency, rate);
|
entries.put(currency, rate);
|
||||||
} // else an empty Cube - ignore
|
} // else an empty Cube - ignore
|
||||||
}
|
}
|
||||||
|
@ -191,13 +192,10 @@ public class ExchangeApiImpl implements ExchangeApi {
|
||||||
Timber.d(ex);
|
Timber.d(ex);
|
||||||
}
|
}
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (date != null) {
|
fetchDate = Calendar.getInstance(TimeZone.getTimeZone("CET"));
|
||||||
fetchDate = Calendar.getInstance(TimeZone.getTimeZone("CET"));
|
fxDate = date;
|
||||||
fxDate = date;
|
fxEntries.clear();
|
||||||
fxEntries.clear();
|
fxEntries.putAll(entries);
|
||||||
fxEntries.putAll(entries);
|
|
||||||
}
|
|
||||||
// else don't change what we have
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -51,6 +51,11 @@ public class ExchangeApiImpl implements ExchangeApi {
|
||||||
this(HttpUrl.parse("https://api.kraken.com/0/public/Ticker"));
|
this(HttpUrl.parse("https://api.kraken.com/0/public/Ticker"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "kraken";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final String quoteCurrency,
|
public void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final String quoteCurrency,
|
||||||
@NonNull final ExchangeCallback callback) {
|
@NonNull final ExchangeCallback callback) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 m2049r@monerujo.io
|
* Copyright (c) 2019-2023 m2049r@monerujo.io
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
// https://developer.android.com/training/basics/network-ops/xml
|
// https://developer.android.com/training/basics/network-ops/xml
|
||||||
|
|
||||||
package com.m2049r.xmrwallet.service.exchange.krakenEcb;
|
package com.m2049r.xmrwallet.service.exchange.krakenFiat;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
@ -24,23 +24,39 @@ import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
||||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
||||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
import com.m2049r.xmrwallet.util.ServiceHelper;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Gets the XMR/EUR rate from kraken and then gets the EUR/fiat rate from the ECB
|
Gets the XMR/EUR rate from kraken and then gets the EUR/fiat rate from the yadio
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class ExchangeApiImpl implements ExchangeApi {
|
public class ExchangeApiImpl implements ExchangeApi {
|
||||||
static public final String BASE_FIAT = "EUR";
|
static public final String BASE_FIAT = "EUR";
|
||||||
|
|
||||||
|
final ExchangeApi krakenApi = new com.m2049r.xmrwallet.service.exchange.kraken.ExchangeApiImpl();
|
||||||
|
|
||||||
|
private ExchangeApi getFiatApi(String symbol) {
|
||||||
|
return ServiceHelper.getFiatApi(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return krakenApi.getName() + "+";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRealName(String fiatService) {
|
||||||
|
return getName() + fiatService;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final String quoteCurrency,
|
public void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final String quoteCurrency,
|
||||||
@NonNull final ExchangeCallback callback) {
|
@NonNull final ExchangeCallback callback) {
|
||||||
Timber.d("B=%s Q=%s", baseCurrency, quoteCurrency);
|
Timber.d("B=%s Q=%s", baseCurrency, quoteCurrency);
|
||||||
if (baseCurrency.equals(quoteCurrency)) {
|
if (baseCurrency.equals(quoteCurrency)) {
|
||||||
Timber.d("BASE=QUOTE=1");
|
Timber.d("BASE=QUOTE=1");
|
||||||
callback.onSuccess(new ExchangeRateImpl(baseCurrency, quoteCurrency, 1.0));
|
callback.onSuccess(new ExchangeRateImpl(getName(), baseCurrency, quoteCurrency, 1.0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,24 +68,21 @@ public class ExchangeApiImpl implements ExchangeApi {
|
||||||
|
|
||||||
final String quote = Helper.BASE_CRYPTO.equals(baseCurrency) ? quoteCurrency : baseCurrency;
|
final String quote = Helper.BASE_CRYPTO.equals(baseCurrency) ? quoteCurrency : baseCurrency;
|
||||||
|
|
||||||
final ExchangeApi krakenApi =
|
|
||||||
new com.m2049r.xmrwallet.service.exchange.kraken.ExchangeApiImpl();
|
|
||||||
krakenApi.queryExchangeRate(Helper.BASE_CRYPTO, BASE_FIAT, new ExchangeCallback() {
|
krakenApi.queryExchangeRate(Helper.BASE_CRYPTO, BASE_FIAT, new ExchangeCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final ExchangeRate krakenRate) {
|
public void onSuccess(final ExchangeRate krakenRate) {
|
||||||
Timber.d("kraken = %f", krakenRate.getRate());
|
Timber.d("kraken = %f", krakenRate.getRate());
|
||||||
final ExchangeApi ecbApi =
|
final ExchangeApi fiatApi = getFiatApi(quote);
|
||||||
new com.m2049r.xmrwallet.service.exchange.ecb.ExchangeApiImpl();
|
fiatApi.queryExchangeRate(BASE_FIAT, quote, new ExchangeCallback() {
|
||||||
ecbApi.queryExchangeRate(BASE_FIAT, quote, new ExchangeCallback() {
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final ExchangeRate ecbRate) {
|
public void onSuccess(final ExchangeRate fiatRate) {
|
||||||
Timber.d("ECB = %f", ecbRate.getRate());
|
Timber.d("FIAT = %f", fiatRate.getRate());
|
||||||
double rate = ecbRate.getRate() * krakenRate.getRate();
|
double rate = fiatRate.getRate() * krakenRate.getRate();
|
||||||
Timber.d("Q=%s QC=%s", quote, quoteCurrency);
|
Timber.d("Q=%s QC=%s", quote, quoteCurrency);
|
||||||
if (!quote.equals(quoteCurrency)) rate = 1.0d / rate;
|
if (!quote.equals(quoteCurrency)) rate = 1.0d / rate;
|
||||||
Timber.d("rate = %f", rate);
|
Timber.d("rate = %f", rate);
|
||||||
final ExchangeRate exchangeRate =
|
final ExchangeRate exchangeRate =
|
||||||
new ExchangeRateImpl(baseCurrency, quoteCurrency, rate);
|
new ExchangeRateImpl(getRealName(fiatApi.getName()), baseCurrency, quoteCurrency, rate);
|
||||||
callback.onSuccess(exchangeRate);
|
callback.onSuccess(exchangeRate);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 m2049r et al.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet.service.exchange.krakenFiat;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
class ExchangeRateImpl implements ExchangeRate {
|
||||||
|
@Getter
|
||||||
|
private final String serviceName;
|
||||||
|
@Getter
|
||||||
|
private final String baseCurrency;
|
||||||
|
@Getter
|
||||||
|
private final String quoteCurrency;
|
||||||
|
@Getter
|
||||||
|
private final double rate;
|
||||||
|
|
||||||
|
ExchangeRateImpl(@NonNull final String serviceName, @NonNull final String baseCurrency, @NonNull final String quoteCurrency, double rate) {
|
||||||
|
super();
|
||||||
|
this.serviceName = serviceName;
|
||||||
|
this.baseCurrency = baseCurrency;
|
||||||
|
this.quoteCurrency = quoteCurrency;
|
||||||
|
this.rate = rate;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 m2049r@monerujo.io
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// https://developer.android.com/training/basics/network-ops/xml
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet.service.exchange.yadio;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeException;
|
||||||
|
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import okhttp3.Call;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
import okhttp3.Response;
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class ExchangeApiImpl implements ExchangeApi {
|
||||||
|
@NonNull
|
||||||
|
private final HttpUrl baseUrl;
|
||||||
|
|
||||||
|
//so we can inject the mockserver url
|
||||||
|
@VisibleForTesting
|
||||||
|
public ExchangeApiImpl(@NonNull final HttpUrl baseUrl) {
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExchangeApiImpl() {
|
||||||
|
this(HttpUrl.parse("https://api.yadio.io/convert/1/eur"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "yadio";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final String quoteCurrency,
|
||||||
|
@NonNull final ExchangeCallback callback) {
|
||||||
|
if (!baseCurrency.equals("EUR")) {
|
||||||
|
callback.onError(new IllegalArgumentException("Only EUR supported as base"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseCurrency.equals(quoteCurrency)) {
|
||||||
|
callback.onSuccess(new ExchangeRateImpl(quoteCurrency, 1.0, 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final HttpUrl url = baseUrl.newBuilder()
|
||||||
|
.addPathSegments(quoteCurrency.substring(0, 3))
|
||||||
|
.build();
|
||||||
|
final NetCipherHelper.Request httpRequest = new NetCipherHelper.Request(url);
|
||||||
|
|
||||||
|
httpRequest.enqueue(new okhttp3.Callback() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull final Call call, @NonNull final IOException ex) {
|
||||||
|
callback.onError(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull final Call call, @NonNull final Response response) throws IOException {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
try {
|
||||||
|
assert response.body() != null;
|
||||||
|
final JSONObject json = new JSONObject(response.body().string());
|
||||||
|
if (json.has("error")) {
|
||||||
|
Timber.d("%d: %s", response.code(), json.getString("error"));
|
||||||
|
callback.onError(new ExchangeException(response.code(), json.getString("error")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double rate = json.getDouble("rate");
|
||||||
|
long timestamp = json.getLong("timestamp");
|
||||||
|
callback.onSuccess(new ExchangeRateImpl(quoteCurrency, rate, timestamp));
|
||||||
|
} catch (JSONException ex) {
|
||||||
|
callback.onError(ex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback.onError(new ExchangeException(response.code(), response.message()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,41 +14,34 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.m2049r.xmrwallet.service.exchange.krakenEcb;
|
package com.m2049r.xmrwallet.service.exchange.yadio;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
class ExchangeRateImpl implements ExchangeRate {
|
class ExchangeRateImpl implements ExchangeRate {
|
||||||
private final String baseCurrency;
|
@Getter
|
||||||
|
private final String baseCurrency = "EUR";
|
||||||
|
@Getter
|
||||||
private final String quoteCurrency;
|
private final String quoteCurrency;
|
||||||
|
@Getter
|
||||||
private final double rate;
|
private final double rate;
|
||||||
|
private final Date date;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getServiceName() {
|
public String getServiceName() {
|
||||||
return "kraken+ecb";
|
return "yadio.io";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
ExchangeRateImpl(@NonNull final String quoteCurrency, double rate, final long timestamp) {
|
||||||
public String getBaseCurrency() {
|
|
||||||
return baseCurrency;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getQuoteCurrency() {
|
|
||||||
return quoteCurrency;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public double getRate() {
|
|
||||||
return rate;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExchangeRateImpl(@NonNull final String baseCurrency, @NonNull final String quoteCurrency, double rate) {
|
|
||||||
super();
|
super();
|
||||||
this.baseCurrency = baseCurrency;
|
|
||||||
this.quoteCurrency = quoteCurrency;
|
this.quoteCurrency = quoteCurrency;
|
||||||
this.rate = rate;
|
this.rate = rate;
|
||||||
|
this.date = timestamp > 0 ? new Date(timestamp) : new Date();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,16 @@
|
||||||
package com.m2049r.xmrwallet.util;
|
package com.m2049r.xmrwallet.util;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.R;
|
||||||
import com.m2049r.xmrwallet.model.NetworkType;
|
import com.m2049r.xmrwallet.model.NetworkType;
|
||||||
import com.m2049r.xmrwallet.model.WalletManager;
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NonNull;
|
||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
|
|
||||||
public class ServiceHelper {
|
public class ServiceHelper {
|
||||||
|
@ -19,6 +26,14 @@ public class ServiceHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
static public ExchangeApi getExchangeApi() {
|
static public ExchangeApi getExchangeApi() {
|
||||||
return new com.m2049r.xmrwallet.service.exchange.krakenEcb.ExchangeApiImpl();
|
return new com.m2049r.xmrwallet.service.exchange.krakenFiat.ExchangeApiImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
static private final ExchangeApi[] fiatApis = new ExchangeApi[]{
|
||||||
|
new com.m2049r.xmrwallet.service.exchange.ecb.ExchangeApiImpl(),
|
||||||
|
new com.m2049r.xmrwallet.service.exchange.yadio.ExchangeApiImpl()};
|
||||||
|
|
||||||
|
static public ExchangeApi getFiatApi(String symbol) {
|
||||||
|
return (symbol.length() == 3) ? fiatApis[0] : fiatApis[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -433,7 +433,7 @@
|
||||||
|
|
||||||
<string name="tx_locked">Monto trabado hasta el bloque %1$d (faltan %2$d bloques ≈ %3$,.2f días)</string>
|
<string name="tx_locked">Monto trabado hasta el bloque %1$d (faltan %2$d bloques ≈ %3$,.2f días)</string>
|
||||||
|
|
||||||
<string name="label_streetmode">Modo calle activado\nSólo se ºmostrarán transacciones nuevas</string>
|
<string name="label_streetmode">Modo calle activado\nSólo se mostrarán transacciones nuevas</string>
|
||||||
|
|
||||||
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain at least 6 coins of the selected amount.</string>
|
<string name="pocketchange_info">To reduce waiting time on repeated spending, Monerujo can create spare change at the expense of higher fees. It\'ll try to create and maintain at least 6 coins of the selected amount.</string>
|
||||||
<string name="pocketchange_create_title">Create Change</string>
|
<string name="pocketchange_create_title">Create Change</string>
|
||||||
|
|
|
@ -10,4 +10,46 @@
|
||||||
<item>Oled</item>
|
<item>Oled</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="currency" translatable="false">
|
||||||
|
<item>EUR</item>
|
||||||
|
<item>USD</item>
|
||||||
|
<item>JPY</item>
|
||||||
|
<item>GBP</item>
|
||||||
|
<item>CHF</item>
|
||||||
|
<item>CAD</item>
|
||||||
|
|
||||||
|
<item>AUD</item>
|
||||||
|
<item>BGN</item>
|
||||||
|
<item>BRL</item>
|
||||||
|
<item>CNY</item>
|
||||||
|
<item>CZK</item>
|
||||||
|
<item>DKK</item>
|
||||||
|
<item>HKD</item>
|
||||||
|
<item>HUF</item>
|
||||||
|
<item>IDR</item>
|
||||||
|
<item>ILS</item>
|
||||||
|
<item>INR</item>
|
||||||
|
<item>ISK</item>
|
||||||
|
<item>KRW</item>
|
||||||
|
<item>MXN</item>
|
||||||
|
<item>MYR</item>
|
||||||
|
<item>NOK</item>
|
||||||
|
<item>NZD</item>
|
||||||
|
<item>PHP</item>
|
||||||
|
<item>PLN</item>
|
||||||
|
<item>RON</item>
|
||||||
|
<item>SEK</item>
|
||||||
|
<item>SGD</item>
|
||||||
|
<item>THB</item>
|
||||||
|
<item>TRY</item>
|
||||||
|
<item>ZAR</item>
|
||||||
|
<!-- shitcoins are marked with a zero width space at the end (U+200B) -->
|
||||||
|
<!-- this is a hack to avoid having different arrays, i.e. shitcoins have 4 charatcers -->
|
||||||
|
<item>ARS</item>
|
||||||
|
<item>IRR</item>
|
||||||
|
<item>LBP</item>
|
||||||
|
<item>RUB</item>
|
||||||
|
<item>VES</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
|
@ -282,42 +282,6 @@
|
||||||
<string name="delete_alert_yes">Yes, do that!</string>
|
<string name="delete_alert_yes">Yes, do that!</string>
|
||||||
<string name="delete_alert_no">No thanks!</string>
|
<string name="delete_alert_no">No thanks!</string>
|
||||||
|
|
||||||
<string-array name="currency" translatable="false">
|
|
||||||
<item>EUR</item>
|
|
||||||
<item>USD</item>
|
|
||||||
<item>JPY</item>
|
|
||||||
<item>GBP</item>
|
|
||||||
<item>CHF</item>
|
|
||||||
<item>CAD</item>
|
|
||||||
|
|
||||||
<item>AUD</item>
|
|
||||||
<item>BGN</item>
|
|
||||||
<item>BRL</item>
|
|
||||||
<item>CNY</item>
|
|
||||||
<item>CZK</item>
|
|
||||||
<item>DKK</item>
|
|
||||||
<item>HKD</item>
|
|
||||||
<item>HRK</item>
|
|
||||||
<item>HUF</item>
|
|
||||||
<item>IDR</item>
|
|
||||||
<item>ILS</item>
|
|
||||||
<item>INR</item>
|
|
||||||
<item>ISK</item>
|
|
||||||
<item>KRW</item>
|
|
||||||
<item>MXN</item>
|
|
||||||
<item>MYR</item>
|
|
||||||
<item>NOK</item>
|
|
||||||
<item>NZD</item>
|
|
||||||
<item>PHP</item>
|
|
||||||
<item>PLN</item>
|
|
||||||
<item>RON</item>
|
|
||||||
<item>SEK</item>
|
|
||||||
<item>SGD</item>
|
|
||||||
<item>THB</item>
|
|
||||||
<item>TRY</item>
|
|
||||||
<item>ZAR</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
<string name="fab_create_new">Create new wallet</string>
|
<string name="fab_create_new">Create new wallet</string>
|
||||||
<string name="fab_restore_viewonly">Restore view-only wallet</string>
|
<string name="fab_restore_viewonly">Restore view-only wallet</string>
|
||||||
<string name="fab_restore_key">Restore wallet from private keys</string>
|
<string name="fab_restore_key">Restore wallet from private keys</string>
|
||||||
|
|
|
@ -0,0 +1,263 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017-2023 m2049r et al.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet.service.exchange.yadio;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeException;
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||||
|
import com.m2049r.xmrwallet.util.NetCipherHelper;
|
||||||
|
|
||||||
|
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.Date;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.mockwebserver.MockResponse;
|
||||||
|
import okhttp3.mockwebserver.MockWebServer;
|
||||||
|
import okhttp3.mockwebserver.RecordedRequest;
|
||||||
|
|
||||||
|
|
||||||
|
public class ExchangeRateTest {
|
||||||
|
|
||||||
|
private MockWebServer mockWebServer;
|
||||||
|
|
||||||
|
private ExchangeApi exchangeApi;
|
||||||
|
|
||||||
|
private Waiter waiter;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
ExchangeCallback mockExchangeCallback;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
mockWebServer = new MockWebServer();
|
||||||
|
mockWebServer.start();
|
||||||
|
|
||||||
|
waiter = new Waiter();
|
||||||
|
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
NetCipherHelper.Request.mockClient = new OkHttpClient();
|
||||||
|
exchangeApi = new ExchangeApiImpl(mockWebServer.url("/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
mockWebServer.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryExchangeRate_shouldBeGetMethod()
|
||||||
|
throws InterruptedException, TimeoutException {
|
||||||
|
|
||||||
|
exchangeApi.queryExchangeRate("EUR", "USD", mockExchangeCallback);
|
||||||
|
|
||||||
|
RecordedRequest request = mockWebServer.takeRequest();
|
||||||
|
assertEquals("GET", request.getMethod());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryExchangeRate_shouldBeEUR()
|
||||||
|
throws InterruptedException, TimeoutException {
|
||||||
|
|
||||||
|
exchangeApi.queryExchangeRate("CHF", "USD", new ExchangeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final ExchangeRate exchangeRate) {
|
||||||
|
waiter.fail();
|
||||||
|
waiter.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(final Exception e) {
|
||||||
|
waiter.assertTrue(e instanceof IllegalArgumentException);
|
||||||
|
waiter.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
waiter.await();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryExchangeRate_shouldBeOneForEur()
|
||||||
|
throws InterruptedException, TimeoutException {
|
||||||
|
|
||||||
|
exchangeApi.queryExchangeRate("EUR", "EUR", new ExchangeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final ExchangeRate exchangeRate) {
|
||||||
|
waiter.assertEquals(1.0, exchangeRate.getRate());
|
||||||
|
waiter.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(final Exception e) {
|
||||||
|
waiter.fail();
|
||||||
|
waiter.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
waiter.await();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryExchangeRate_wasSuccessfulShouldRespondWithUsdRate()
|
||||||
|
throws InterruptedException, JSONException, TimeoutException {
|
||||||
|
final String base = "EUR";
|
||||||
|
final String quote = "USD";
|
||||||
|
final double rate = 1.1043;
|
||||||
|
|
||||||
|
MockResponse jsonMockResponse = new MockResponse().setBody(createMockExchangeRateResponse(quote, rate));
|
||||||
|
mockWebServer.enqueue(jsonMockResponse);
|
||||||
|
|
||||||
|
exchangeApi.queryExchangeRate(base, quote, new ExchangeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final ExchangeRate exchangeRate) {
|
||||||
|
waiter.assertEquals(base, exchangeRate.getBaseCurrency());
|
||||||
|
waiter.assertEquals(quote, exchangeRate.getQuoteCurrency());
|
||||||
|
waiter.assertEquals(rate, exchangeRate.getRate());
|
||||||
|
waiter.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(final Exception e) {
|
||||||
|
waiter.fail(e);
|
||||||
|
waiter.resume();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
waiter.await();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryExchangeRate_wasSuccessfulShouldRespondWithAudRate()
|
||||||
|
throws InterruptedException, JSONException, TimeoutException {
|
||||||
|
final String base = "EUR";
|
||||||
|
final String quote = "AUD";
|
||||||
|
final double rate = 99.114651;
|
||||||
|
|
||||||
|
MockResponse jsonMockResponse = new MockResponse().setBody(createMockExchangeRateResponse(quote, rate));
|
||||||
|
mockWebServer.enqueue(jsonMockResponse);
|
||||||
|
|
||||||
|
exchangeApi.queryExchangeRate(base, quote, new ExchangeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final ExchangeRate exchangeRate) {
|
||||||
|
waiter.assertEquals(base, exchangeRate.getBaseCurrency());
|
||||||
|
waiter.assertEquals(quote, exchangeRate.getQuoteCurrency());
|
||||||
|
waiter.assertEquals(rate, exchangeRate.getRate());
|
||||||
|
waiter.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(final Exception e) {
|
||||||
|
waiter.fail(e);
|
||||||
|
waiter.resume();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
waiter.await();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryExchangeRate_wasSuccessfulShouldRespondWithZarRate()
|
||||||
|
throws InterruptedException, JSONException, TimeoutException {
|
||||||
|
final String base = "EUR";
|
||||||
|
final String quote = "ZAR";
|
||||||
|
final double rate = 99.114651;
|
||||||
|
|
||||||
|
MockResponse jsonMockResponse = new MockResponse().setBody(createMockExchangeRateResponse(quote, rate));
|
||||||
|
mockWebServer.enqueue(jsonMockResponse);
|
||||||
|
|
||||||
|
exchangeApi.queryExchangeRate(base, quote, new ExchangeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final ExchangeRate exchangeRate) {
|
||||||
|
waiter.assertEquals(base, exchangeRate.getBaseCurrency());
|
||||||
|
waiter.assertEquals(quote, exchangeRate.getQuoteCurrency());
|
||||||
|
waiter.assertEquals(rate, exchangeRate.getRate());
|
||||||
|
waiter.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(final Exception e) {
|
||||||
|
waiter.fail(e);
|
||||||
|
waiter.resume();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
waiter.await();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryExchangeRate_wasNotSuccessfulShouldCallOnError()
|
||||||
|
throws InterruptedException, JSONException, TimeoutException {
|
||||||
|
mockWebServer.enqueue(new MockResponse().setResponseCode(500));
|
||||||
|
exchangeApi.queryExchangeRate("EUR", "USD", new ExchangeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final ExchangeRate exchangeRate) {
|
||||||
|
waiter.fail();
|
||||||
|
waiter.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(final Exception e) {
|
||||||
|
waiter.assertTrue(e instanceof ExchangeException);
|
||||||
|
waiter.assertTrue(((ExchangeException) e).getCode() == 500);
|
||||||
|
waiter.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
waiter.await();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryExchangeRate_unknownAssetShouldCallOnError()
|
||||||
|
throws InterruptedException, JSONException, TimeoutException {
|
||||||
|
MockResponse jsonMockResponse = new MockResponse().setBody(createMockExchangeRateResponse("X", 0));
|
||||||
|
mockWebServer.enqueue(jsonMockResponse);
|
||||||
|
exchangeApi.queryExchangeRate("EUR", "ABC", new ExchangeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final ExchangeRate exchangeRate) {
|
||||||
|
waiter.fail();
|
||||||
|
waiter.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(final Exception e) {
|
||||||
|
waiter.assertTrue(e instanceof ExchangeException);
|
||||||
|
ExchangeException ex = (ExchangeException) e;
|
||||||
|
waiter.assertEquals(ex.getCode(), 200);
|
||||||
|
waiter.assertEquals(ex.getErrorMsg(), "currency not found");
|
||||||
|
waiter.resume();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
waiter.await();
|
||||||
|
}
|
||||||
|
|
||||||
|
static public String createMockExchangeRateResponse(String quote, double rate) {
|
||||||
|
if (!quote.equals("X")) {
|
||||||
|
return "{\"request\":{\"amount\":1,\"from\":\"EUR\",\"to\":\"" + quote + "\"},\"result\":" + rate + ",\"rate\":" + rate + ",\"timestamp\":" + new Date().getTime() + "}";
|
||||||
|
}
|
||||||
|
return "{\"error\":\"currency not found\"}";
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ buildscript {
|
||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.1.0'
|
classpath 'com.android.tools.build:gradle:8.1.2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue