1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-05 09:58:42 +02:00

Compare commits

...

32 Commits

Author SHA1 Message Date
m2049r
97fb0a5483 bump version 1.14.6 2020-09-21 22:11:35 +02:00
m2049r
fc950c6772 fix onboarding layout (#683) 2020-09-21 22:10:51 +02:00
m2049r
46add5e927 bump version v1.14.5 2020-09-21 14:37:51 +02:00
m2049r
fa0692ceab force agree for fingerprint send (#682) 2020-09-21 14:36:17 +02:00
m2049r
ff4f4a1c2c remove notices covered by onboarding (#681) 2020-09-21 14:35:50 +02:00
m2049r
79abb89725 disable button on finish onbaording (#680) 2020-09-21 14:35:25 +02:00
m2049r
ef8301fd6f bump version v1.14.4 2020-09-18 22:09:32 +02:00
hiddener
3a15c842ff updated Romanian translation 2020-09-18 22:01:41 +02:00
m2049r
1697da55b5 only update if we still have a wallet 2020-09-18 22:00:14 +02:00
m2049r
454f3e412a don't do stuff if the wallet is already closed 2020-09-18 22:00:14 +02:00
m2049r
d803a1e220 remove unused resources (#678) 2020-09-18 21:59:38 +02:00
m2049r
f2fe781cb5 update restore heights 2020-09-17 10:18:58 +02:00
m2049r
dcf60ae193 bump version v1.14.3 2020-09-17 09:35:28 +02:00
m2049r
ffdf54c2e1 Merge pull request #677 from m2049r/fix_various
Various fixes & tweaks
2020-09-17 09:34:04 +02:00
m2049r
c060a2ab88 don't show onboarding every time 2020-09-16 19:59:58 +02:00
m2049r
05fc654f3a clean up code 2020-09-16 18:56:27 +02:00
m2049r
c32d157150 monero addresses are 95 characters 2020-09-16 17:51:47 +02:00
m2049r
74e9278baa MainActivity must be singleTop 2020-09-16 17:45:07 +02:00
m2049r
e41e344d63 bump version v1.14.2 2020-09-14 18:16:35 +02:00
m2049r
e66875437d always show onboarding 2020-09-14 18:14:38 +02:00
m2049r
c1d2db3d7d onboarding tweaks & version bump (#676) 2020-09-14 00:24:57 +02:00
m2049r
0c9a2f5e01 fixups 2020-09-13 18:52:26 +02:00
m2049r
64616e3921 onboarding (#675)
Co-authored-by: Stephan <stephan.hagios@gmail.com>
2020-09-13 18:34:22 +02:00
m2049r
82b4d66987 allow spend with fingerprint only (#674) 2020-09-13 18:26:36 +02:00
m2049r
10f2bc6561 upgrade monero core (#673) 2020-09-05 11:01:34 +02:00
m2049r
2ed9a78d9e upgrade gradle 2020-07-16 14:14:26 +02:00
m2049r
ca19f32f8f bump version 2020-07-06 15:00:10 +02:00
m2049r
4431d74051 Fix for Ledger protocol v3 (#670)
* update protocol to V3 for Monero App v1.6.0
2020-07-06 14:59:38 +02:00
TheFuzzStone
f00da6ecda Renamed folder to '*-uk' and files to *-uk. (#666)
* Renamed folder to '*-uk' and files to `*-uk`. 
Changed '&apos;' to '\''

* Renamed file names
2020-06-20 18:07:43 +02:00
m2049r
1cecd0b718 xmr.to v3 API (#667) 2020-06-19 19:17:38 +02:00
m2049r
a0d6117bbb don't keepTimestampsInApk 2020-06-06 09:46:28 +02:00
m2049r
0b0648a172 upgrade dev env 2020-06-05 08:56:02 +02:00
72 changed files with 1974 additions and 887 deletions

1
.gitignore vendored
View File

@@ -14,3 +14,4 @@
/app/prodMainnet /app/prodMainnet
/app/alphaStagenet /app/alphaStagenet
/app/prodStagenet /app/prodStagenet
/app/.cxx

View File

@@ -2,14 +2,13 @@ apply plugin: 'com.android.application'
android { android {
compileSdkVersion 28 compileSdkVersion 28
buildToolsVersion '28.0.3' buildToolsVersion '29.0.2'
defaultConfig { defaultConfig {
applicationId "com.m2049r.xmrwallet" applicationId "com.m2049r.xmrwallet"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 28 targetSdkVersion 28
versionCode 301 versionCode 406
versionName "1.13.1 'ReStart'" versionName "1.14.6 'On Board'"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild { externalNativeBuild {
cmake { cmake {
@@ -93,6 +92,11 @@ android {
outputFileName = "$rootProject.ext.apkName-" + v + "_" + abiName + ".apk" outputFileName = "$rootProject.ext.apkName-" + v + "_" + abiName + ".apk"
} }
} }
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
} }
dependencies { dependencies {
@@ -102,6 +106,7 @@ dependencies {
implementation "com.android.support:recyclerview-v7:$rootProject.ext.supportVersion" implementation "com.android.support:recyclerview-v7:$rootProject.ext.supportVersion"
implementation "com.android.support:cardview-v7:$rootProject.ext.supportVersion" implementation "com.android.support:cardview-v7:$rootProject.ext.supportVersion"
implementation "com.android.support:swiperefreshlayout:$rootProject.ext.supportVersion" implementation "com.android.support:swiperefreshlayout:$rootProject.ext.supportVersion"
implementation "com.android.support.constraint:constraint-layout:$rootProject.ext.constraintVersion"
implementation 'me.dm7.barcodescanner:zxing:1.9.8' implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion" implementation "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
@@ -124,5 +129,4 @@ dependencies {
testImplementation "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion" testImplementation "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
testImplementation 'org.json:json:20180813' testImplementation 'org.json:json:20180813'
testImplementation 'net.jodah:concurrentunit:0.4.4' testImplementation 'net.jodah:concurrentunit:0.4.4'
} }

View File

@@ -20,24 +20,27 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/MyMaterialTheme" android:theme="@style/MyMaterialTheme"
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true">
<activity android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden"
android:launchMode="singleTop"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity <activity
android:name=".WalletActivity" android:name=".WalletActivity"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:label="@string/wallet_activity_name" android:label="@string/wallet_activity_name"
android:launchMode="singleTask" android:launchMode="singleTask"
android:screenOrientation="behind" /> android:screenOrientation="behind" />
<activity <activity
android:name=".LoginActivity" android:name=".LoginActivity"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleTop" android:launchMode="singleTop"
android:screenOrientation="locked"> android:screenOrientation="locked">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter> </intent-filter>
@@ -62,6 +65,10 @@
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/usb_device_filter" /> android:resource="@xml/usb_device_filter" />
</activity> </activity>
<activity android:name=".onboarding.OnBoardingActivity"
android:configChanges="orientation|keyboardHidden"
android:launchMode="singleTask"
android:screenOrientation="portrait"/>
<service <service
android:name=".service.WalletService" android:name=".service.WalletService"
@@ -79,4 +86,4 @@
android:resource="@xml/filepaths" /> android:resource="@xml/filepaths" />
</provider> </provider>
</application> </application>
</manifest> </manifest>

File diff suppressed because it is too large Load Diff

View File

@@ -315,10 +315,14 @@ public class LoginActivity extends BaseActivity
if (WalletManager.getInstance().walletExists(walletFile)) { if (WalletManager.getInstance().walletExists(walletFile)) {
Helper.promptPassword(LoginActivity.this, walletName, true, new Helper.PasswordAction() { Helper.promptPassword(LoginActivity.this, walletName, true, new Helper.PasswordAction() {
@Override @Override
public void action(String walletName, String password, boolean fingerprintUsed) { public void act(String walletName, String password, boolean fingerprintUsed) {
if (checkDevice(walletName, password)) if (checkDevice(walletName, password))
startDetails(walletFile, password, GenerateReviewFragment.VIEW_TYPE_DETAILS); startDetails(walletFile, password, GenerateReviewFragment.VIEW_TYPE_DETAILS);
} }
@Override
public void fail(String walletName, String password, boolean fingerprintUsed) {
}
}); });
} else { // this cannot really happen as we prefilter choices } else { // this cannot really happen as we prefilter choices
Timber.e("Wallet missing: %s", walletName); Timber.e("Wallet missing: %s", walletName);
@@ -348,10 +352,14 @@ public class LoginActivity extends BaseActivity
if (WalletManager.getInstance().walletExists(walletFile)) { if (WalletManager.getInstance().walletExists(walletFile)) {
Helper.promptPassword(LoginActivity.this, walletName, false, new Helper.PasswordAction() { Helper.promptPassword(LoginActivity.this, walletName, false, new Helper.PasswordAction() {
@Override @Override
public void action(String walletName, String password, boolean fingerprintUsed) { public void act(String walletName, String password, boolean fingerprintUsed) {
if (checkDevice(walletName, password)) if (checkDevice(walletName, password))
startReceive(walletFile, password); startReceive(walletFile, password);
} }
@Override
public void fail(String walletName, String password, boolean fingerprintUsed) {
}
}); });
} else { // this cannot really happen as we prefilter choices } else { // this cannot really happen as we prefilter choices
Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show();
@@ -1306,10 +1314,15 @@ public class LoginActivity extends BaseActivity
Helper.promptPassword(LoginActivity.this, walletName, false, Helper.promptPassword(LoginActivity.this, walletName, false,
new Helper.PasswordAction() { new Helper.PasswordAction() {
@Override @Override
public void action(String walletName, String password, boolean fingerprintUsed) { public void act(String walletName, String password, boolean fingerprintUsed) {
if (checkDevice(walletName, password)) if (checkDevice(walletName, password))
startWallet(walletName, password, fingerprintUsed, streetmode); startWallet(walletName, password, fingerprintUsed, streetmode);
} }
@Override
public void fail(String walletName, String password, boolean fingerprintUsed) {
}
}); });
} else { // this cannot really happen as we prefilter choices } else { // this cannot really happen as we prefilter choices
Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show(); Toast.makeText(this, getString(R.string.bad_wallet), Toast.LENGTH_SHORT).show();

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018-2020 EarlOfEgo, 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;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import com.m2049r.xmrwallet.onboarding.OnBoardingActivity;
import com.m2049r.xmrwallet.onboarding.OnBoardingManager;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (OnBoardingManager.shouldShowOnBoarding(getApplicationContext())) {
startActivity(new Intent(this, OnBoardingActivity.class));
} else {
startActivity(new Intent(this, LoginActivity.class));
}
finish();
}
}

View File

@@ -88,7 +88,6 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
private ActionBarDrawerToggle drawerToggle; private ActionBarDrawerToggle drawerToggle;
private Toolbar toolbar; private Toolbar toolbar;
private boolean needVerifyIdentity;
private boolean requestStreetMode = false; private boolean requestStreetMode = false;
private String password; private String password;
@@ -142,7 +141,6 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
private void enableStreetMode(boolean enable) { private void enableStreetMode(boolean enable) {
if (enable) { if (enable) {
needVerifyIdentity = true;
streetMode = getWallet().getDaemonBlockChainHeight(); streetMode = getWallet().getDaemonBlockChainHeight();
} else { } else {
streetMode = 0; streetMode = 0;
@@ -151,11 +149,9 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
getSupportFragmentManager().findFragmentByTag(WalletFragment.class.getName()); getSupportFragmentManager().findFragmentByTag(WalletFragment.class.getName());
if (walletFragment != null) walletFragment.resetDismissedTransactions(); if (walletFragment != null) walletFragment.resetDismissedTransactions();
forceUpdate(); forceUpdate();
runOnUiThread(new Runnable() { runOnUiThread(() -> {
@Override if (getWallet() != null)
public void run() {
updateAccountsBalance(); updateAccountsBalance();
}
}); });
} }
@@ -200,7 +196,6 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
if (extras != null) { if (extras != null) {
acquireWakeLock(); acquireWakeLock();
String walletId = extras.getString(REQUEST_ID); String walletId = extras.getString(REQUEST_ID);
needVerifyIdentity = extras.getBoolean(REQUEST_FINGERPRINT_USED);
// we can set the streetmode height AFTER opening the wallet // we can set the streetmode height AFTER opening the wallet
requestStreetMode = extras.getBoolean(REQUEST_STREETMODE); requestStreetMode = extras.getBoolean(REQUEST_STREETMODE);
password = extras.getString(REQUEST_PW); password = extras.getString(REQUEST_PW);
@@ -333,7 +328,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
private void onDisableStreetMode() { private void onDisableStreetMode() {
Helper.promptPassword(WalletActivity.this, getWallet().getName(), false, new Helper.PasswordAction() { Helper.promptPassword(WalletActivity.this, getWallet().getName(), false, new Helper.PasswordAction() {
@Override @Override
public void action(String walletName, String password, boolean fingerprintUsed) { public void act(String walletName, String password, boolean fingerprintUsed) {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -342,6 +337,10 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
} }
}); });
} }
@Override
public void fail(String walletName, String password, boolean fingerprintUsed) {
}
}); });
} }
@@ -576,10 +575,9 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
@Override @Override
public boolean onRefreshed(final Wallet wallet, final boolean full) { public boolean onRefreshed(final Wallet wallet, final boolean full) {
Timber.d("onRefreshed()"); Timber.d("onRefreshed()");
runOnUiThread(new Runnable() { runOnUiThread(() -> {
public void run() { if (getWallet() != null)
updateAccountsBalance(); updateAccountsBalance();
}
}); });
if (numAccounts != wallet.getNumAccounts()) { if (numAccounts != wallet.getNumAccounts()) {
numAccounts = wallet.getNumAccounts(); numAccounts = wallet.getNumAccounts();
@@ -855,17 +853,16 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
final Bundle extras = new Bundle(); final Bundle extras = new Bundle();
extras.putString(GenerateReviewFragment.REQUEST_TYPE, GenerateReviewFragment.VIEW_TYPE_WALLET); extras.putString(GenerateReviewFragment.REQUEST_TYPE, GenerateReviewFragment.VIEW_TYPE_WALLET);
if (needVerifyIdentity) { Helper.promptPassword(WalletActivity.this, getWallet().getName(), true, new Helper.PasswordAction() {
Helper.promptPassword(WalletActivity.this, getWallet().getName(), true, new Helper.PasswordAction() { @Override
@Override public void act(String walletName, String password, boolean fingerprintUsed) {
public void action(String walletName, String password, boolean fingerprintUsed) { replaceFragment(new GenerateReviewFragment(), null, extras);
replaceFragment(new GenerateReviewFragment(), null, extras); }
needVerifyIdentity = false;
} @Override
}); public void fail(String walletName, String password, boolean fingerprintUsed) {
} else { }
replaceFragment(new GenerateReviewFragment(), null, extras); });
}
break; break;
case DialogInterface.BUTTON_NEGATIVE: case DialogInterface.BUTTON_NEGATIVE:
@@ -1003,12 +1000,6 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
return getWallet().getUnlockedBalance(); return getWallet().getUnlockedBalance();
} }
@Override
public boolean verifyWalletPassword(String password) {
String walletPassword = Helper.getWalletPassword(getApplicationContext(), getWalletName(), password);
return walletPassword != null;
}
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (drawer.isDrawerOpen(GravityCompat.START)) { if (drawer.isDrawerOpen(GravityCompat.START)) {

View File

@@ -346,103 +346,16 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
} }
public void preSend() { public void preSend() {
final Activity activity = getActivity(); Helper.promptPassword(getContext(), getActivityCallback().getWalletName(), false, new Helper.PasswordAction() {
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 = promptsView.findViewById(R.id.etPassword);
etPassword.setHint(getString(R.string.prompt_send_password));
etPassword.getEditText().addTextChangedListener(new TextWatcher() {
@Override @Override
public void afterTextChanged(Editable s) { public void act(String walletName, String password, boolean fingerprintUsed) {
if (etPassword.getError() != null) { send();
etPassword.setError(null);
}
} }
@Override public void fail(String walletName, String password, boolean fingerprintUsed) {
public void beforeTextChanged(CharSequence s, int start, bSend.setEnabled(sendCountdown > 0); // allow to try again
int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
} }
}); });
alertDialogBuilder
.setCancelable(false)
.setPositiveButton(getString(R.string.label_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(getString(R.string.label_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) && (event.getAction() == KeyEvent.ACTION_DOWN))
|| (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;
}
});
if (Helper.preventScreenshot()) {
passwordDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}
passwordDialog.show();
} }
// creates a pending transaction and calls us back with transactionCreated() // creates a pending transaction and calls us back with transactionCreated()
@@ -457,8 +370,8 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
} }
showProgress(3, getString(R.string.label_send_progress_create_tx)); showProgress(3, getString(R.string.label_send_progress_create_tx));
TxData txData = sendListener.getTxData(); TxData txData = sendListener.getTxData();
txData.setDestinationAddress(xmrtoStatus.getXmrReceivingSubaddress()); txData.setDestinationAddress(xmrtoStatus.getReceivingSubaddress());
txData.setAmount(Wallet.getAmountFromDouble(xmrtoStatus.getXmrAmountTotal())); txData.setAmount(Wallet.getAmountFromDouble(xmrtoStatus.getIncomingAmountTotal()));
getActivityCallback().onPrepareSend(xmrtoStatus.getUuid(), txData); getActivityCallback().onPrepareSend(xmrtoStatus.getUuid(), txData);
} }
@@ -572,22 +485,22 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
NumberFormat df = NumberFormat.getInstance(Locale.US); NumberFormat df = NumberFormat.getInstance(Locale.US);
df.setMaximumFractionDigits(12); df.setMaximumFractionDigits(12);
String btcAmount = df.format(status.getBtcAmount()); String btcAmount = df.format(status.getBtcAmount());
String xmrAmountTotal = df.format(status.getXmrAmountTotal()); String xmrAmountTotal = df.format(status.getIncomingAmountTotal());
tvTxBtcAmount.setText(getString(R.string.text_send_btc_amount, btcAmount, xmrAmountTotal)); tvTxBtcAmount.setText(getString(R.string.text_send_btc_amount, btcAmount, xmrAmountTotal));
String xmrPriceBtc = df.format(status.getXmrPriceBtc()); String xmrPriceBtc = df.format(status.getIncomingPriceBtc());
tvTxBtcRate.setText(getString(R.string.text_send_btc_rate, xmrPriceBtc)); tvTxBtcRate.setText(getString(R.string.text_send_btc_rate, xmrPriceBtc));
double calcRate = status.getBtcAmount() / status.getXmrPriceBtc(); double calcRate = status.getBtcAmount() / status.getIncomingPriceBtc();
Timber.i("Rates: %f / %f", calcRate, status.getXmrPriceBtc()); Timber.d("Rates: %f / %f", calcRate, status.getIncomingPriceBtc());
tvTxBtcAddress.setText(status.getBtcDestAddress()); // TODO test if this is different? tvTxBtcAddress.setText(status.getBtcDestAddress()); // TODO test if this is different?
Timber.i("Expires @ %s, in %s seconds", status.getExpiresAt().toString(), status.getSecondsTillTimeout()); Timber.d("Expires @ %s, in %s seconds", status.getExpiresAt().toString(), status.getSecondsTillTimeout());
Timber.i("Status = %s", status.getState().toString()); Timber.d("Status = %s", status.getState().toString());
tvTxXmrToKey.setText(status.getUuid()); tvTxXmrToKey.setText(status.getUuid());
Timber.d("AmountRemaining=%f, XmrAmountTotal=%f", status.getXmrAmountRemaining(), status.getXmrAmountTotal()); Timber.d("AmountRemaining=%f, XmrAmountTotal=%f", status.getRemainingAmountIncoming(), status.getIncomingAmountTotal());
hideProgress(); hideProgress();
startSendTimer(); startSendTimer();
prepareSend(); prepareSend();

View File

@@ -141,7 +141,12 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
void send() { void send() {
sendListener.commitTransaction(); sendListener.commitTransaction();
pbProgressSend.setVisibility(View.VISIBLE); getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
pbProgressSend.setVisibility(View.VISIBLE);
}
});
} }
@Override @Override
@@ -225,103 +230,16 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
} }
public void preSend() { public void preSend() {
final Activity activity = getActivity(); Helper.promptPassword(getContext(), getActivityCallback().getWalletName(), false, new Helper.PasswordAction() {
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 = promptsView.findViewById(R.id.etPassword);
etPassword.setHint(getString(R.string.prompt_send_password));
etPassword.getEditText().addTextChangedListener(new TextWatcher() {
@Override @Override
public void afterTextChanged(Editable s) { public void act(String walletName, String password, boolean fingerprintUsed) {
if (etPassword.getError() != null) { send();
etPassword.setError(null);
}
} }
@Override public void fail(String walletName, String password, boolean fingerprintUsed) {
public void beforeTextChanged(CharSequence s, int start, bSend.setEnabled(true); // allow to try again
int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
} }
}); });
alertDialogBuilder
.setCancelable(false)
.setPositiveButton(getString(R.string.label_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(getString(R.string.label_cancel),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Helper.hideKeyboardAlways(activity);
dialog.cancel();
bSend.setEnabled(true); // 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) && (event.getAction() == KeyEvent.ACTION_DOWN))
|| (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;
}
});
if (Helper.preventScreenshot()) {
passwordDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}
passwordDialog.show();
} }
// creates a pending transaction and calls us back with transactionCreated() // creates a pending transaction and calls us back with transactionCreated()

View File

@@ -75,7 +75,7 @@ public class SendFragment extends Fragment
void onPrepareSend(String tag, TxData data); void onPrepareSend(String tag, TxData data);
boolean verifyWalletPassword(String password); String getWalletName();
void onSend(UserNotes notes); void onSend(UserNotes notes);

View File

@@ -17,12 +17,15 @@
package com.m2049r.xmrwallet.ledger; package com.m2049r.xmrwallet.ledger;
public enum Instruction { public enum Instruction {
INS_NONE(0x00), INS_NONE(0x00),
INS_RESET(0x02), INS_RESET(0x02),
INS_GET_KEY(0x20), INS_GET_KEY(0x20),
INS_DISPLAY_ADDRESS(0x21),
INS_PUT_KEY(0x22), INS_PUT_KEY(0x22),
INS_GET_CHACHA8_PREKEY(0x24), INS_GET_CHACHA8_PREKEY(0x24),
INS_VERIFY_KEY(0x26), INS_VERIFY_KEY(0x26),
INS_MANAGE_SEEDWORDS(0x28),
INS_SECRET_KEY_TO_PUBLIC_KEY(0x30), INS_SECRET_KEY_TO_PUBLIC_KEY(0x30),
INS_GEN_KEY_DERIVATION(0x32), INS_GEN_KEY_DERIVATION(0x32),
@@ -30,6 +33,7 @@ public enum Instruction {
INS_DERIVE_PUBLIC_KEY(0x36), INS_DERIVE_PUBLIC_KEY(0x36),
INS_DERIVE_SECRET_KEY(0x38), INS_DERIVE_SECRET_KEY(0x38),
INS_GEN_KEY_IMAGE(0x3A), INS_GEN_KEY_IMAGE(0x3A),
INS_SECRET_KEY_ADD(0x3C), INS_SECRET_KEY_ADD(0x3C),
INS_SECRET_KEY_SUB(0x3E), INS_SECRET_KEY_SUB(0x3E),
INS_GENERATE_KEYPAIR(0x40), INS_GENERATE_KEYPAIR(0x40),
@@ -45,15 +49,20 @@ public enum Instruction {
INS_SET_SIGNATURE_MODE(0x72), INS_SET_SIGNATURE_MODE(0x72),
INS_GET_ADDITIONAL_KEY(0x74), INS_GET_ADDITIONAL_KEY(0x74),
INS_STEALTH(0x76), INS_STEALTH(0x76),
INS_GEN_COMMITMENT_MASK(0x77),
INS_BLIND(0x78), INS_BLIND(0x78),
INS_UNBLIND(0x7A), INS_UNBLIND(0x7A),
INS_GEN_TXOUT_KEYS(0x7B),
INS_VALIDATE(0x7C), INS_VALIDATE(0x7C),
INS_PREFIX_HASH(0x7D),
INS_MLSAG(0x7E), INS_MLSAG(0x7E),
INS_CLOSE_TX(0x80), INS_CLOSE_TX(0x80),
INS_GET_RESPONSE(0xc0), INS_GET_TX_PROOF(0xA0),
INS_UNDEFINED(0xff); INS_GET_RESPONSE(0xC0),
INS_UNDEFINED(0xFF);
public static Instruction fromByte(byte n) { public static Instruction fromByte(byte n) {
switch (n & 0xFF) { switch (n & 0xFF) {

View File

@@ -42,11 +42,11 @@ public class Ledger {
static public final int LOOKAHEAD_SUBADDRESSES = 20; static public final int LOOKAHEAD_SUBADDRESSES = 20;
static public final String SUBADDRESS_LOOKAHEAD = LOOKAHEAD_ACCOUNTS + ":" + LOOKAHEAD_SUBADDRESSES; static public final String SUBADDRESS_LOOKAHEAD = LOOKAHEAD_ACCOUNTS + ":" + LOOKAHEAD_SUBADDRESSES;
private static final byte PROTOCOL_VERSION = 0x02; private static final byte PROTOCOL_VERSION = 0x03;
public static final int SW_OK = 0x9000; public static final int SW_OK = 0x9000;
public static final int SW_INS_NOT_SUPPORTED = 0x6D00; public static final int SW_INS_NOT_SUPPORTED = 0x6D00;
public static final int OK[] = {SW_OK}; public static final int OK[] = {SW_OK};
public static final int MINIMUM_LEDGER_VERSION = (1 << 16) + (3 << 8) + (1); // 1.3.1 public static final int MINIMUM_LEDGER_VERSION = (1 << 16) + (6 << 8) + (0); // 1.6.0
public static UsbDevice findDevice(UsbManager usbManager) { public static UsbDevice findDevice(UsbManager usbManager) {
if (!ENABLED) return null; if (!ENABLED) return null;

View File

@@ -51,6 +51,7 @@ public class LedgerProgressDialog extends ProgressDialog implements Ledger.Liste
switch (ins) { switch (ins) {
case INS_RESET: // ledger may ask for confirmation - maybe a bug? case INS_RESET: // ledger may ask for confirmation - maybe a bug?
case INS_GET_KEY: // ledger asks for confirmation to send keys case INS_GET_KEY: // ledger asks for confirmation to send keys
case INS_DISPLAY_ADDRESS:
setIndeterminate(true); setIndeterminate(true);
setMessage(getContext().getString(R.string.progress_ledger_confirm)); setMessage(getContext().getString(R.string.progress_ledger_confirm));
break; break;
@@ -102,6 +103,11 @@ public class LedgerProgressDialog extends ProgressDialog implements Ledger.Liste
setMessage(getContext().getString(R.string.progress_ledger_mlsag)); setMessage(getContext().getString(R.string.progress_ledger_mlsag));
} }
break; break;
case INS_PREFIX_HASH:
if ((apdu[2] != 1) || (apdu[3] != 0)) break;
setIndeterminate(true);
setMessage(getContext().getString(R.string.progress_ledger_confirm));
break;
case INS_VALIDATE: case INS_VALIDATE:
if ((apdu[2] != 1) || (apdu[3] != 1)) break; if ((apdu[2] != 1) || (apdu[3] != 1)) break;
validate = true; validate = true;

View File

@@ -0,0 +1,122 @@
/*
* Copyright (c) 2018-2020 EarlOfEgo, 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.onboarding;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import com.m2049r.xmrwallet.LoginActivity;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.util.KeyStoreHelper;
public class OnBoardingActivity extends AppCompatActivity implements OnBoardingAdapter.Listener {
private OnBoardingViewPager pager;
private OnBoardingAdapter pagerAdapter;
private Button nextButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_on_boarding);
nextButton = findViewById(R.id.buttonNext);
pager = findViewById(R.id.pager);
pagerAdapter = new OnBoardingAdapter(getApplicationContext(), this);
pager.setAdapter(pagerAdapter);
int pixels = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics());
pager.setPageMargin(pixels);
pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
setButtonState(position);
}
});
final TabLayout tabLayout = (TabLayout) findViewById(R.id.tabLayout);
if (pagerAdapter.getCount() > 1) {
tabLayout.setupWithViewPager(pager, true);
LinearLayout tabStrip = ((LinearLayout) tabLayout.getChildAt(0));
for (int i = 0; i < tabStrip.getChildCount(); i++) {
tabStrip.getChildAt(i).setClickable(false);
}
} else {
tabLayout.setVisibility(View.GONE);
}
nextButton.setOnClickListener(v -> {
final int item = pager.getCurrentItem();
if (item + 1 >= pagerAdapter.getCount()) {
finishOnboarding();
} else {
pager.setCurrentItem(item + 1);
}
});
// let old users who have fingerprint wallets already agree for fingerprint sending
OnBoardingScreen.FPSEND.setMustAgree(KeyStoreHelper.hasStoredPasswords(this));
for (int i = 0; i < OnBoardingScreen.values().length; i++) {
agreed[i] = !OnBoardingScreen.values()[i].isMustAgree();
}
setButtonState(0);
}
private void finishOnboarding() {
nextButton.setEnabled(false);
OnBoardingManager.setOnBoardingShown(getApplicationContext());
startActivity(new Intent(this, LoginActivity.class));
finish();
}
boolean[] agreed = new boolean[OnBoardingScreen.values().length];
@Override
public void setAgreeClicked(int position, boolean isChecked) {
agreed[position] = isChecked;
setButtonState(position);
}
@Override
public boolean isAgreeClicked(int position) {
return agreed[position];
}
@Override
public void setButtonState(int position) {
nextButton.setEnabled(agreed[position]);
if (nextButton.isEnabled())
pager.setAllowedSwipeDirection(OnBoardingViewPager.SwipeDirection.ALL);
else
pager.setAllowedSwipeDirection(OnBoardingViewPager.SwipeDirection.LEFT);
if (pager.getCurrentItem() + 1 == pagerAdapter.getCount()) { // last page
nextButton.setText(R.string.onboarding_button_ready);
} else {
nextButton.setText(R.string.onboarding_button_next);
}
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 2018-2020 EarlOfEgo, 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.onboarding;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import com.m2049r.xmrwallet.R;
import timber.log.Timber;
public class OnBoardingAdapter extends PagerAdapter {
interface Listener {
void setAgreeClicked(int position, boolean isChecked);
boolean isAgreeClicked(int position);
void setButtonState(int position);
}
private final Context context;
private Listener listener;
OnBoardingAdapter(final Context context, final Listener listener) {
this.context = context;
this.listener = listener;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup collection, int position) {
LayoutInflater inflater = LayoutInflater.from(context);
final View view = inflater.inflate(R.layout.view_onboarding, collection, false);
final OnBoardingScreen onBoardingScreen = OnBoardingScreen.values()[position];
final Drawable drawable = ContextCompat.getDrawable(context, onBoardingScreen.getDrawable());
((ImageView) view.findViewById(R.id.onboardingImage)).setImageDrawable(drawable);
((TextView) view.findViewById(R.id.onboardingTitle)).setText(onBoardingScreen.getTitle());
((TextView) view.findViewById(R.id.onboardingInformation)).setText(onBoardingScreen.getInformation());
if (onBoardingScreen.isMustAgree()) {
final CheckBox agree = ((CheckBox) view.findViewById(R.id.onboardingAgree));
agree.setVisibility(View.VISIBLE);
agree.setChecked(listener.isAgreeClicked(position));
agree.setOnClickListener(v -> {
listener.setAgreeClicked(position, ((CheckBox) v).isChecked());
});
}
collection.addView(view);
return view;
}
@Override
public int getCount() {
return OnBoardingScreen.values().length;
}
@Override
public void destroyItem(@NonNull ViewGroup collection, int position, @NonNull Object view) {
Timber.d("destroy " + position);
collection.removeView((View) view);
}
@Override
public boolean isViewFromObject(@NonNull final View view, @NonNull final Object object) {
return view == object;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2018-2020 EarlOfEgo, 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.onboarding;
import android.content.Context;
import android.content.SharedPreferences;
import com.m2049r.xmrwallet.util.KeyStoreHelper;
import java.util.Date;
import timber.log.Timber;
public class OnBoardingManager {
private static final String PREFS_ONBOARDING = "PREFS_ONBOARDING";
private static final String ONBOARDING_SHOWN = "ONBOARDING_SHOWN";
public static boolean shouldShowOnBoarding(final Context context) {
return !getSharedPreferences(context).contains(ONBOARDING_SHOWN);
}
public static void setOnBoardingShown(final Context context) {
Timber.d("Set onboarding shown.");
SharedPreferences sharedPreferences = getSharedPreferences(context);
sharedPreferences.edit().putLong(ONBOARDING_SHOWN, new Date().getTime()).apply();
}
public static void clearOnBoardingShown(final Context context) {
SharedPreferences sharedPreferences = getSharedPreferences(context);
sharedPreferences.edit().remove(ONBOARDING_SHOWN).apply();
}
private static SharedPreferences getSharedPreferences(final Context context) {
return context.getSharedPreferences(PREFS_ONBOARDING, Context.MODE_PRIVATE);
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2018-2020 EarlOfEgo, 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.onboarding;
import com.m2049r.xmrwallet.R;
enum OnBoardingScreen {
WELCOME(R.string.onboarding_welcome_title, R.string.onboarding_welcome_information, R.drawable.ic_onboarding_welcome, false),
SEED(R.string.onboarding_seed_title, R.string.onboarding_seed_information, R.drawable.ic_onboarding_seed, true),
FPSEND(R.string.onboarding_fpsend_title, R.string.onboarding_fpsend_information, R.drawable.ic_onboarding_fingerprint, false),
XMRTO(R.string.onboarding_xmrto_title, R.string.onboarding_xmrto_information, R.drawable.ic_onboarding_xmrto, false),
NODES(R.string.onboarding_nodes_title, R.string.onboarding_nodes_information, R.drawable.ic_onboarding_nodes, false);
private final int title;
private final int information;
private final int drawable;
private boolean mustAgree;
OnBoardingScreen(final int title, final int information, final int drawable, final boolean mustAgree) {
this.title = title;
this.information = information;
this.drawable = drawable;
this.mustAgree = mustAgree;
}
public int getTitle() {
return title;
}
public int getInformation() {
return information;
}
public int getDrawable() {
return drawable;
}
public boolean isMustAgree() {
return mustAgree;
}
public boolean setMustAgree(boolean mustAgree) {
return this.mustAgree = mustAgree;
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2020 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// based on https://stackoverflow.com/a/34076649
package com.m2049r.xmrwallet.onboarding;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
public class OnBoardingViewPager extends ViewPager {
public enum SwipeDirection {
ALL, LEFT, RIGHT, NONE;
}
private float initialXValue;
private SwipeDirection direction;
public OnBoardingViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
this.direction = SwipeDirection.ALL;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (this.IsSwipeAllowed(event)) {
return super.onTouchEvent(event);
}
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (this.IsSwipeAllowed(event)) {
return super.onInterceptTouchEvent(event);
}
return false;
}
private boolean IsSwipeAllowed(MotionEvent event) {
if (this.direction == SwipeDirection.ALL) return true;
if (direction == SwipeDirection.NONE)//disable any swipe
return false;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
initialXValue = event.getX();
return true;
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
float diffX = event.getX() - initialXValue;
if (diffX > 0 && direction == SwipeDirection.RIGHT) {
// swipe from left to right detected
return false;
} else if (diffX < 0 && direction == SwipeDirection.LEFT) {
// swipe from right to left detected
return false;
}
}
return true;
}
public void setAllowedSwipeDirection(SwipeDirection direction) {
this.direction = direction;
}
}

View File

@@ -281,97 +281,114 @@ public class WalletService extends Service {
case START_SERVICE: { case START_SERVICE: {
Bundle extras = msg.getData(); Bundle extras = msg.getData();
String cmd = extras.getString(REQUEST, null); String cmd = extras.getString(REQUEST, null);
if (cmd.equals(REQUEST_CMD_LOAD)) { switch (cmd) {
String walletId = extras.getString(REQUEST_WALLET, null); case REQUEST_CMD_LOAD:
String walletPw = extras.getString(REQUEST_CMD_LOAD_PW, null); String walletId = extras.getString(REQUEST_WALLET, null);
Timber.d("LOAD wallet %s", walletId); String walletPw = extras.getString(REQUEST_CMD_LOAD_PW, null);
if (walletId != null) { Timber.d("LOAD wallet %s", walletId);
showProgress(getString(R.string.status_wallet_loading)); if (walletId != null) {
showProgress(10); showProgress(getString(R.string.status_wallet_loading));
Wallet.Status walletStatus = start(walletId, walletPw); showProgress(10);
if (observer != null) observer.onWalletStarted(walletStatus); Wallet.Status walletStatus = start(walletId, walletPw);
if ((walletStatus == null) || !walletStatus.isOk()) { if (observer != null) observer.onWalletStarted(walletStatus);
errorState = true; if ((walletStatus == null) || !walletStatus.isOk()) {
stop(); errorState = true;
} stop();
} }
} else if (cmd.equals(REQUEST_CMD_STORE)) {
Wallet myWallet = getWallet();
Timber.d("STORE wallet: %s", myWallet.getName());
boolean rc = myWallet.store();
Timber.d("wallet stored: %s with rc=%b", myWallet.getName(), rc);
if (!rc) {
Timber.w("Wallet store failed: %s", myWallet.getStatus().getErrorString());
}
if (observer != null) observer.onWalletStored(rc);
} else if (cmd.equals(REQUEST_CMD_TX)) {
Wallet myWallet = getWallet();
Timber.d("CREATE TX for wallet: %s", myWallet.getName());
myWallet.disposePendingTransaction(); // remove any old pending tx
TxData txData = extras.getParcelable(REQUEST_CMD_TX_DATA);
String txTag = extras.getString(REQUEST_CMD_TX_TAG);
PendingTransaction pendingTransaction = myWallet.createTransaction(txData);
PendingTransaction.Status status = pendingTransaction.getStatus();
Timber.d("transaction status %s", status);
if (status != PendingTransaction.Status.Status_Ok) {
Timber.w("Create Transaction failed: %s", pendingTransaction.getErrorString());
}
if (observer != null) {
observer.onTransactionCreated(txTag, pendingTransaction);
} else {
myWallet.disposePendingTransaction();
}
} else if (cmd.equals(REQUEST_CMD_SWEEP)) {
Wallet myWallet = getWallet();
Timber.d("SWEEP TX for wallet: %s", myWallet.getName());
myWallet.disposePendingTransaction(); // remove any old pending tx
String txTag = extras.getString(REQUEST_CMD_TX_TAG);
PendingTransaction pendingTransaction = myWallet.createSweepUnmixableTransaction();
PendingTransaction.Status status = pendingTransaction.getStatus();
Timber.d("transaction status %s", status);
if (status != PendingTransaction.Status.Status_Ok) {
Timber.w("Create Transaction failed: %s", pendingTransaction.getErrorString());
}
if (observer != null) {
observer.onTransactionCreated(txTag, pendingTransaction);
} else {
myWallet.disposePendingTransaction();
}
} else if (cmd.equals(REQUEST_CMD_SEND)) {
Wallet myWallet = getWallet();
Timber.d("SEND TX for wallet: %s", myWallet.getName());
PendingTransaction pendingTransaction = myWallet.getPendingTransaction();
if (pendingTransaction == null) {
throw new IllegalArgumentException("PendingTransaction is null"); // die
}
if (pendingTransaction.getStatus() != PendingTransaction.Status.Status_Ok) {
Timber.e("PendingTransaction is %s", pendingTransaction.getStatus());
final String error = pendingTransaction.getErrorString();
myWallet.disposePendingTransaction(); // it's broken anyway
if (observer != null) observer.onSendTransactionFailed(error);
return;
}
final String txid = pendingTransaction.getFirstTxId(); // tx ids vanish after commit()!
boolean success = pendingTransaction.commit("", true);
if (success) {
myWallet.disposePendingTransaction();
if (observer != null) observer.onTransactionSent(txid);
String notes = extras.getString(REQUEST_CMD_SEND_NOTES);
if ((notes != null) && (!notes.isEmpty())) {
myWallet.setUserNote(txid, notes);
} }
break;
case REQUEST_CMD_STORE: {
Wallet myWallet = getWallet();
if (myWallet == null) break;
Timber.d("STORE wallet: %s", myWallet.getName());
boolean rc = myWallet.store(); boolean rc = myWallet.store();
Timber.d("wallet stored: %s with rc=%b", myWallet.getName(), rc); Timber.d("wallet stored: %s with rc=%b", myWallet.getName(), rc);
if (!rc) { if (!rc) {
Timber.w("Wallet store failed: %s", myWallet.getStatus().getErrorString()); Timber.w("Wallet store failed: %s", myWallet.getStatus().getErrorString());
} }
if (observer != null) observer.onWalletStored(rc); if (observer != null) observer.onWalletStored(rc);
listener.updated = true; break;
} else { }
final String error = pendingTransaction.getErrorString(); case REQUEST_CMD_TX: {
myWallet.disposePendingTransaction(); Wallet myWallet = getWallet();
if (observer != null) observer.onSendTransactionFailed(error); if (myWallet == null) break;
return; Timber.d("CREATE TX for wallet: %s", myWallet.getName());
myWallet.disposePendingTransaction(); // remove any old pending tx
TxData txData = extras.getParcelable(REQUEST_CMD_TX_DATA);
String txTag = extras.getString(REQUEST_CMD_TX_TAG);
PendingTransaction pendingTransaction = myWallet.createTransaction(txData);
PendingTransaction.Status status = pendingTransaction.getStatus();
Timber.d("transaction status %s", status);
if (status != PendingTransaction.Status.Status_Ok) {
Timber.w("Create Transaction failed: %s", pendingTransaction.getErrorString());
}
if (observer != null) {
observer.onTransactionCreated(txTag, pendingTransaction);
} else {
myWallet.disposePendingTransaction();
}
break;
}
case REQUEST_CMD_SWEEP: {
Wallet myWallet = getWallet();
if (myWallet == null) break;
Timber.d("SWEEP TX for wallet: %s", myWallet.getName());
myWallet.disposePendingTransaction(); // remove any old pending tx
String txTag = extras.getString(REQUEST_CMD_TX_TAG);
PendingTransaction pendingTransaction = myWallet.createSweepUnmixableTransaction();
PendingTransaction.Status status = pendingTransaction.getStatus();
Timber.d("transaction status %s", status);
if (status != PendingTransaction.Status.Status_Ok) {
Timber.w("Create Transaction failed: %s", pendingTransaction.getErrorString());
}
if (observer != null) {
observer.onTransactionCreated(txTag, pendingTransaction);
} else {
myWallet.disposePendingTransaction();
}
break;
}
case REQUEST_CMD_SEND: {
Wallet myWallet = getWallet();
if (myWallet == null) break;
Timber.d("SEND TX for wallet: %s", myWallet.getName());
PendingTransaction pendingTransaction = myWallet.getPendingTransaction();
if (pendingTransaction == null) {
throw new IllegalArgumentException("PendingTransaction is null"); // die
}
if (pendingTransaction.getStatus() != PendingTransaction.Status.Status_Ok) {
Timber.e("PendingTransaction is %s", pendingTransaction.getStatus());
final String error = pendingTransaction.getErrorString();
myWallet.disposePendingTransaction(); // it's broken anyway
if (observer != null) observer.onSendTransactionFailed(error);
return;
}
final String txid = pendingTransaction.getFirstTxId(); // tx ids vanish after commit()!
boolean success = pendingTransaction.commit("", true);
if (success) {
myWallet.disposePendingTransaction();
if (observer != null) observer.onTransactionSent(txid);
String notes = extras.getString(REQUEST_CMD_SEND_NOTES);
if ((notes != null) && (!notes.isEmpty())) {
myWallet.setUserNote(txid, notes);
}
boolean rc = myWallet.store();
Timber.d("wallet stored: %s with rc=%b", myWallet.getName(), rc);
if (!rc) {
Timber.w("Wallet store failed: %s", myWallet.getStatus().getErrorString());
}
if (observer != null) observer.onWalletStored(rc);
listener.updated = true;
} else {
final String error = pendingTransaction.getErrorString();
myWallet.disposePendingTransaction();
if (observer != null) observer.onSendTransactionFailed(error);
return;
}
break;
} }
} }
} }

View File

@@ -323,9 +323,9 @@ public class Helper {
static public HttpUrl getXmrToBaseUrl() { static public HttpUrl getXmrToBaseUrl() {
if ((WalletManager.getInstance() == null) if ((WalletManager.getInstance() == null)
|| (WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet)) { || (WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet)) {
return HttpUrl.parse("https://test.xmr.to/api/v2/xmr2btc/"); return HttpUrl.parse("https://test.xmr.to/api/v3/xmr2btc/");
} else { } else {
return HttpUrl.parse("https://xmr.to/api/v2/xmr2btc/"); return HttpUrl.parse("https://xmr.to/api/v3/xmr2btc/");
} }
} }
@@ -417,7 +417,7 @@ public class Helper {
} }
static AlertDialog openDialog = null; // for preventing opening of multiple dialogs static AlertDialog openDialog = null; // for preventing opening of multiple dialogs
static AsyncTask<Void, Void, Boolean> loginTask = null; static AsyncTask<Void, Void, Boolean> passwordTask = null;
static public void promptPassword(final Context context, final String wallet, boolean fingerprintDisabled, final PasswordAction action) { static public void promptPassword(final Context context, final String wallet, boolean fingerprintDisabled, final PasswordAction action) {
if (openDialog != null) return; // we are already asking for password if (openDialog != null) return; // we are already asking for password
@@ -442,11 +442,11 @@ public class Helper {
final AtomicBoolean incorrectSavedPass = new AtomicBoolean(false); final AtomicBoolean incorrectSavedPass = new AtomicBoolean(false);
class LoginWalletTask extends AsyncTask<Void, Void, Boolean> { class PasswordTask extends AsyncTask<Void, Void, Boolean> {
private String pass; private String pass;
private boolean fingerprintUsed; private boolean fingerprintUsed;
LoginWalletTask(String pass, boolean fingerprintUsed) { PasswordTask(String pass, boolean fingerprintUsed) {
this.pass = pass; this.pass = pass;
this.fingerprintUsed = fingerprintUsed; this.fingerprintUsed = fingerprintUsed;
} }
@@ -488,7 +488,7 @@ public class Helper {
etPassword.setError(context.getString(R.string.bad_password)); etPassword.setError(context.getString(R.string.bad_password));
} }
} }
loginTask = null; passwordTask = null;
} }
} }
@@ -521,9 +521,9 @@ public class Helper {
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
Helper.hideKeyboardAlways((Activity) context); Helper.hideKeyboardAlways((Activity) context);
cancelSignal.cancel(); cancelSignal.cancel();
if (loginTask != null) { if (passwordTask != null) {
loginTask.cancel(true); passwordTask.cancel(true);
loginTask = null; passwordTask = null;
} }
dialog.cancel(); dialog.cancel();
openDialog = null; openDialog = null;
@@ -552,9 +552,9 @@ public class Helper {
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
try { try {
String userPass = KeyStoreHelper.loadWalletUserPass(context, wallet); String userPass = KeyStoreHelper.loadWalletUserPass(context, wallet);
if (loginTask == null) { if (passwordTask == null) {
loginTask = new LoginWalletTask(userPass, true); passwordTask = new PasswordTask(userPass, true);
loginTask.execute(); passwordTask.execute();
} }
} catch (KeyStoreHelper.BrokenPasswordStoreException ex) { } catch (KeyStoreHelper.BrokenPasswordStoreException ex) {
etPassword.setError(context.getString(R.string.bad_password)); etPassword.setError(context.getString(R.string.bad_password));
@@ -586,9 +586,9 @@ public class Helper {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
String pass = etPassword.getEditText().getText().toString(); String pass = etPassword.getEditText().getText().toString();
if (loginTask == null) { if (passwordTask == null) {
loginTask = new LoginWalletTask(pass, false); passwordTask = new PasswordTask(pass, false);
loginTask.execute(); passwordTask.execute();
} }
} }
}); });
@@ -601,9 +601,9 @@ public class Helper {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|| (actionId == EditorInfo.IME_ACTION_DONE)) { || (actionId == EditorInfo.IME_ACTION_DONE)) {
String pass = etPassword.getEditText().getText().toString(); String pass = etPassword.getEditText().getText().toString();
if (loginTask == null) { if (passwordTask == null) {
loginTask = new LoginWalletTask(pass, false); passwordTask = new PasswordTask(pass, false);
loginTask.execute(); passwordTask.execute();
} }
return true; return true;
} }
@@ -620,15 +620,18 @@ public class Helper {
} }
public interface PasswordAction { public interface PasswordAction {
void action(String walletName, String password, boolean fingerprintUsed); void act(String walletName, String password, boolean fingerprintUsed);
void fail(String walletName, String password, boolean fingerprintUsed);
} }
static private boolean processPasswordEntry(Context context, String walletName, String pass, boolean fingerprintUsed, PasswordAction action) { static private boolean processPasswordEntry(Context context, String walletName, String pass, boolean fingerprintUsed, PasswordAction action) {
String walletPassword = Helper.getWalletPassword(context, walletName, pass); String walletPassword = Helper.getWalletPassword(context, walletName, pass);
if (walletPassword != null) { if (walletPassword != null) {
action.action(walletName, walletPassword, fingerprintUsed); action.act(walletName, walletPassword, fingerprintUsed);
return true; return true;
} else { } else {
action.fail(walletName, walletPassword, fingerprintUsed);
return false; return false;
} }
} }

View File

@@ -140,6 +140,11 @@ public class KeyStoreHelper {
} }
} }
public static boolean hasStoredPasswords(@NonNull Context context) {
SharedPreferences prefs = context.getSharedPreferences(SecurityConstants.WALLET_PASS_PREFS_NAME, Context.MODE_PRIVATE);
return prefs.getAll().size() > 0;
}
public static String loadWalletUserPass(@NonNull Context context, String wallet) throws BrokenPasswordStoreException { public static String loadWalletUserPass(@NonNull Context context, String wallet) throws BrokenPasswordStoreException {
String walletKeyAlias = SecurityConstants.WALLET_PASS_KEY_PREFIX + wallet; String walletKeyAlias = SecurityConstants.WALLET_PASS_KEY_PREFIX + wallet;
String encoded = context.getSharedPreferences(SecurityConstants.WALLET_PASS_PREFS_NAME, Context.MODE_PRIVATE) String encoded = context.getSharedPreferences(SecurityConstants.WALLET_PASS_PREFS_NAME, Context.MODE_PRIVATE)

View File

@@ -37,33 +37,19 @@ public class Notice {
private static final String PREFS_NAME = "notice"; private static final String PREFS_NAME = "notice";
private static List<Notice> notices = null; private static List<Notice> notices = null;
private static final String NOTICE_SHOW_XMRTO_ENABLED_LOGIN = "notice_xmrto_enabled_login";
private static final String NOTICE_SHOW_XMRTO_ENABLED_SEND = "notice_xmrto_enabled_send"; private static final String NOTICE_SHOW_XMRTO_ENABLED_SEND = "notice_xmrto_enabled_send";
private static final String NOTICE_SHOW_LEDGER = "notice_ledger_enabled_login"; private static final String NOTICE_SHOW_LEDGER = "notice_ledger_enabled_login";
private static final String NOTICE_SHOW_NODES = "notice_nodes";
private static void init() { private static void init() {
synchronized (Notice.class) { synchronized (Notice.class) {
if (notices != null) return; if (notices != null) return;
notices = new ArrayList<>(); notices = new ArrayList<>();
notices.add(
new Notice(NOTICE_SHOW_NODES,
R.string.info_nodes_enabled,
R.string.help_node,
1)
);
notices.add( notices.add(
new Notice(NOTICE_SHOW_XMRTO_ENABLED_SEND, new Notice(NOTICE_SHOW_XMRTO_ENABLED_SEND,
R.string.info_xmrto_enabled, R.string.info_xmrto_enabled,
R.string.help_xmrto, R.string.help_xmrto,
1) 1)
); );
notices.add(
new Notice(NOTICE_SHOW_XMRTO_ENABLED_LOGIN,
R.string.info_xmrto_enabled,
R.string.help_xmrto,
1)
);
notices.add( notices.add(
new Notice(NOTICE_SHOW_LEDGER, new Notice(NOTICE_SHOW_LEDGER,
R.string.info_ledger_enabled, R.string.info_ledger_enabled,

View File

@@ -116,6 +116,9 @@ public class RestoreHeight {
blockheight.put("2020-04-01", 2066806L); blockheight.put("2020-04-01", 2066806L);
blockheight.put("2020-05-01", 2088411L); blockheight.put("2020-05-01", 2088411L);
blockheight.put("2020-06-01", 2110702L); blockheight.put("2020-06-01", 2110702L);
blockheight.put("2020-07-01", 2132318L);
blockheight.put("2020-08-01", 2154590L);
blockheight.put("2020-09-01", 2176790L);
} }
public long getHeight(String date) { public long getHeight(String date) {

View File

@@ -43,47 +43,31 @@ public interface QueryOrderStatus {
boolean isError(); boolean isError();
State getState(); // "state": "<order_state_as_string>", QueryOrderStatus.State getState();
double getBtcAmount(); // "btc_amount": <requested_amount_in_btc_as_float>, double getBtcAmount();
String getBtcDestAddress(); // "btc_dest_address": "<requested_destination_address_as_string>", String getBtcDestAddress();
String getUuid(); // "uuid": "<unique_order_identifier_as_12_character_string>" String getUuid();
int getBtcNumConfirmations(); // "btc_num_confirmations": <btc_num_confirmations_as_integer>, int getBtcNumConfirmationsThreshold();
int getBtcNumConfirmationsBeforePurge(); // "btc_num_confirmations_before_purge": <btc_num_confirmations_before_purge_as_integer>, Date getCreatedAt();
String getBtcTransactionId(); // "btc_transaction_id": "<btc_transaction_id_as_string>", Date getExpiresAt();
Date getCreatedAt(); // "created_at": "<timestamp_as_string>", int getSecondsTillTimeout();
Date getExpiresAt(); // "expires_at": "<timestamp_as_string>", double getIncomingAmountTotal();
int getSecondsTillTimeout(); // "seconds_till_timeout": <seconds_till_timeout_as_integer>, double getRemainingAmountIncoming();
double getXmrAmountTotal(); // "xmr_amount_total": <amount_in_xmr_for_this_order_as_float>, int getIncomingNumConfirmationsRemaining();
double getXmrAmountRemaining(); // "xmr_amount_remaining": <amount_in_xmr_that_the_user_must_still_send_as_float>, double getIncomingPriceBtc();
int getXmrNumConfirmationsRemaining(); // "xmr_num_confirmations_remaining": <num_xmr_confirmations_remaining_before_bitcoins_will_be_sent_as_integer>, String getReceivingSubaddress();
double getXmrPriceBtc(); // "xmr_price_btc": <price_of_1_btc_in_xmr_as_offered_by_service_as_float>,
String getXmrReceivingAddress(); // "xmr_receiving_address": "xmr_old_style_address_user_can_send_funds_to_as_string",
String getXmrReceivingSubaddress(); // <xmr_subaddress_user_needs_to_send_funds_to_as_string>,
String getXmrReceivingIntegratedAddress(); // "xmr_receiving_integrated_address": "xmr_integrated_address_user_needs_to_send_funds_to_as_string",
int getXmrRecommendedMixin(); // "xmr_recommended_mixin": <xmr_recommended_mixin_as_integer>,
@Deprecated
double getXmrRequiredAmount(); // "xmr_required_amount": <xmr_amount_user_needs_to_send_as_float>,
String getXmrRequiredPaymentIdLong(); // "xmr_required_payment_id_long": "xmr_payment_id_user_needs_to_include_when_using_old_stlye_address_as_string"
String getXmrRequiredPaymentIdShort(); // "xmr_required_payment_id_short": "xmr_payment_id_included_in_integrated_address_as_string"
int getRecommendedMixin();
} }

View File

@@ -113,7 +113,8 @@ class CreateOrderImpl implements CreateOrder {
static JSONObject createRequest(final double amount, final String address) throws JSONException { static JSONObject createRequest(final double amount, final String address) throws JSONException {
final JSONObject jsonObject = new JSONObject(); final JSONObject jsonObject = new JSONObject();
jsonObject.put("btc_amount", amount); jsonObject.put("amount", amount);
jsonObject.put("amount_currency", "BTC");
jsonObject.put("btc_dest_address", address); jsonObject.put("btc_dest_address", address);
return jsonObject; return jsonObject;
} }

View File

@@ -42,23 +42,17 @@ class QueryOrderStatusImpl implements QueryOrderStatus {
private String btcDestAddress; // "btc_dest_address": "<requested_destination_address_as_string>", private String btcDestAddress; // "btc_dest_address": "<requested_destination_address_as_string>",
private String uuid; // "uuid": "<unique_order_identifier_as_12_character_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 // 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 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 int btcNumConfirmationsThreshold; // "btc_num_confirmations_threshold": <btc_num_confirmations_threshold_as_integer>,
private String btcTransactionId; // "btc_transaction_id": "<btc_transaction_id_as_string>",
private Date createdAt; // "created_at": "<timestamp_as_string>", private Date createdAt; // "created_at": "<timestamp_as_string>",
private Date expiresAt; // "expires_at": "<timestamp_as_string>", private Date expiresAt; // "expires_at": "<timestamp_as_string>",
private int secondsTillTimeout; // "seconds_till_timeout": <seconds_till_timeout_as_integer>, 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 incomingAmountTotal; // "incoming_amount_total": <amount_in_incoming_currency_for_this_order_as_float>,
private double xmrAmountRemaining; // "xmr_amount_remaining": <amount_in_xmr_that_the_user_must_still_send_as_float>, private double remainingAmountIncoming; // "remaining_amount_incoming": <amount_in_incoming_currency_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 int incomingNumConfirmationsRemaining; // "incoming_num_confirmations_remaining": <num_incoming_currency_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 double incomingPriceBtc; // "incoming_price_btc": <price_of_1_incoming_in_btc_currency_as_offered_by_service_as_float>,
private String xmrReceivingSubaddress; // <xmr_subaddress_user_needs_to_send_funds_to_as_string>, private String receivingSubaddress; // "receiving_subaddress": <xmr_subaddress_user_needs_to_send_funds_to_as_string>,
private String xmrReceivingAddress; // "xmr_receiving_address": "xmr_old_style_address_user_can_send_funds_to_as_string", private int recommendedMixin; // "recommended_mixin": <recommended_mixin_as_integer>,
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() { public QueryOrderStatus.State getState() {
return state; return state;
@@ -76,16 +70,8 @@ class QueryOrderStatusImpl implements QueryOrderStatus {
return uuid; return uuid;
} }
public int getBtcNumConfirmations() { public int getBtcNumConfirmationsThreshold() {
return btcNumConfirmations; return btcNumConfirmationsThreshold;
}
public int getBtcNumConfirmationsBeforePurge() {
return btcNumConfirmationsBeforePurge;
}
public String getBtcTransactionId() {
return btcTransactionId;
} }
public Date getCreatedAt() { public Date getCreatedAt() {
@@ -100,48 +86,28 @@ class QueryOrderStatusImpl implements QueryOrderStatus {
return secondsTillTimeout; return secondsTillTimeout;
} }
public double getXmrAmountTotal() { public double getIncomingAmountTotal() {
return xmrAmountTotal; return incomingAmountTotal;
} }
public double getXmrAmountRemaining() { public double getRemainingAmountIncoming() {
return xmrAmountRemaining; return remainingAmountIncoming;
} }
public int getXmrNumConfirmationsRemaining() { public int getIncomingNumConfirmationsRemaining() {
return xmrNumConfirmationsRemaining; return incomingNumConfirmationsRemaining;
} }
public double getXmrPriceBtc() { public double getIncomingPriceBtc() {
return xmrPriceBtc; return incomingPriceBtc;
} }
public String getXmrReceivingSubaddress() { public String getReceivingSubaddress() {
return xmrReceivingSubaddress; return receivingSubaddress;
} }
public String getXmrReceivingAddress() { public int getRecommendedMixin() {
return xmrReceivingAddress; return recommendedMixin;
}
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() { public boolean isCreated() {
@@ -197,29 +163,22 @@ class QueryOrderStatusImpl implements QueryOrderStatus {
uuid = jsonObject.getString("uuid"); // "uuid": "<unique_order_identifier_as_12_character_string>" uuid = jsonObject.getString("uuid"); // "uuid": "<unique_order_identifier_as_12_character_string>"
if (isCreated()) { if (isCreated()) {
btcNumConfirmations = jsonObject.getInt("btc_num_confirmations"); // "btc_num_confirmations": <btc_num_confirmations_as_integer>, btcNumConfirmationsThreshold = jsonObject.getInt("btc_num_confirmations_threshold");
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 { try {
String created = jsonObject.getString("created_at"); // "created_at": "<timestamp_as_string>", String created = jsonObject.getString("created_at");
createdAt = parseDate(created); createdAt = parseDate(created);
String expires = jsonObject.getString("expires_at"); // "expires_at": "<timestamp_as_string>", String expires = jsonObject.getString("expires_at");
expiresAt = parseDate(expires); expiresAt = parseDate(expires);
} catch (ParseException ex) { } catch (ParseException ex) {
throw new JSONException(ex.getLocalizedMessage()); throw new JSONException(ex.getLocalizedMessage());
} }
secondsTillTimeout = jsonObject.getInt("seconds_till_timeout"); // "seconds_till_timeout": <seconds_till_timeout_as_integer>, secondsTillTimeout = jsonObject.getInt("seconds_till_timeout");
xmrAmountTotal = jsonObject.getDouble("xmr_amount_total"); // "xmr_amount_total": <amount_in_xmr_for_this_order_as_float>, incomingAmountTotal = jsonObject.getDouble("incoming_amount_total");
xmrAmountRemaining = jsonObject.getDouble("xmr_amount_remaining"); // "xmr_amount_remaining": <amount_in_xmr_that_the_user_must_still_send_as_float>, remainingAmountIncoming = jsonObject.getDouble("remaining_amount_incoming");
xmrNumConfirmationsRemaining = jsonObject.getInt("xmr_num_confirmations_remaining"); // "xmr_num_confirmations_remaining": <num_xmr_confirmations_remaining_before_bitcoins_will_be_sent_as_integer>, incomingNumConfirmationsRemaining = jsonObject.getInt("incoming_num_confirmations_remaining");
xmrPriceBtc = jsonObject.getDouble("xmr_price_btc"); // "xmr_price_btc": <price_of_1_btc_in_xmr_as_offered_by_service_as_float>, incomingPriceBtc = jsonObject.getDouble("incoming_price_btc");
xmrReceivingSubaddress = jsonObject.getString("xmr_receiving_subaddress"); // <xmr_subaddress_user_needs_to_send_funds_to_as_string>, receivingSubaddress = jsonObject.getString("receiving_subaddress");
xmrReceivingAddress = jsonObject.getString("xmr_receiving_address"); // "xmr_receiving_address": "xmr_old_style_address_user_can_send_funds_to_as_string", recommendedMixin = jsonObject.getInt("recommended_mixin");
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"
} }
} }

View File

@@ -53,11 +53,6 @@ public class XmrToApiImpl implements XmrToApi, XmrToApiCall {
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
} }
public XmrToApiImpl(@NonNull final OkHttpClient okHttpClient) {
this(okHttpClient, HttpUrl.parse("https://xmr.to/api/v2/xmr2btc/"));
}
@Override @Override
public void createOrder(final double amount, @NonNull final String address, public void createOrder(final double amount, @NonNull final String address,
@NonNull final XmrToCallback<CreateOrder> callback) { @NonNull final XmrToCallback<CreateOrder> callback) {

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="8dp"
android:useLevel="false">
<solid android:color="@color/gradientOrange" />
</shape>
</item>
</layer-list>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="8dp"
android:useLevel="false">
<solid android:color="#CDD1D9" />
</shape>
</item>
</layer-list>

View File

@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="36dp"
android:height="36dp"
android:autoMirrored="true"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M22,3L7,3c-0.69,0 -1.23,0.35 -1.59,0.88L0,12l5.41,8.11c0.36,0.53 0.9,0.89 1.59,0.89h15c1.1,0 2,-0.9 2,-2L24,5c0,-1.1 -0.9,-2 -2,-2zM19,15.59L17.59,17 14,13.41 10.41,17 9,15.59 12.59,12 9,8.41 10.41,7 14,10.59 17.59,7 19,8.41 15.41,12 19,15.59z" />
</vector>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More