Skip to content

Commit a6e9d0e

Browse files
authored
use yadio for pricing "exotic" fiat (#921)
* yadio api * add more currencies * fix es typo * bump version & fix cicleci build
1 parent 17df7c3 commit a6e9d0e

File tree

16 files changed

+537
-89
lines changed

16 files changed

+537
-89
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ jobs:
33
build:
44
working_directory: ~/code
55
docker:
6-
- image: cimg/android:2022.03-ndk
6+
- image: cimg/android:2023.12-ndk
77
environment:
88
JVM_OPTS: -Xmx3200m
99
steps:

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ android {
88
compileSdk 33
99
minSdkVersion 21
1010
targetSdkVersion 33
11-
versionCode 3308
12-
versionName "3.3.8 'Pocket Change'"
11+
versionCode 3310
12+
versionName "3.3.10 'Argentina'"
1313
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1414
externalNativeBuild {
1515
cmake {

app/src/main/java/com/m2049r/xmrwallet/XmrWalletApplication.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,24 @@
2222
import android.os.Build;
2323

2424
import androidx.annotation.NonNull;
25+
import androidx.annotation.OptIn;
2526
import androidx.fragment.app.FragmentManager;
27+
import androidx.fragment.app.FragmentStateManagerControl;
2628

2729
import com.m2049r.xmrwallet.model.NetworkType;
2830
import com.m2049r.xmrwallet.util.LocaleHelper;
2931
import com.m2049r.xmrwallet.util.NetCipherHelper;
3032
import com.m2049r.xmrwallet.util.NightmodeHelper;
33+
import com.m2049r.xmrwallet.util.ServiceHelper;
34+
35+
import java.util.Arrays;
3136

3237
import timber.log.Timber;
3338

3439
public class XmrWalletApplication extends Application {
3540

3641
@Override
42+
@OptIn(markerClass = FragmentStateManagerControl.class)
3743
public void onCreate() {
3844
super.onCreate();
3945
FragmentManager.enableNewStateManager(false);

app/src/main/java/com/m2049r/xmrwallet/service/exchange/api/ExchangeApi.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@ public interface ExchangeApi {
3333
void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final String quoteCurrency,
3434
@NonNull final ExchangeCallback callback);
3535

36+
String getName();
3637
}
3738

app/src/main/java/com/m2049r/xmrwallet/service/exchange/ecb/ExchangeApiImpl.java

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ public ExchangeApiImpl() {
6767
// data is daily and is refreshed around 16:00 CET every working day
6868
}
6969

70-
public static boolean isSameDay(Calendar calendar, Calendar anotherCalendar) {
71-
return (calendar.get(Calendar.YEAR) == anotherCalendar.get(Calendar.YEAR)) &&
72-
(calendar.get(Calendar.DAY_OF_YEAR) == anotherCalendar.get(Calendar.DAY_OF_YEAR));
70+
@Override
71+
public String getName() {
72+
return "ecb";
7373
}
7474

7575
@Override
@@ -121,12 +121,12 @@ public void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final
121121
final NetCipherHelper.Request httpRequest = new NetCipherHelper.Request(baseUrl);
122122
httpRequest.enqueue(new okhttp3.Callback() {
123123
@Override
124-
public void onFailure(final Call call, final IOException ex) {
124+
public void onFailure(@NonNull final Call call, @NonNull final IOException ex) {
125125
callback.onError(ex);
126126
}
127127

128128
@Override
129-
public void onResponse(final Call call, final Response response) throws IOException {
129+
public void onResponse(@NonNull final Call call, @NonNull final Response response) throws IOException {
130130
if (response.isSuccessful()) {
131131
try {
132132
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
@@ -178,11 +178,12 @@ private void parse(final Document xmlRootDoc) {
178178
Element cube = (Element) node;
179179
if (cube.hasAttribute("time")) { // a time Cube
180180
final Date time = DATE_FORMAT.parse(cube.getAttribute("time"));
181+
assert time != null;
181182
date.setTime(time);
182183
} else if (cube.hasAttribute("currency")
183184
&& cube.hasAttribute("rate")) { // a rate Cube
184185
String currency = cube.getAttribute("currency");
185-
double rate = Double.valueOf(cube.getAttribute("rate"));
186+
double rate = Double.parseDouble(cube.getAttribute("rate"));
186187
entries.put(currency, rate);
187188
} // else an empty Cube - ignore
188189
}
@@ -191,13 +192,10 @@ private void parse(final Document xmlRootDoc) {
191192
Timber.d(ex);
192193
}
193194
synchronized (this) {
194-
if (date != null) {
195-
fetchDate = Calendar.getInstance(TimeZone.getTimeZone("CET"));
196-
fxDate = date;
197-
fxEntries.clear();
198-
fxEntries.putAll(entries);
199-
}
200-
// else don't change what we have
195+
fetchDate = Calendar.getInstance(TimeZone.getTimeZone("CET"));
196+
fxDate = date;
197+
fxEntries.clear();
198+
fxEntries.putAll(entries);
201199
}
202200
}
203201
}

app/src/main/java/com/m2049r/xmrwallet/service/exchange/kraken/ExchangeApiImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ public ExchangeApiImpl() {
5151
this(HttpUrl.parse("https://api.kraken.com/0/public/Ticker"));
5252
}
5353

54+
@Override
55+
public String getName() {
56+
return "kraken";
57+
}
58+
5459
@Override
5560
public void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final String quoteCurrency,
5661
@NonNull final ExchangeCallback callback) {

app/src/main/java/com/m2049r/xmrwallet/service/exchange/krakenEcb/ExchangeApiImpl.java renamed to app/src/main/java/com/m2049r/xmrwallet/service/exchange/krakenFiat/ExchangeApiImpl.java

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019 [email protected]
2+
* Copyright (c) 2019-2023 [email protected]
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,31 +16,47 @@
1616

1717
// https://developer.android.com/training/basics/network-ops/xml
1818

19-
package com.m2049r.xmrwallet.service.exchange.krakenEcb;
19+
package com.m2049r.xmrwallet.service.exchange.krakenFiat;
2020

2121
import androidx.annotation.NonNull;
2222

2323
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
2424
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
2525
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
2626
import com.m2049r.xmrwallet.util.Helper;
27+
import com.m2049r.xmrwallet.util.ServiceHelper;
2728

2829
import timber.log.Timber;
2930

3031
/*
31-
Gets the XMR/EUR rate from kraken and then gets the EUR/fiat rate from the ECB
32+
Gets the XMR/EUR rate from kraken and then gets the EUR/fiat rate from the yadio
3233
*/
3334

3435
public class ExchangeApiImpl implements ExchangeApi {
3536
static public final String BASE_FIAT = "EUR";
3637

38+
final ExchangeApi krakenApi = new com.m2049r.xmrwallet.service.exchange.kraken.ExchangeApiImpl();
39+
40+
private ExchangeApi getFiatApi(String symbol) {
41+
return ServiceHelper.getFiatApi(symbol);
42+
}
43+
44+
@Override
45+
public String getName() {
46+
return krakenApi.getName() + "+";
47+
}
48+
49+
public String getRealName(String fiatService) {
50+
return getName() + fiatService;
51+
}
52+
3753
@Override
3854
public void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final String quoteCurrency,
3955
@NonNull final ExchangeCallback callback) {
4056
Timber.d("B=%s Q=%s", baseCurrency, quoteCurrency);
4157
if (baseCurrency.equals(quoteCurrency)) {
4258
Timber.d("BASE=QUOTE=1");
43-
callback.onSuccess(new ExchangeRateImpl(baseCurrency, quoteCurrency, 1.0));
59+
callback.onSuccess(new ExchangeRateImpl(getName(), baseCurrency, quoteCurrency, 1.0));
4460
return;
4561
}
4662

@@ -52,24 +68,21 @@ public void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final
5268

5369
final String quote = Helper.BASE_CRYPTO.equals(baseCurrency) ? quoteCurrency : baseCurrency;
5470

55-
final ExchangeApi krakenApi =
56-
new com.m2049r.xmrwallet.service.exchange.kraken.ExchangeApiImpl();
5771
krakenApi.queryExchangeRate(Helper.BASE_CRYPTO, BASE_FIAT, new ExchangeCallback() {
5872
@Override
5973
public void onSuccess(final ExchangeRate krakenRate) {
6074
Timber.d("kraken = %f", krakenRate.getRate());
61-
final ExchangeApi ecbApi =
62-
new com.m2049r.xmrwallet.service.exchange.ecb.ExchangeApiImpl();
63-
ecbApi.queryExchangeRate(BASE_FIAT, quote, new ExchangeCallback() {
75+
final ExchangeApi fiatApi = getFiatApi(quote);
76+
fiatApi.queryExchangeRate(BASE_FIAT, quote, new ExchangeCallback() {
6477
@Override
65-
public void onSuccess(final ExchangeRate ecbRate) {
66-
Timber.d("ECB = %f", ecbRate.getRate());
67-
double rate = ecbRate.getRate() * krakenRate.getRate();
78+
public void onSuccess(final ExchangeRate fiatRate) {
79+
Timber.d("FIAT = %f", fiatRate.getRate());
80+
double rate = fiatRate.getRate() * krakenRate.getRate();
6881
Timber.d("Q=%s QC=%s", quote, quoteCurrency);
6982
if (!quote.equals(quoteCurrency)) rate = 1.0d / rate;
7083
Timber.d("rate = %f", rate);
7184
final ExchangeRate exchangeRate =
72-
new ExchangeRateImpl(baseCurrency, quoteCurrency, rate);
85+
new ExchangeRateImpl(getRealName(fiatApi.getName()), baseCurrency, quoteCurrency, rate);
7386
callback.onSuccess(exchangeRate);
7487
}
7588

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) 2019-2023 m2049r et al.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.m2049r.xmrwallet.service.exchange.krakenFiat;
18+
19+
import androidx.annotation.NonNull;
20+
21+
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
22+
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
23+
24+
import lombok.Getter;
25+
26+
class ExchangeRateImpl implements ExchangeRate {
27+
@Getter
28+
private final String serviceName;
29+
@Getter
30+
private final String baseCurrency;
31+
@Getter
32+
private final String quoteCurrency;
33+
@Getter
34+
private final double rate;
35+
36+
ExchangeRateImpl(@NonNull final String serviceName, @NonNull final String baseCurrency, @NonNull final String quoteCurrency, double rate) {
37+
super();
38+
this.serviceName = serviceName;
39+
this.baseCurrency = baseCurrency;
40+
this.quoteCurrency = quoteCurrency;
41+
this.rate = rate;
42+
}
43+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright (c) 2019 [email protected]
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
// https://developer.android.com/training/basics/network-ops/xml
18+
19+
package com.m2049r.xmrwallet.service.exchange.yadio;
20+
21+
import androidx.annotation.NonNull;
22+
import androidx.annotation.VisibleForTesting;
23+
24+
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
25+
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
26+
import com.m2049r.xmrwallet.service.exchange.api.ExchangeException;
27+
import com.m2049r.xmrwallet.util.NetCipherHelper;
28+
29+
import org.json.JSONException;
30+
import org.json.JSONObject;
31+
32+
import java.io.IOException;
33+
34+
import okhttp3.Call;
35+
import okhttp3.HttpUrl;
36+
import okhttp3.Response;
37+
import timber.log.Timber;
38+
39+
public class ExchangeApiImpl implements ExchangeApi {
40+
@NonNull
41+
private final HttpUrl baseUrl;
42+
43+
//so we can inject the mockserver url
44+
@VisibleForTesting
45+
public ExchangeApiImpl(@NonNull final HttpUrl baseUrl) {
46+
this.baseUrl = baseUrl;
47+
}
48+
49+
public ExchangeApiImpl() {
50+
this(HttpUrl.parse("https://api.yadio.io/convert/1/eur"));
51+
}
52+
53+
@Override
54+
public String getName() {
55+
return "yadio";
56+
}
57+
58+
@Override
59+
public void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final String quoteCurrency,
60+
@NonNull final ExchangeCallback callback) {
61+
if (!baseCurrency.equals("EUR")) {
62+
callback.onError(new IllegalArgumentException("Only EUR supported as base"));
63+
return;
64+
}
65+
66+
if (baseCurrency.equals(quoteCurrency)) {
67+
callback.onSuccess(new ExchangeRateImpl(quoteCurrency, 1.0, 0));
68+
return;
69+
}
70+
71+
final HttpUrl url = baseUrl.newBuilder()
72+
.addPathSegments(quoteCurrency.substring(0, 3))
73+
.build();
74+
final NetCipherHelper.Request httpRequest = new NetCipherHelper.Request(url);
75+
76+
httpRequest.enqueue(new okhttp3.Callback() {
77+
@Override
78+
public void onFailure(@NonNull final Call call, @NonNull final IOException ex) {
79+
callback.onError(ex);
80+
}
81+
82+
@Override
83+
public void onResponse(@NonNull final Call call, @NonNull final Response response) throws IOException {
84+
if (response.isSuccessful()) {
85+
try {
86+
assert response.body() != null;
87+
final JSONObject json = new JSONObject(response.body().string());
88+
if (json.has("error")) {
89+
Timber.d("%d: %s", response.code(), json.getString("error"));
90+
callback.onError(new ExchangeException(response.code(), json.getString("error")));
91+
return;
92+
}
93+
double rate = json.getDouble("rate");
94+
long timestamp = json.getLong("timestamp");
95+
callback.onSuccess(new ExchangeRateImpl(quoteCurrency, rate, timestamp));
96+
} catch (JSONException ex) {
97+
callback.onError(ex);
98+
}
99+
} else {
100+
callback.onError(new ExchangeException(response.code(), response.message()));
101+
}
102+
}
103+
});
104+
}
105+
}

0 commit comments

Comments
 (0)