mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-04 17:28:42 +02:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
faf57c96fc | ||
![]() |
57ddddfce2 | ||
![]() |
ab6069058b | ||
![]() |
d94d6e6925 | ||
![]() |
4a819cc159 | ||
![]() |
f40c3d6c6d | ||
![]() |
82b25df7ad | ||
![]() |
08f815e830 | ||
![]() |
4e59be2dff | ||
![]() |
45c5883e11 | ||
![]() |
067a23e6a5 | ||
![]() |
d21fd41c44 |
@@ -1,14 +1,14 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 28
|
compileSdkVersion 29
|
||||||
buildToolsVersion '29.0.2'
|
buildToolsVersion '29.0.2'
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.m2049r.xmrwallet"
|
applicationId "com.m2049r.xmrwallet"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 28
|
targetSdkVersion 29
|
||||||
versionCode 503
|
versionCode 705
|
||||||
versionName "1.15.3 'Dark Fork'"
|
versionName "1.17.5.1 'Druk'"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
@@ -56,6 +56,9 @@ android {
|
|||||||
debug {
|
debug {
|
||||||
applicationIdSuffix ".debug"
|
applicationIdSuffix ".debug"
|
||||||
}
|
}
|
||||||
|
applicationVariants.all { variant ->
|
||||||
|
variant.buildConfigField "String", "ID_A", "\"" + getId("ID_A") + "\""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
@@ -109,20 +112,26 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getId(name) {
|
||||||
|
def Properties props = new Properties()
|
||||||
|
props.load(new FileInputStream(new File('monerujo.id')))
|
||||||
|
return props[name]
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.core:core:1.3.2'
|
implementation 'androidx.core:core:1.3.2'
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
implementation group: 'com.google.android.material', name: 'material', version: '1.3.0-alpha03'
|
implementation 'com.google.android.material:material:1.3.0'
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||||
implementation 'androidx.cardview:cardview:1.0.0'
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||||
|
|
||||||
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:4.9.0"
|
||||||
implementation "com.burgstaller:okhttp-digest:2.0"
|
implementation "com.burgstaller:okhttp-digest:2.1"
|
||||||
implementation "com.jakewharton.timber:timber:$rootProject.ext.timberVersion"
|
implementation "com.jakewharton.timber:timber:4.7.1"
|
||||||
|
|
||||||
implementation 'com.nulab-inc:zxcvbn:1.3.0'
|
implementation 'com.nulab-inc:zxcvbn:1.3.0'
|
||||||
|
|
||||||
@@ -131,12 +140,14 @@ dependencies {
|
|||||||
implementation 'org.slf4j:slf4j-nop:1.7.30'
|
implementation 'org.slf4j:slf4j-nop:1.7.30'
|
||||||
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
|
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
|
||||||
|
|
||||||
// https://mvnrepository.com/artifact/com.github.aelstad/keccakj
|
|
||||||
implementation 'com.github.aelstad:keccakj:1.1.0'
|
implementation 'com.github.aelstad:keccakj:1.1.0'
|
||||||
|
|
||||||
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
||||||
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
||||||
testImplementation "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
|
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.0"
|
||||||
testImplementation 'org.json:json:20180813'
|
testImplementation 'org.json:json:20180813'
|
||||||
testImplementation 'net.jodah:concurrentunit:0.4.4'
|
testImplementation 'net.jodah:concurrentunit:0.4.4'
|
||||||
|
|
||||||
|
compileOnly 'org.projectlombok:lombok:1.18.16'
|
||||||
|
annotationProcessor 'org.projectlombok:lombok:1.18.16'
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@
|
|||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
android:name=".XmrWalletApplication"
|
android:name=".XmrWalletApplication"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
@@ -185,7 +185,6 @@ public class Dispatcher implements PeerRetriever.OnGetPeers {
|
|||||||
|
|
||||||
public void seedPeers(Collection<NodeInfo> seedNodes) {
|
public void seedPeers(Collection<NodeInfo> seedNodes) {
|
||||||
for (NodeInfo node : seedNodes) {
|
for (NodeInfo node : seedNodes) {
|
||||||
node.clear();
|
|
||||||
if (node.isFavourite()) {
|
if (node.isFavourite()) {
|
||||||
rpcNodes.add(node);
|
rpcNodes.add(node);
|
||||||
if (listener != null) listener.onGet(node);
|
if (listener != null) listener.onGet(node);
|
||||||
|
@@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 m2049r
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.m2049r.levin.scanner;
|
package com.m2049r.levin.scanner;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
@@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017-2020 m2049r et al.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.m2049r.xmrwallet;
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
@@ -197,7 +213,7 @@ public class BaseActivity extends SecureActivity implements GenerateReviewFragme
|
|||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
Toast.makeText(this, getString(R.string.nfc_tag_read_undef), Toast.LENGTH_LONG).show();
|
Toast.makeText(this, getString(R.string.nfc_tag_read_undef), Toast.LENGTH_LONG).show();
|
||||||
} else {
|
} else {
|
||||||
BarcodeData bc = BarcodeData.fromQrCode(uri.toString());
|
BarcodeData bc = BarcodeData.fromString(uri.toString());
|
||||||
if (bc == null)
|
if (bc == null)
|
||||||
Toast.makeText(this, getString(R.string.nfc_tag_read_undef), Toast.LENGTH_LONG).show();
|
Toast.makeText(this, getString(R.string.nfc_tag_read_undef), Toast.LENGTH_LONG).show();
|
||||||
else
|
else
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -45,6 +45,7 @@ import com.m2049r.xmrwallet.layout.WalletInfoAdapter;
|
|||||||
import com.m2049r.xmrwallet.model.WalletManager;
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.util.KeyStoreHelper;
|
import com.m2049r.xmrwallet.util.KeyStoreHelper;
|
||||||
|
import com.m2049r.xmrwallet.util.NodePinger;
|
||||||
import com.m2049r.xmrwallet.util.Notice;
|
import com.m2049r.xmrwallet.util.Notice;
|
||||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
|
|
||||||
@@ -105,6 +106,8 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
|
|
||||||
Set<NodeInfo> getFavouriteNodes();
|
Set<NodeInfo> getFavouriteNodes();
|
||||||
|
|
||||||
|
Set<NodeInfo> getOrPopulateFavourites();
|
||||||
|
|
||||||
boolean hasLedger();
|
boolean hasLedger();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,11 +135,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
activityCallback.setTitle(null);
|
activityCallback.setTitle(null);
|
||||||
activityCallback.setToolbarButton(Toolbar.BUTTON_CREDITS);
|
activityCallback.setToolbarButton(Toolbar.BUTTON_CREDITS);
|
||||||
activityCallback.showNet();
|
activityCallback.showNet();
|
||||||
NodeInfo node = activityCallback.getNode();
|
pingSelectedNode();
|
||||||
if (node == null)
|
|
||||||
findBestNode();
|
|
||||||
else
|
|
||||||
showNode(node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -186,23 +185,10 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
|
|
||||||
pbNode = view.findViewById(R.id.pbNode);
|
pbNode = view.findViewById(R.id.pbNode);
|
||||||
llNode = view.findViewById(R.id.llNode);
|
llNode = view.findViewById(R.id.llNode);
|
||||||
llNode.setOnClickListener(new View.OnClickListener() {
|
llNode.setOnClickListener(v -> startNodePrefs());
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
if (activityCallback.getFavouriteNodes().isEmpty())
|
|
||||||
startNodePrefs();
|
|
||||||
else
|
|
||||||
findBestNode();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
tvNodeName = view.findViewById(R.id.tvNodeName);
|
tvNodeName = view.findViewById(R.id.tvNodeName);
|
||||||
tvNodeAddress = view.findViewById(R.id.tvNodeAddress);
|
tvNodeAddress = view.findViewById(R.id.tvNodeAddress);
|
||||||
view.findViewById(R.id.ibOption).setOnClickListener(new View.OnClickListener() {
|
view.findViewById(R.id.ibRenew).setOnClickListener(v -> findBestNode());
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
startNodePrefs();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Helper.hideKeyboard(getActivity());
|
Helper.hideKeyboard(getActivity());
|
||||||
|
|
||||||
@@ -421,32 +407,57 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void findBestNode() {
|
public void findBestNode() {
|
||||||
new AsyncFindBestNode().execute();
|
new AsyncFindBestNode().execute(AsyncFindBestNode.FIND_BEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AsyncFindBestNode extends AsyncTask<Void, Void, NodeInfo> {
|
public void pingSelectedNode() {
|
||||||
|
new AsyncFindBestNode().execute(AsyncFindBestNode.PING_SELECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private NodeInfo autoselect(Set<NodeInfo> nodes) {
|
||||||
|
if (nodes.isEmpty()) return null;
|
||||||
|
NodePinger.execute(nodes, null);
|
||||||
|
List<NodeInfo> nodeList = new ArrayList<>(nodes);
|
||||||
|
Collections.sort(nodeList, NodeInfo.BestNodeComparator);
|
||||||
|
return nodeList.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AsyncFindBestNode extends AsyncTask<Integer, Void, NodeInfo> {
|
||||||
|
final static int PING_SELECTED = 0;
|
||||||
|
final static int FIND_BEST = 1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPreExecute() {
|
protected void onPreExecute() {
|
||||||
super.onPreExecute();
|
super.onPreExecute();
|
||||||
pbNode.setVisibility(View.VISIBLE);
|
pbNode.setVisibility(View.VISIBLE);
|
||||||
llNode.setVisibility(View.INVISIBLE);
|
llNode.setVisibility(View.INVISIBLE);
|
||||||
activityCallback.setNode(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected NodeInfo doInBackground(Void... params) {
|
protected NodeInfo doInBackground(Integer... params) {
|
||||||
List<NodeInfo> nodesToTest = new ArrayList<>(activityCallback.getFavouriteNodes());
|
Set<NodeInfo> favourites = activityCallback.getOrPopulateFavourites();
|
||||||
Timber.d("testing best node from %d", nodesToTest.size());
|
NodeInfo selectedNode;
|
||||||
if (nodesToTest.isEmpty()) return null;
|
if (params[0] == FIND_BEST) {
|
||||||
for (NodeInfo node : nodesToTest) {
|
selectedNode = autoselect(favourites);
|
||||||
node.testRpcService(); // TODO: do this in parallel?
|
} else if (params[0] == PING_SELECTED) {
|
||||||
// no: it's better if it looks like it's doing something
|
selectedNode = activityCallback.getNode();
|
||||||
}
|
if (!activityCallback.getFavouriteNodes().contains(selectedNode))
|
||||||
Collections.sort(nodesToTest, NodeInfo.BestNodeComparator);
|
selectedNode = null; // it's not in the favourites (any longer)
|
||||||
NodeInfo bestNode = nodesToTest.get(0);
|
if (selectedNode == null)
|
||||||
if (bestNode.isValid()) {
|
for (NodeInfo node : favourites) {
|
||||||
activityCallback.setNode(bestNode);
|
if (node.isSelected()) {
|
||||||
return bestNode;
|
selectedNode = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (selectedNode == null) { // autoselect
|
||||||
|
selectedNode = autoselect(favourites);
|
||||||
|
} else
|
||||||
|
selectedNode.testRpcService();
|
||||||
|
} else throw new IllegalStateException();
|
||||||
|
if ((selectedNode != null) && selectedNode.isValid()) {
|
||||||
|
activityCallback.setNode(selectedNode);
|
||||||
|
return selectedNode;
|
||||||
} else {
|
} else {
|
||||||
activityCallback.setNode(null);
|
activityCallback.setNode(null);
|
||||||
return null;
|
return null;
|
||||||
@@ -462,17 +473,10 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
Timber.d("found a good node %s", result.toString());
|
Timber.d("found a good node %s", result.toString());
|
||||||
showNode(result);
|
showNode(result);
|
||||||
} else {
|
} else {
|
||||||
if (!activityCallback.getFavouriteNodes().isEmpty()) {
|
tvNodeName.setText(getResources().getText(R.string.node_create_hint));
|
||||||
tvNodeName.setText(getResources().getText(R.string.node_refresh_hint));
|
tvNodeName.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||||
tvNodeName.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_refresh_black_24dp, 0, 0, 0);
|
tvNodeAddress.setText(null);
|
||||||
tvNodeAddress.setText(null);
|
tvNodeAddress.setVisibility(View.GONE);
|
||||||
tvNodeAddress.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
tvNodeName.setText(getResources().getText(R.string.node_create_hint));
|
|
||||||
tvNodeName.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
|
||||||
tvNodeAddress.setText(null);
|
|
||||||
tvNodeAddress.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -485,12 +489,11 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
private void showNode(NodeInfo nodeInfo) {
|
private void showNode(NodeInfo nodeInfo) {
|
||||||
tvNodeName.setText(nodeInfo.getName());
|
tvNodeName.setText(nodeInfo.getName());
|
||||||
tvNodeName.setCompoundDrawablesWithIntrinsicBounds(NodeInfoAdapter.getPingIcon(nodeInfo), 0, 0, 0);
|
tvNodeName.setCompoundDrawablesWithIntrinsicBounds(NodeInfoAdapter.getPingIcon(nodeInfo), 0, 0, 0);
|
||||||
tvNodeAddress.setText(nodeInfo.getAddress());
|
Helper.showTimeDifference(tvNodeAddress, nodeInfo.getTimestamp());
|
||||||
tvNodeAddress.setVisibility(View.VISIBLE);
|
tvNodeAddress.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startNodePrefs() {
|
private void startNodePrefs() {
|
||||||
activityCallback.setNode(null);
|
|
||||||
activityCallback.onNodePrefs();
|
activityCallback.onNodePrefs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -56,7 +56,9 @@ import com.google.zxing.WriterException;
|
|||||||
import com.google.zxing.common.BitMatrix;
|
import com.google.zxing.common.BitMatrix;
|
||||||
import com.google.zxing.qrcode.QRCodeWriter;
|
import com.google.zxing.qrcode.QRCodeWriter;
|
||||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||||
|
import com.m2049r.xmrwallet.BuildConfig;
|
||||||
import com.m2049r.xmrwallet.data.BarcodeData;
|
import com.m2049r.xmrwallet.data.BarcodeData;
|
||||||
|
import com.m2049r.xmrwallet.data.Crypto;
|
||||||
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
|
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
|
||||||
import com.m2049r.xmrwallet.model.Wallet;
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
import com.m2049r.xmrwallet.model.WalletManager;
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
@@ -468,7 +470,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
Timber.d("CLEARQR");
|
Timber.d("CLEARQR");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bcData = new BarcodeData(BarcodeData.Asset.XMR, address, null, notes, xmrAmount);
|
bcData = new BarcodeData(Crypto.XMR, address, notes, xmrAmount);
|
||||||
int size = Math.max(ivQrCode.getWidth(), ivQrCode.getHeight());
|
int size = Math.max(ivQrCode.getWidth(), ivQrCode.getHeight());
|
||||||
Bitmap qr = generate(bcData.getUriString(), size, size);
|
Bitmap qr = generate(bcData.getUriString(), size, size);
|
||||||
if (qr != null) {
|
if (qr != null) {
|
||||||
@@ -610,6 +612,8 @@ public class ReceiveFragment extends Fragment {
|
|||||||
super.onPostExecute(result);
|
super.onPostExecute(result);
|
||||||
if (dialogOpened)
|
if (dialogOpened)
|
||||||
progressCallback.dismissProgressDialog();
|
progressCallback.dismissProgressDialog();
|
||||||
|
if (!isAdded()) // never mind then
|
||||||
|
return;
|
||||||
tvAddress.setText(newSubaddress);
|
tvAddress.setText(newSubaddress);
|
||||||
tvAddressLabel.setText(getString(R.string.generate_address_label_sub,
|
tvAddressLabel.setText(getString(R.string.generate_address_label_sub,
|
||||||
wallet.getNumSubaddresses() - 1));
|
wallet.getNumSubaddresses() - 1));
|
||||||
|
@@ -73,14 +73,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
|
|||||||
// * On older devices continuously stopping and resuming camera preview can result in freezing the app.
|
// * On older devices continuously stopping and resuming camera preview can result in freezing the app.
|
||||||
// * I don't know why this is the case but I don't have the time to figure out.
|
// * I don't know why this is the case but I don't have the time to figure out.
|
||||||
Handler handler = new Handler();
|
Handler handler = new Handler();
|
||||||
handler.postDelayed(new
|
handler.postDelayed(() -> mScannerView.resumeCameraPreview(ScannerFragment.this), 2000);
|
||||||
|
|
||||||
Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
mScannerView.resumeCameraPreview(ScannerFragment.this);
|
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -16,8 +16,11 @@
|
|||||||
|
|
||||||
package com.m2049r.xmrwallet;
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -25,6 +28,7 @@ import android.view.Menu;
|
|||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@@ -77,6 +81,9 @@ public class TxFragment extends Fragment {
|
|||||||
private TextView tvTxXmrToKey;
|
private TextView tvTxXmrToKey;
|
||||||
private TextView tvDestinationBtc;
|
private TextView tvDestinationBtc;
|
||||||
private TextView tvTxAmountBtc;
|
private TextView tvTxAmountBtc;
|
||||||
|
private TextView tvXmrToSupport;
|
||||||
|
private TextView tvXmrToKeyLabel;
|
||||||
|
private ImageView tvXmrToLogo;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
@@ -88,6 +95,9 @@ public class TxFragment extends Fragment {
|
|||||||
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
|
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
|
||||||
tvDestinationBtc = view.findViewById(R.id.tvDestinationBtc);
|
tvDestinationBtc = view.findViewById(R.id.tvDestinationBtc);
|
||||||
tvTxAmountBtc = view.findViewById(R.id.tvTxAmountBtc);
|
tvTxAmountBtc = view.findViewById(R.id.tvTxAmountBtc);
|
||||||
|
tvXmrToSupport = view.findViewById(R.id.tvXmrToSupport);
|
||||||
|
tvXmrToKeyLabel = view.findViewById(R.id.tvXmrToKeyLabel);
|
||||||
|
tvXmrToLogo = view.findViewById(R.id.tvXmrToLogo);
|
||||||
|
|
||||||
tvAccount = view.findViewById(R.id.tvAccount);
|
tvAccount = view.findViewById(R.id.tvAccount);
|
||||||
tvAddress = view.findViewById(R.id.tvAddress);
|
tvAddress = view.findViewById(R.id.tvAddress);
|
||||||
@@ -104,12 +114,9 @@ public class TxFragment extends Fragment {
|
|||||||
|
|
||||||
etTxNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
|
etTxNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
|
||||||
|
|
||||||
tvTxXmrToKey.setOnClickListener(new View.OnClickListener() {
|
tvTxXmrToKey.setOnClickListener(v -> {
|
||||||
@Override
|
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
|
||||||
public void onClick(View v) {
|
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
|
||||||
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
|
|
||||||
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Bundle args = getArguments();
|
Bundle args = getArguments();
|
||||||
@@ -283,12 +290,36 @@ public class TxFragment extends Fragment {
|
|||||||
showBtcInfo();
|
showBtcInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
void showBtcInfo() {
|
void showBtcInfo() {
|
||||||
if (userNotes.xmrtoKey != null) {
|
if (userNotes.xmrtoKey != null) {
|
||||||
cvXmrTo.setVisibility(View.VISIBLE);
|
cvXmrTo.setVisibility(View.VISIBLE);
|
||||||
tvTxXmrToKey.setText(userNotes.xmrtoKey);
|
String key = userNotes.xmrtoKey;
|
||||||
|
if ("xmrto".equals(userNotes.xmrtoTag)) { // legacy xmr.to service :(
|
||||||
|
key = "xmrto-" + key;
|
||||||
|
}
|
||||||
|
tvTxXmrToKey.setText(key);
|
||||||
tvDestinationBtc.setText(userNotes.xmrtoDestination);
|
tvDestinationBtc.setText(userNotes.xmrtoDestination);
|
||||||
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " BTC");
|
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " "+ userNotes.xmrtoCurrency);
|
||||||
|
switch (userNotes.xmrtoTag) {
|
||||||
|
case "xmrto":
|
||||||
|
tvXmrToSupport.setVisibility(View.GONE);
|
||||||
|
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
|
||||||
|
tvXmrToLogo.setImageResource(R.drawable.ic_xmrto_logo);
|
||||||
|
break;
|
||||||
|
case "side": // defaults in layout - just add underline
|
||||||
|
tvXmrToSupport.setPaintFlags(tvXmrToSupport.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
||||||
|
tvXmrToSupport.setOnClickListener(v -> {
|
||||||
|
Uri uri = Uri.parse("https://sideshift.ai/orders/" + userNotes.xmrtoKey);
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
tvXmrToSupport.setVisibility(View.GONE);
|
||||||
|
tvXmrToKeyLabel.setVisibility(View.INVISIBLE);
|
||||||
|
tvXmrToLogo.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cvXmrTo.setVisibility(View.GONE);
|
cvXmrTo.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
@@ -579,11 +579,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
|||||||
});
|
});
|
||||||
if (numAccounts != wallet.getNumAccounts()) {
|
if (numAccounts != wallet.getNumAccounts()) {
|
||||||
numAccounts = wallet.getNumAccounts();
|
numAccounts = wallet.getNumAccounts();
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(this::updateAccountsList);
|
||||||
public void run() {
|
|
||||||
updateAccountsList();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final WalletFragment walletFragment = (WalletFragment)
|
final WalletFragment walletFragment = (WalletFragment)
|
||||||
@@ -925,7 +921,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
|||||||
@Override
|
@Override
|
||||||
public boolean onScanned(String qrCode) {
|
public boolean onScanned(String qrCode) {
|
||||||
// #gurke
|
// #gurke
|
||||||
BarcodeData bcData = BarcodeData.fromQrCode(qrCode);
|
BarcodeData bcData = BarcodeData.fromString(qrCode);
|
||||||
if (bcData != null) {
|
if (bcData != null) {
|
||||||
popFragmentStack(null);
|
popFragmentStack(null);
|
||||||
Timber.d("AAA");
|
Timber.d("AAA");
|
||||||
@@ -1055,21 +1051,23 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateAccountsList() {
|
void updateAccountsList() {
|
||||||
final Wallet wallet = getWallet();
|
|
||||||
Menu menu = accountsView.getMenu();
|
Menu menu = accountsView.getMenu();
|
||||||
menu.removeGroup(R.id.accounts_list);
|
menu.removeGroup(R.id.accounts_list);
|
||||||
final int n = wallet.getNumAccounts();
|
final Wallet wallet = getWallet();
|
||||||
final boolean showBalances = (n > 1) && !isStreetMode();
|
if (wallet != null) {
|
||||||
for (int i = 0; i < n; i++) {
|
final int n = wallet.getNumAccounts();
|
||||||
final String label = (showBalances ?
|
final boolean showBalances = (n > 1) && !isStreetMode();
|
||||||
getString(R.string.label_account, wallet.getAccountLabel(i), Helper.getDisplayAmount(wallet.getBalance(i), 2))
|
for (int i = 0; i < n; i++) {
|
||||||
: wallet.getAccountLabel(i));
|
final String label = (showBalances ?
|
||||||
final MenuItem item = menu.add(R.id.accounts_list, getAccountId(i), 2 * i, label);
|
getString(R.string.label_account, wallet.getAccountLabel(i), Helper.getDisplayAmount(wallet.getBalance(i), 2))
|
||||||
item.setIcon(R.drawable.ic_account_balance_wallet_black_24dp);
|
: wallet.getAccountLabel(i));
|
||||||
if (i == wallet.getAccountIndex())
|
final MenuItem item = menu.add(R.id.accounts_list, getAccountId(i), 2 * i, label);
|
||||||
item.setChecked(true);
|
item.setIcon(R.drawable.ic_account_balance_wallet_black_24dp);
|
||||||
|
if (i == wallet.getAccountIndex())
|
||||||
|
item.setChecked(true);
|
||||||
|
}
|
||||||
|
menu.setGroupCheckable(R.id.accounts_list, true, true);
|
||||||
}
|
}
|
||||||
menu.setGroupCheckable(R.id.accounts_list, true, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -49,6 +49,7 @@ 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 com.m2049r.xmrwallet.widget.Toolbar;
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
@@ -242,7 +243,7 @@ public class WalletFragment extends Fragment
|
|||||||
String balanceCurrency = Helper.BASE_CRYPTO;
|
String balanceCurrency = Helper.BASE_CRYPTO;
|
||||||
double balanceRate = 1.0;
|
double balanceRate = 1.0;
|
||||||
|
|
||||||
private final ExchangeApi exchangeApi = Helper.getExchangeApi();
|
private final ExchangeApi exchangeApi = ServiceHelper.getExchangeApi();
|
||||||
|
|
||||||
void refreshBalance() {
|
void refreshBalance() {
|
||||||
double unconfirmedXmr = Helper.getDecimalAmount(balance - unlockedBalance).doubleValue();
|
double unconfirmedXmr = Helper.getDecimalAmount(balance - unlockedBalance).doubleValue();
|
||||||
|
@@ -16,14 +16,13 @@
|
|||||||
|
|
||||||
package com.m2049r.xmrwallet;
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.BuildConfig;
|
||||||
import com.m2049r.xmrwallet.model.NetworkType;
|
import com.m2049r.xmrwallet.model.NetworkType;
|
||||||
import com.m2049r.xmrwallet.util.DayNightMode;
|
|
||||||
import com.m2049r.xmrwallet.util.LocaleHelper;
|
import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||||
import com.m2049r.xmrwallet.util.NightmodeHelper;
|
import com.m2049r.xmrwallet.util.NightmodeHelper;
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
89
app/src/main/java/com/m2049r/xmrwallet/data/Crypto.java
Normal file
89
app/src/main/java/com/m2049r/xmrwallet/data/Crypto.java
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package com.m2049r.xmrwallet.data;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.R;
|
||||||
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
|
import com.m2049r.xmrwallet.util.validator.BitcoinAddressType;
|
||||||
|
import com.m2049r.xmrwallet.util.validator.BitcoinAddressValidator;
|
||||||
|
import com.m2049r.xmrwallet.util.validator.EthAddressValidator;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public enum Crypto {
|
||||||
|
XMR("XMR", true, "monero:tx_amount:recipient_name:tx_description", R.id.ibXMR, R.drawable.ic_monero, R.drawable.ic_monero_bw, Wallet::isAddressValid),
|
||||||
|
BTC("BTC", true, "bitcoin:amount:label:message", R.id.ibBTC, R.drawable.ic_xmrto_btc, R.drawable.ic_xmrto_btc_off, address -> {
|
||||||
|
return BitcoinAddressValidator.validate(address, BitcoinAddressType.BTC);
|
||||||
|
}),
|
||||||
|
DASH("DASH", true, "dash:amount:label:message", R.id.ibDASH, R.drawable.ic_xmrto_dash, R.drawable.ic_xmrto_dash_off, address -> {
|
||||||
|
return BitcoinAddressValidator.validate(address, BitcoinAddressType.DASH);
|
||||||
|
}),
|
||||||
|
DOGE("DOGE", true, "dogecoin:amount:label:message", R.id.ibDOGE, R.drawable.ic_xmrto_doge, R.drawable.ic_xmrto_doge_off, address -> {
|
||||||
|
return BitcoinAddressValidator.validate(address, BitcoinAddressType.DOGE);
|
||||||
|
}),
|
||||||
|
ETH("ETH", false, "ethereum:amount:label:message", R.id.ibETH, R.drawable.ic_xmrto_eth, R.drawable.ic_xmrto_eth_off, EthAddressValidator::validate),
|
||||||
|
LTC("LTC", true, "litecoin:amount:label:message", R.id.ibLTC, R.drawable.ic_xmrto_ltc, R.drawable.ic_xmrto_ltc_off, address -> {
|
||||||
|
return BitcoinAddressValidator.validate(address, BitcoinAddressType.LTC);
|
||||||
|
});
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@NonNull
|
||||||
|
private final String symbol;
|
||||||
|
@Getter
|
||||||
|
private final boolean casefull;
|
||||||
|
@NonNull
|
||||||
|
private final String uriSpec;
|
||||||
|
@Getter
|
||||||
|
private final int buttonId;
|
||||||
|
@Getter
|
||||||
|
private final int iconEnabledId;
|
||||||
|
@Getter
|
||||||
|
private final int iconDisabledId;
|
||||||
|
@NonNull
|
||||||
|
private final Validator validator;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Crypto withScheme(@NonNull String scheme) {
|
||||||
|
for (Crypto crypto : values()) {
|
||||||
|
if (crypto.getUriScheme().equals(scheme)) return crypto;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Crypto withSymbol(@NonNull String symbol) {
|
||||||
|
final String upperSymbol = symbol.toUpperCase();
|
||||||
|
for (Crypto crypto : values()) {
|
||||||
|
if (crypto.symbol.equals(upperSymbol)) return crypto;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Validator {
|
||||||
|
boolean validate(String address);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO maybe cache these segments
|
||||||
|
String getUriScheme() {
|
||||||
|
return uriSpec.split(":")[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
String getUriAmount() {
|
||||||
|
return uriSpec.split(":")[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
String getUriLabel() {
|
||||||
|
return uriSpec.split(":")[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
String getUriMessage() {
|
||||||
|
return uriSpec.split(":")[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean validate(String address) {
|
||||||
|
return validator.validate(address);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 m2049r
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet.data;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
// Nodes stolen from https://moneroworld.com/#nodes
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum DefaultNodes {
|
||||||
|
MONERUJO("nodex.monerujo.io:18081"),
|
||||||
|
XMRTO("node.xmr.to:18081"),
|
||||||
|
SUPPORTXMR("node.supportxmr.com:18081"),
|
||||||
|
HASHVAULT("nodes.hashvault.pro:18081"),
|
||||||
|
MONEROWORLD("node.moneroworld.com:18089"),
|
||||||
|
XMRTW("opennode.xmr-tw.org:18089");
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final String uri;
|
||||||
|
}
|
@@ -26,6 +26,8 @@ import java.net.URLDecoder;
|
|||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class Node {
|
public class Node {
|
||||||
@@ -33,15 +35,29 @@ public class Node {
|
|||||||
static public final String STAGENET = "stagenet";
|
static public final String STAGENET = "stagenet";
|
||||||
static public final String TESTNET = "testnet";
|
static public final String TESTNET = "testnet";
|
||||||
|
|
||||||
|
@Getter
|
||||||
private String name = null;
|
private String name = null;
|
||||||
|
@Getter
|
||||||
final private NetworkType networkType;
|
final private NetworkType networkType;
|
||||||
InetAddress hostAddress;
|
InetAddress hostAddress;
|
||||||
|
@Getter
|
||||||
private String host;
|
private String host;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
int rpcPort = 0;
|
int rpcPort = 0;
|
||||||
private int levinPort = 0;
|
private int levinPort = 0;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
private String username = "";
|
private String username = "";
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
private String password = "";
|
private String password = "";
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
private boolean favourite = false;
|
private boolean favourite = false;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private boolean selected = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
@@ -193,7 +209,6 @@ public class Node {
|
|||||||
this.levinPort = socketAddress.getPort();
|
this.levinPort = socketAddress.getPort();
|
||||||
this.username = "";
|
this.username = "";
|
||||||
this.password = "";
|
this.password = "";
|
||||||
//this.name = socketAddress.getHostName(); // triggers DNS so we don't do it by default
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAddress() {
|
public String getAddress() {
|
||||||
@@ -204,14 +219,6 @@ public class Node {
|
|||||||
return hostAddress.getHostAddress();
|
return hostAddress.getHostAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getHost() {
|
|
||||||
return host;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRpcPort() {
|
|
||||||
return rpcPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHost(String host) throws UnknownHostException {
|
public void setHost(String host) throws UnknownHostException {
|
||||||
if ((host == null) || (host.isEmpty()))
|
if ((host == null) || (host.isEmpty()))
|
||||||
throw new UnknownHostException("loopback not supported (yet?)");
|
throw new UnknownHostException("loopback not supported (yet?)");
|
||||||
@@ -219,18 +226,6 @@ public class Node {
|
|||||||
this.hostAddress = InetAddress.getByName(host);
|
this.hostAddress = InetAddress.getByName(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUsername(String user) {
|
|
||||||
username = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPassword(String pass) {
|
|
||||||
password = pass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRpcPort(int port) {
|
|
||||||
this.rpcPort = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName() {
|
public void setName() {
|
||||||
if (name == null)
|
if (name == null)
|
||||||
this.name = hostAddress.getHostName();
|
this.name = hostAddress.getHostName();
|
||||||
@@ -243,30 +238,6 @@ public class Node {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetworkType getNetworkType() {
|
|
||||||
return networkType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPassword() {
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFavourite() {
|
|
||||||
return favourite;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFavourite(boolean favourite) {
|
|
||||||
this.favourite = favourite;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void toggleFavourite() {
|
public void toggleFavourite() {
|
||||||
favourite = !favourite;
|
favourite = !favourite;
|
||||||
}
|
}
|
||||||
|
@@ -21,8 +21,8 @@ import com.burgstaller.okhttp.CachingAuthenticatorDecorator;
|
|||||||
import com.burgstaller.okhttp.digest.CachingAuthenticator;
|
import com.burgstaller.okhttp.digest.CachingAuthenticator;
|
||||||
import com.burgstaller.okhttp.digest.Credentials;
|
import com.burgstaller.okhttp.digest.Credentials;
|
||||||
import com.burgstaller.okhttp.digest.DigestAuthenticator;
|
import com.burgstaller.okhttp.digest.DigestAuthenticator;
|
||||||
import com.m2049r.levin.scanner.Dispatcher;
|
|
||||||
import com.m2049r.levin.scanner.LevinPeer;
|
import com.m2049r.levin.scanner.LevinPeer;
|
||||||
|
import com.m2049r.xmrwallet.util.NodePinger;
|
||||||
import com.m2049r.xmrwallet.util.OkHttpHelper;
|
import com.m2049r.xmrwallet.util.OkHttpHelper;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@@ -36,6 +36,8 @@ import java.util.Comparator;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
import okhttp3.MediaType;
|
import okhttp3.MediaType;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
@@ -46,14 +48,24 @@ import okhttp3.ResponseBody;
|
|||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class NodeInfo extends Node {
|
public class NodeInfo extends Node {
|
||||||
final static public int MIN_MAJOR_VERSION = 11;
|
final static public int MIN_MAJOR_VERSION = 14;
|
||||||
final static public String RPC_VERSION = "2.0";
|
final static public String RPC_VERSION = "2.0";
|
||||||
|
|
||||||
|
@Getter
|
||||||
private long height = 0;
|
private long height = 0;
|
||||||
|
@Getter
|
||||||
private long timestamp = 0;
|
private long timestamp = 0;
|
||||||
|
@Getter
|
||||||
private int majorVersion = 0;
|
private int majorVersion = 0;
|
||||||
|
@Getter
|
||||||
private double responseTime = Double.MAX_VALUE;
|
private double responseTime = Double.MAX_VALUE;
|
||||||
|
@Getter
|
||||||
private int responseCode = 0;
|
private int responseCode = 0;
|
||||||
|
@Getter
|
||||||
|
private boolean tested = false;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private boolean selecting = false;
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
height = 0;
|
height = 0;
|
||||||
@@ -61,13 +73,13 @@ public class NodeInfo extends Node {
|
|||||||
responseTime = Double.MAX_VALUE;
|
responseTime = Double.MAX_VALUE;
|
||||||
responseCode = 0;
|
responseCode = 0;
|
||||||
timestamp = 0;
|
timestamp = 0;
|
||||||
|
tested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public NodeInfo fromString(String nodeString) {
|
static public NodeInfo fromString(String nodeString) {
|
||||||
try {
|
try {
|
||||||
return new NodeInfo(nodeString);
|
return new NodeInfo(nodeString);
|
||||||
} catch (IllegalArgumentException ex) {
|
} catch (IllegalArgumentException ex) {
|
||||||
Timber.w(ex);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,26 +125,6 @@ public class NodeInfo extends Node {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getHeight() {
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getTimestamp() {
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMajorVersion() {
|
|
||||||
return majorVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getResponseTime() {
|
|
||||||
return responseTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getResponseCode() {
|
|
||||||
return responseCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSuccessful() {
|
public boolean isSuccessful() {
|
||||||
return (responseCode >= 200) && (responseCode < 300);
|
return (responseCode >= 200) && (responseCode < 300);
|
||||||
}
|
}
|
||||||
@@ -145,23 +137,20 @@ public class NodeInfo extends Node {
|
|||||||
return isSuccessful() && (majorVersion >= MIN_MAJOR_VERSION) && (responseTime < Double.MAX_VALUE);
|
return isSuccessful() && (majorVersion >= MIN_MAJOR_VERSION) && (responseTime < Double.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static public Comparator<NodeInfo> BestNodeComparator = new Comparator<NodeInfo>() {
|
static public Comparator<NodeInfo> BestNodeComparator = (o1, o2) -> {
|
||||||
@Override
|
if (o1.isValid()) {
|
||||||
public int compare(NodeInfo o1, NodeInfo o2) {
|
if (o2.isValid()) { // both are valid
|
||||||
if (o1.isValid()) {
|
// higher node wins
|
||||||
if (o2.isValid()) { // both are valid
|
int heightDiff = (int) (o2.height - o1.height);
|
||||||
// higher node wins
|
if (heightDiff != 0)
|
||||||
int heightDiff = (int) (o2.height - o1.height);
|
return heightDiff;
|
||||||
if (Math.abs(heightDiff) > Dispatcher.HEIGHT_WINDOW)
|
// if they are equal, faster node wins
|
||||||
return heightDiff;
|
return (int) Math.signum(o1.responseTime - o2.responseTime);
|
||||||
// if they are (nearly) equal, faster node wins
|
|
||||||
return (int) Math.signum(o1.responseTime - o2.responseTime);
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return 1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -192,7 +181,7 @@ public class NodeInfo extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final int HTTP_TIMEOUT = OkHttpHelper.HTTP_TIMEOUT;
|
private static final int HTTP_TIMEOUT = OkHttpHelper.HTTP_TIMEOUT;
|
||||||
public static final double PING_GOOD = HTTP_TIMEOUT / 3; //ms
|
public static final double PING_GOOD = HTTP_TIMEOUT / 3.0; //ms
|
||||||
public static final double PING_MEDIUM = 2 * PING_GOOD; //ms
|
public static final double PING_MEDIUM = 2 * PING_GOOD; //ms
|
||||||
public static final double PING_BAD = HTTP_TIMEOUT;
|
public static final double PING_BAD = HTTP_TIMEOUT;
|
||||||
|
|
||||||
@@ -200,7 +189,15 @@ public class NodeInfo extends Node {
|
|||||||
return testRpcService(rpcPort);
|
return testRpcService(rpcPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean testRpcService(NodePinger.Listener listener) {
|
||||||
|
boolean result = testRpcService(rpcPort);
|
||||||
|
if (listener != null)
|
||||||
|
listener.publish(this);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean testRpcService(int port) {
|
private boolean testRpcService(int port) {
|
||||||
|
Timber.d("Testing %s", toNodeString());
|
||||||
clear();
|
clear();
|
||||||
try {
|
try {
|
||||||
OkHttpClient client = OkHttpHelper.getEagerClient();
|
OkHttpClient client = OkHttpHelper.getEagerClient();
|
||||||
@@ -247,8 +244,9 @@ public class NodeInfo extends Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException | JSONException ex) {
|
} catch (IOException | JSONException ex) {
|
||||||
// failure
|
Timber.d(ex);
|
||||||
Timber.d(ex.getMessage());
|
} finally {
|
||||||
|
tested = true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,7 @@ import android.os.Parcel;
|
|||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||||
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/2139134/how-to-send-an-object-from-one-android-activity-to-another-using-intents
|
// https://stackoverflow.com/questions/2139134/how-to-send-an-object-from-one-android-activity-to-another-using-intents
|
||||||
public class TxData implements Parcelable {
|
public class TxData implements Parcelable {
|
||||||
@@ -52,6 +53,10 @@ public class TxData implements Parcelable {
|
|||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getAmountAsDouble() {
|
||||||
|
return 1.0 * amount / 1000000000000L;
|
||||||
|
}
|
||||||
|
|
||||||
public int getMixin() {
|
public int getMixin() {
|
||||||
return mixin;
|
return mixin;
|
||||||
}
|
}
|
||||||
@@ -68,6 +73,10 @@ public class TxData implements Parcelable {
|
|||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAmount(double amount) {
|
||||||
|
this.amount = Wallet.getAmountFromDouble(amount);
|
||||||
|
}
|
||||||
|
|
||||||
public void setMixin(int mixin) {
|
public void setMixin(int mixin) {
|
||||||
this.mixin = mixin;
|
this.mixin = mixin;
|
||||||
}
|
}
|
||||||
|
@@ -18,11 +18,23 @@ package com.m2049r.xmrwallet.data;
|
|||||||
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
|
|
||||||
public class TxDataBtc extends TxData {
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
private String xmrtoUuid;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
public class TxDataBtc extends TxData {
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private String btcSymbol; // the actual non-XMR thing we're sending
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private String xmrtoOrderId; // shown in success screen
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
private String btcAddress;
|
private String btcAddress;
|
||||||
private String bip70;
|
@Getter
|
||||||
|
@Setter
|
||||||
private double btcAmount;
|
private double btcAmount;
|
||||||
|
|
||||||
public TxDataBtc() {
|
public TxDataBtc() {
|
||||||
@@ -33,44 +45,12 @@ public class TxDataBtc extends TxData {
|
|||||||
super(txDataBtc);
|
super(txDataBtc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getXmrtoUuid() {
|
|
||||||
return xmrtoUuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setXmrtoUuid(String xmrtoUuid) {
|
|
||||||
this.xmrtoUuid = xmrtoUuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBtcAddress() {
|
|
||||||
return btcAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBtcAddress(String btcAddress) {
|
|
||||||
this.btcAddress = btcAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBip70() {
|
|
||||||
return bip70;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBip70(String bip70) {
|
|
||||||
this.bip70 = bip70;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getBtcAmount() {
|
|
||||||
return btcAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBtcAmount(double btcAmount) {
|
|
||||||
this.btcAmount = btcAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel out, int flags) {
|
public void writeToParcel(Parcel out, int flags) {
|
||||||
super.writeToParcel(out, flags);
|
super.writeToParcel(out, flags);
|
||||||
out.writeString(xmrtoUuid);
|
out.writeString(btcSymbol);
|
||||||
|
out.writeString(xmrtoOrderId);
|
||||||
out.writeString(btcAddress);
|
out.writeString(btcAddress);
|
||||||
out.writeString(bip70);
|
|
||||||
out.writeDouble(btcAmount);
|
out.writeDouble(btcAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,23 +67,35 @@ public class TxDataBtc extends TxData {
|
|||||||
|
|
||||||
protected TxDataBtc(Parcel in) {
|
protected TxDataBtc(Parcel in) {
|
||||||
super(in);
|
super(in);
|
||||||
xmrtoUuid = in.readString();
|
btcSymbol = in.readString();
|
||||||
|
xmrtoOrderId = in.readString();
|
||||||
btcAddress = in.readString();
|
btcAddress = in.readString();
|
||||||
bip70 = in.readString();
|
|
||||||
btcAmount = in.readDouble();
|
btcAmount = in.readDouble();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(",xmrtoUuid:");
|
sb.append("xmrtoOrderId:");
|
||||||
sb.append(xmrtoUuid);
|
sb.append(xmrtoOrderId);
|
||||||
|
sb.append(",btcSymbol:");
|
||||||
|
sb.append(btcSymbol);
|
||||||
sb.append(",btcAddress:");
|
sb.append(",btcAddress:");
|
||||||
sb.append(btcAddress);
|
sb.append(btcAddress);
|
||||||
sb.append(",bip70:");
|
|
||||||
sb.append(bip70);
|
|
||||||
sb.append(",btcAmount:");
|
sb.append(",btcAmount:");
|
||||||
sb.append(btcAmount);
|
sb.append(btcAmount);
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean validateAddress(@NonNull String address) {
|
||||||
|
if ((btcSymbol == null) || (btcAddress == null)) return false;
|
||||||
|
final Crypto crypto = Crypto.withSymbol(btcSymbol);
|
||||||
|
if (crypto == null) return false;
|
||||||
|
if (crypto.isCasefull()) { // compare as-is
|
||||||
|
return address.equals(btcAddress);
|
||||||
|
} else { // normalize & compare (e.g. ETH with and without checksum capitals
|
||||||
|
return address.toLowerCase().equals(btcAddress.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,18 +16,19 @@
|
|||||||
|
|
||||||
package com.m2049r.xmrwallet.data;
|
package com.m2049r.xmrwallet.data;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.xmrto.api.QueryOrderStatus;
|
import com.m2049r.xmrwallet.service.shift.sideshift.api.CreateOrder;
|
||||||
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
public class UserNotes {
|
public class UserNotes {
|
||||||
public String txNotes = "";
|
public String txNotes = "";
|
||||||
public String note = "";
|
public String note = "";
|
||||||
|
public String xmrtoTag = null;
|
||||||
public String xmrtoKey = null;
|
public String xmrtoKey = null;
|
||||||
public String xmrtoAmount = null; // could be a double - but we are not doing any calculations
|
public String xmrtoAmount = null; // could be a double - but we are not doing any calculations
|
||||||
|
public String xmrtoCurrency = null;
|
||||||
public String xmrtoDestination = null;
|
public String xmrtoDestination = null;
|
||||||
|
|
||||||
public UserNotes(final String txNotes) {
|
public UserNotes(final String txNotes) {
|
||||||
@@ -35,13 +36,15 @@ public class UserNotes {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.txNotes = txNotes;
|
this.txNotes = txNotes;
|
||||||
Pattern p = Pattern.compile("^\\{(xmrto-\\w{6}),([0-9.]*)BTC,(\\w*)\\} ?(.*)");
|
Pattern p = Pattern.compile("^\\{([a-z]+)-(\\w{6,}),([0-9.]*)([A-Z]+),(\\w*)\\} ?(.*)");
|
||||||
Matcher m = p.matcher(txNotes);
|
Matcher m = p.matcher(txNotes);
|
||||||
if (m.find()) {
|
if (m.find()) {
|
||||||
xmrtoKey = m.group(1);
|
xmrtoTag = m.group(1);
|
||||||
xmrtoAmount = m.group(2);
|
xmrtoKey = m.group(2);
|
||||||
xmrtoDestination = m.group(3);
|
xmrtoAmount = m.group(3);
|
||||||
note = m.group(4);
|
xmrtoCurrency = m.group(4);
|
||||||
|
xmrtoDestination = m.group(5);
|
||||||
|
note = m.group(6);
|
||||||
} else {
|
} else {
|
||||||
note = txNotes;
|
note = txNotes;
|
||||||
}
|
}
|
||||||
@@ -56,12 +59,15 @@ public class UserNotes {
|
|||||||
txNotes = buildTxNote();
|
txNotes = buildTxNote();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setXmrtoStatus(QueryOrderStatus xmrtoStatus) {
|
public void setXmrtoOrder(CreateOrder order) {
|
||||||
if (xmrtoStatus != null) {
|
if (order != null) {
|
||||||
xmrtoKey = xmrtoStatus.getUuid();
|
xmrtoTag = order.TAG;
|
||||||
xmrtoAmount = String.valueOf(xmrtoStatus.getBtcAmount());
|
xmrtoKey = order.getOrderId();
|
||||||
xmrtoDestination = xmrtoStatus.getBtcDestAddress();
|
xmrtoAmount = Helper.getDisplayAmount(order.getBtcAmount());
|
||||||
|
xmrtoCurrency = order.getBtcCurrency();
|
||||||
|
xmrtoDestination = order.getBtcAddress();
|
||||||
} else {
|
} else {
|
||||||
|
xmrtoTag = null;
|
||||||
xmrtoKey = null;
|
xmrtoKey = null;
|
||||||
xmrtoAmount = null;
|
xmrtoAmount = null;
|
||||||
xmrtoDestination = null;
|
xmrtoDestination = null;
|
||||||
@@ -70,15 +76,18 @@ public class UserNotes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String buildTxNote() {
|
private String buildTxNote() {
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuilder sb = new StringBuilder();
|
||||||
if (xmrtoKey != null) {
|
if (xmrtoKey != null) {
|
||||||
if ((xmrtoAmount == null) || (xmrtoDestination == null))
|
if ((xmrtoAmount == null) || (xmrtoDestination == null))
|
||||||
throw new IllegalArgumentException("Broken notes");
|
throw new IllegalArgumentException("Broken notes");
|
||||||
sb.append("{");
|
sb.append("{");
|
||||||
|
sb.append(xmrtoTag);
|
||||||
|
sb.append("-");
|
||||||
sb.append(xmrtoKey);
|
sb.append(xmrtoKey);
|
||||||
sb.append(",");
|
sb.append(",");
|
||||||
sb.append(xmrtoAmount);
|
sb.append(xmrtoAmount);
|
||||||
sb.append("BTC,");
|
sb.append(xmrtoCurrency);
|
||||||
|
sb.append(",");
|
||||||
sb.append(xmrtoDestination);
|
sb.append(xmrtoDestination);
|
||||||
sb.append("}");
|
sb.append("}");
|
||||||
if ((note != null) && (!note.isEmpty()))
|
if ((note != null) && (!note.isEmpty()))
|
||||||
|
@@ -27,7 +27,6 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.BuildConfig;
|
|
||||||
import com.m2049r.xmrwallet.R;
|
import com.m2049r.xmrwallet.R;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -42,9 +42,8 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
|||||||
|
|
||||||
Listener sendListener;
|
Listener sendListener;
|
||||||
|
|
||||||
public SendAmountWizardFragment setSendListener(Listener listener) {
|
public void setSendListener(Listener listener) {
|
||||||
this.sendListener = listener;
|
this.sendListener = listener;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Listener {
|
interface Listener {
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package com.m2049r.xmrwallet.fragment.send;
|
package com.m2049r.xmrwallet.fragment.send;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -27,15 +30,17 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.R;
|
import com.m2049r.xmrwallet.R;
|
||||||
|
import com.m2049r.xmrwallet.data.Crypto;
|
||||||
import com.m2049r.xmrwallet.data.PendingTx;
|
import com.m2049r.xmrwallet.data.PendingTx;
|
||||||
import com.m2049r.xmrwallet.data.TxDataBtc;
|
import com.m2049r.xmrwallet.data.TxDataBtc;
|
||||||
|
import com.m2049r.xmrwallet.service.shift.ShiftCallback;
|
||||||
|
import com.m2049r.xmrwallet.service.shift.ShiftException;
|
||||||
|
import com.m2049r.xmrwallet.service.shift.sideshift.api.QueryOrderStatus;
|
||||||
|
import com.m2049r.xmrwallet.service.shift.sideshift.api.SideShiftApi;
|
||||||
|
import com.m2049r.xmrwallet.service.shift.sideshift.network.SideShiftApiImpl;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.util.OkHttpHelper;
|
import com.m2049r.xmrwallet.util.OkHttpHelper;
|
||||||
import com.m2049r.xmrwallet.xmrto.XmrToException;
|
import com.m2049r.xmrwallet.util.ServiceHelper;
|
||||||
import com.m2049r.xmrwallet.xmrto.api.QueryOrderStatus;
|
|
||||||
import com.m2049r.xmrwallet.xmrto.api.XmrToApi;
|
|
||||||
import com.m2049r.xmrwallet.xmrto.api.XmrToCallback;
|
|
||||||
import com.m2049r.xmrwallet.xmrto.network.XmrToApiImpl;
|
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -52,23 +57,23 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
|||||||
|
|
||||||
SendSuccessWizardFragment.Listener sendListener;
|
SendSuccessWizardFragment.Listener sendListener;
|
||||||
|
|
||||||
public SendBtcSuccessWizardFragment setSendListener(SendSuccessWizardFragment.Listener listener) {
|
public void setSendListener(SendSuccessWizardFragment.Listener listener) {
|
||||||
this.sendListener = listener;
|
this.sendListener = listener;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton bCopyTxId;
|
ImageButton bCopyTxId;
|
||||||
private TextView tvTxId;
|
private TextView tvTxId;
|
||||||
private TextView tvTxAddress;
|
private TextView tvTxAddress;
|
||||||
private TextView tvTxPaymentId;
|
|
||||||
private TextView tvTxAmount;
|
private TextView tvTxAmount;
|
||||||
private TextView tvTxFee;
|
private TextView tvTxFee;
|
||||||
private TextView tvXmrToAmount;
|
private TextView tvXmrToAmount;
|
||||||
|
private ImageView ivXmrToIcon;
|
||||||
private TextView tvXmrToStatus;
|
private TextView tvXmrToStatus;
|
||||||
private ImageView ivXmrToStatus;
|
private ImageView ivXmrToStatus;
|
||||||
private ImageView ivXmrToStatusBig;
|
private ImageView ivXmrToStatusBig;
|
||||||
private ProgressBar pbXmrto;
|
private ProgressBar pbXmrto;
|
||||||
private TextView tvTxXmrToKey;
|
private TextView tvTxXmrToKey;
|
||||||
|
private TextView tvXmrToSupport;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
@@ -87,13 +92,13 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tvXmrToAmount = view.findViewById(R.id.tvXmrToAmount);
|
tvXmrToAmount = view.findViewById(R.id.tvXmrToAmount);
|
||||||
|
ivXmrToIcon = view.findViewById(R.id.ivXmrToIcon);
|
||||||
tvXmrToStatus = view.findViewById(R.id.tvXmrToStatus);
|
tvXmrToStatus = view.findViewById(R.id.tvXmrToStatus);
|
||||||
ivXmrToStatus = view.findViewById(R.id.ivXmrToStatus);
|
ivXmrToStatus = view.findViewById(R.id.ivXmrToStatus);
|
||||||
ivXmrToStatusBig = view.findViewById(R.id.ivXmrToStatusBig);
|
ivXmrToStatusBig = view.findViewById(R.id.ivXmrToStatusBig);
|
||||||
|
|
||||||
tvTxId = view.findViewById(R.id.tvTxId);
|
tvTxId = view.findViewById(R.id.tvTxId);
|
||||||
tvTxAddress = view.findViewById(R.id.tvTxAddress);
|
tvTxAddress = view.findViewById(R.id.tvTxAddress);
|
||||||
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
|
|
||||||
tvTxAmount = view.findViewById(R.id.tvTxAmount);
|
tvTxAmount = view.findViewById(R.id.tvTxAmount);
|
||||||
tvTxFee = view.findViewById(R.id.tvTxFee);
|
tvTxFee = view.findViewById(R.id.tvTxFee);
|
||||||
|
|
||||||
@@ -101,14 +106,14 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
|||||||
pbXmrto.getIndeterminateDrawable().setColorFilter(0x61000000, android.graphics.PorterDuff.Mode.MULTIPLY);
|
pbXmrto.getIndeterminateDrawable().setColorFilter(0x61000000, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||||
|
|
||||||
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
|
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
|
||||||
tvTxXmrToKey.setOnClickListener(new View.OnClickListener() {
|
tvTxXmrToKey.setOnClickListener(v -> {
|
||||||
@Override
|
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
|
||||||
public void onClick(View v) {
|
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
|
||||||
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_xmrtokey), tvTxXmrToKey.getText().toString());
|
|
||||||
Toast.makeText(getActivity(), getString(R.string.message_copy_xmrtokey), Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tvXmrToSupport = view.findViewById(R.id.tvXmrToSupport);
|
||||||
|
tvXmrToSupport.setPaintFlags(tvXmrToSupport.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,9 +152,16 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
|||||||
NumberFormat df = NumberFormat.getInstance(Locale.US);
|
NumberFormat df = NumberFormat.getInstance(Locale.US);
|
||||||
df.setMaximumFractionDigits(12);
|
df.setMaximumFractionDigits(12);
|
||||||
String btcAmount = df.format(btcData.getBtcAmount());
|
String btcAmount = df.format(btcData.getBtcAmount());
|
||||||
tvXmrToAmount.setText(getString(R.string.info_send_xmrto_success_btc, btcAmount));
|
tvXmrToAmount.setText(getString(R.string.info_send_xmrto_success_btc, btcAmount, btcData.getBtcSymbol()));
|
||||||
//TODO btcData.getBtcAddress();
|
//TODO btcData.getBtcAddress();
|
||||||
tvTxXmrToKey.setText(btcData.getXmrtoUuid());
|
tvTxXmrToKey.setText(btcData.getXmrtoOrderId());
|
||||||
|
final Crypto crypto = Crypto.withSymbol(btcData.getBtcSymbol());
|
||||||
|
ivXmrToIcon.setImageResource(crypto.getIconEnabledId());
|
||||||
|
tvXmrToSupport.setOnClickListener(v -> {
|
||||||
|
Uri orderUri = getXmrToApi().getQueryOrderUri(btcData.getXmrtoOrderId());
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW, orderUri);
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
queryOrder();
|
queryOrder();
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("btcData is null");
|
throw new IllegalStateException("btcData is null");
|
||||||
@@ -158,33 +170,23 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
|||||||
sendListener.enableDone();
|
sendListener.enableDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int QUERY_INTERVAL = 1000; // ms
|
|
||||||
|
|
||||||
private void processQueryOrder(final QueryOrderStatus status) {
|
private void processQueryOrder(final QueryOrderStatus status) {
|
||||||
Timber.d("processQueryOrder %s for %s", status.getState().toString(), status.getUuid());
|
Timber.d("processQueryOrder %s for %s", status.getState().toString(), status.getOrderId());
|
||||||
if (!btcData.getXmrtoUuid().equals(status.getUuid()))
|
if (!btcData.getXmrtoOrderId().equals(status.getOrderId()))
|
||||||
throw new IllegalStateException("UUIDs do not match!");
|
throw new IllegalStateException("UUIDs do not match!");
|
||||||
if (isResumed && (getView() != null))
|
if (isResumed && (getView() != null))
|
||||||
getView().post(new Runnable() {
|
getView().post(() -> {
|
||||||
@Override
|
showXmrToStatus(status);
|
||||||
public void run() {
|
if (!status.isTerminal()) {
|
||||||
showXmrToStatus(status);
|
getView().postDelayed(this::queryOrder, SideShiftApi.QUERY_INTERVAL);
|
||||||
if (!status.isTerminal()) {
|
|
||||||
getView().postDelayed(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
queryOrder();
|
|
||||||
}
|
|
||||||
}, QUERY_INTERVAL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void queryOrder() {
|
private void queryOrder() {
|
||||||
Timber.d("queryOrder(%s)", btcData.getXmrtoUuid());
|
Timber.d("queryOrder(%s)", btcData.getXmrtoOrderId());
|
||||||
if (!isResumed) return;
|
if (!isResumed) return;
|
||||||
getXmrToApi().queryOrderStatus(btcData.getXmrtoUuid(), new XmrToCallback<QueryOrderStatus>() {
|
getXmrToApi().queryOrderStatus(btcData.getXmrtoOrderId(), new ShiftCallback<QueryOrderStatus>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(QueryOrderStatus status) {
|
public void onSuccess(QueryOrderStatus status) {
|
||||||
if (!isAdded()) return;
|
if (!isAdded()) return;
|
||||||
@@ -194,38 +196,34 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onError(final Exception ex) {
|
public void onError(final Exception ex) {
|
||||||
if (!isResumed) return;
|
if (!isResumed) return;
|
||||||
Timber.e(ex);
|
Timber.w(ex);
|
||||||
getActivity().runOnUiThread(new Runnable() {
|
getActivity().runOnUiThread(() -> {
|
||||||
@Override
|
if (ex instanceof ShiftException) {
|
||||||
public void run() {
|
Toast.makeText(getActivity(), ((ShiftException) ex).getError().getErrorMsg(), Toast.LENGTH_LONG).show();
|
||||||
if (ex instanceof XmrToException) {
|
} else {
|
||||||
Toast.makeText(getActivity(), ((XmrToException) ex).getError().getErrorMsg(), Toast.LENGTH_LONG).show();
|
Toast.makeText(getActivity(), ex.getLocalizedMessage(), Toast.LENGTH_LONG).show();
|
||||||
} else {
|
|
||||||
Toast.makeText(getActivity(), ex.getLocalizedMessage(), Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private int statusResource = 0;
|
|
||||||
|
|
||||||
void showXmrToStatus(final QueryOrderStatus status) {
|
void showXmrToStatus(final QueryOrderStatus status) {
|
||||||
|
int statusResource = 0;
|
||||||
if (status.isError()) {
|
if (status.isError()) {
|
||||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_error, status.toString()));
|
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_error, status.toString()));
|
||||||
statusResource = R.drawable.ic_error_red_24dp;
|
statusResource = R.drawable.ic_error_red_24dp;
|
||||||
pbXmrto.getIndeterminateDrawable().setColorFilter(0xff8b0000, android.graphics.PorterDuff.Mode.MULTIPLY);
|
pbXmrto.getIndeterminateDrawable().setColorFilter(0xff8b0000, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||||
} else if (status.isSent()) {
|
} else if (status.isSent() || status.isPaid()) {
|
||||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_sent));
|
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_sent, btcData.getBtcSymbol()));
|
||||||
statusResource = R.drawable.ic_success_green_24dp;
|
statusResource = R.drawable.ic_success_green_24dp;
|
||||||
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFF417505, android.graphics.PorterDuff.Mode.MULTIPLY);
|
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFF417505, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||||
|
} else if (status.isWaiting()) {
|
||||||
|
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_unpaid));
|
||||||
|
statusResource = R.drawable.ic_pending_orange_24dp;
|
||||||
|
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFFFF6105, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||||
} else if (status.isPending()) {
|
} else if (status.isPending()) {
|
||||||
if (status.isPaid()) {
|
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_paid));
|
||||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_paid));
|
|
||||||
} else {
|
|
||||||
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_unpaid));
|
|
||||||
}
|
|
||||||
statusResource = R.drawable.ic_pending_orange_24dp;
|
statusResource = R.drawable.ic_pending_orange_24dp;
|
||||||
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFFFF6105, android.graphics.PorterDuff.Mode.MULTIPLY);
|
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFFFF6105, android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||||
} else {
|
} else {
|
||||||
@@ -234,20 +232,21 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
|||||||
ivXmrToStatus.setImageResource(statusResource);
|
ivXmrToStatus.setImageResource(statusResource);
|
||||||
if (status.isTerminal()) {
|
if (status.isTerminal()) {
|
||||||
pbXmrto.setVisibility(View.INVISIBLE);
|
pbXmrto.setVisibility(View.INVISIBLE);
|
||||||
|
ivXmrToIcon.setVisibility(View.GONE);
|
||||||
ivXmrToStatus.setVisibility(View.GONE);
|
ivXmrToStatus.setVisibility(View.GONE);
|
||||||
ivXmrToStatusBig.setImageResource(statusResource);
|
ivXmrToStatusBig.setImageResource(statusResource);
|
||||||
ivXmrToStatusBig.setVisibility(View.VISIBLE);
|
ivXmrToStatusBig.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private XmrToApi xmrToApi = null;
|
private SideShiftApi xmrToApi = null;
|
||||||
|
|
||||||
private final XmrToApi getXmrToApi() {
|
private SideShiftApi getXmrToApi() {
|
||||||
if (xmrToApi == null) {
|
if (xmrToApi == null) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (xmrToApi == null) {
|
if (xmrToApi == null) {
|
||||||
xmrToApi = new XmrToApiImpl(OkHttpHelper.getOkHttpClient(),
|
xmrToApi = new SideShiftApiImpl(OkHttpHelper.getOkHttpClient(),
|
||||||
Helper.getXmrToBaseUrl());
|
ServiceHelper.getXmrToBaseUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -29,6 +29,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
@@ -92,8 +93,6 @@ public class SendFragment extends Fragment
|
|||||||
void setOnUriScannedListener(OnUriScannedListener onUriScannedListener);
|
void setOnUriScannedListener(OnUriScannedListener onUriScannedListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private EditText etDummy;
|
|
||||||
|
|
||||||
private View llNavBar;
|
private View llNavBar;
|
||||||
private DotBar dotBar;
|
private DotBar dotBar;
|
||||||
private Button bPrev;
|
private Button bPrev;
|
||||||
@@ -101,7 +100,7 @@ public class SendFragment extends Fragment
|
|||||||
|
|
||||||
private Button bDone;
|
private Button bDone;
|
||||||
|
|
||||||
static private int MAX_FALLBACK = Integer.MAX_VALUE;
|
static private final int MAX_FALLBACK = Integer.MAX_VALUE;
|
||||||
|
|
||||||
public static SendFragment newInstance(String uri) {
|
public static SendFragment newInstance(String uri) {
|
||||||
SendFragment f = new SendFragment();
|
SendFragment f = new SendFragment();
|
||||||
@@ -166,28 +165,18 @@ public class SendFragment extends Fragment
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
bPrev.setOnClickListener(new View.OnClickListener() {
|
bPrev.setOnClickListener(v -> spendViewPager.previous());
|
||||||
public void onClick(View v) {
|
|
||||||
spendViewPager.previous();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
bNext.setOnClickListener(new View.OnClickListener() {
|
bNext.setOnClickListener(v -> spendViewPager.next());
|
||||||
public void onClick(View v) {
|
|
||||||
spendViewPager.next();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
bDone.setOnClickListener(new View.OnClickListener() {
|
bDone.setOnClickListener(v -> {
|
||||||
public void onClick(View v) {
|
Timber.d("bDone.onClick");
|
||||||
Timber.d("bDone.onClick");
|
activityCallback.onFragmentDone();
|
||||||
activityCallback.onFragmentDone();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
updatePosition(0);
|
updatePosition(0);
|
||||||
|
|
||||||
etDummy = view.findViewById(R.id.etDummy);
|
final EditText etDummy = view.findViewById(R.id.etDummy);
|
||||||
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||||
etDummy.requestFocus();
|
etDummy.requestFocus();
|
||||||
Helper.hideKeyboard(getActivity());
|
Helper.hideKeyboard(getActivity());
|
||||||
@@ -197,7 +186,7 @@ public class SendFragment extends Fragment
|
|||||||
String uri = args.getString(WalletActivity.REQUEST_URI);
|
String uri = args.getString(WalletActivity.REQUEST_URI);
|
||||||
Timber.d("URI: %s", uri);
|
Timber.d("URI: %s", uri);
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
barcodeData = BarcodeData.fromQrCode(uri);
|
barcodeData = BarcodeData.fromString(uri);
|
||||||
Timber.d("barcodeData: %s", barcodeData != null ? barcodeData.toString() : "null");
|
Timber.d("barcodeData: %s", barcodeData != null ? barcodeData.toString() : "null");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,7 +225,7 @@ public class SendFragment extends Fragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
Timber.d("onAttach %s", context);
|
Timber.d("onAttach %s", context);
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof Listener) {
|
if (context instanceof Listener) {
|
||||||
@@ -300,12 +289,7 @@ public class SendFragment extends Fragment
|
|||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Mode " + String.valueOf(aMode) + " unknown!");
|
throw new IllegalArgumentException("Mode " + String.valueOf(aMode) + " unknown!");
|
||||||
}
|
}
|
||||||
getView().post(new Runnable() {
|
getView().post(() -> pagerAdapter.notifyDataSetChanged());
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
pagerAdapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Timber.d("New Mode = %s", mode.toString());
|
Timber.d("New Mode = %s", mode.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,8 +322,9 @@ public class SendFragment extends Fragment
|
|||||||
return numPages;
|
return numPages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Object instantiateItem(ViewGroup container, int position) {
|
public Object instantiateItem(@NonNull ViewGroup container, int position) {
|
||||||
Timber.d("instantiateItem %d", position);
|
Timber.d("instantiateItem %d", position);
|
||||||
SendWizardFragment fragment = (SendWizardFragment) super.instantiateItem(container, position);
|
SendWizardFragment fragment = (SendWizardFragment) super.instantiateItem(container, position);
|
||||||
myFragments.put(position, new WeakReference<>(fragment));
|
myFragments.put(position, new WeakReference<>(fragment));
|
||||||
@@ -347,20 +332,21 @@ public class SendFragment extends Fragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||||
Timber.d("destroyItem %d", position);
|
Timber.d("destroyItem %d", position);
|
||||||
myFragments.remove(position);
|
myFragments.remove(position);
|
||||||
super.destroyItem(container, position, object);
|
super.destroyItem(container, position, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SendWizardFragment getFragment(int position) {
|
public SendWizardFragment getFragment(int position) {
|
||||||
WeakReference ref = myFragments.get(position);
|
WeakReference<SendWizardFragment> ref = myFragments.get(position);
|
||||||
if (ref != null)
|
if (ref != null)
|
||||||
return myFragments.get(position).get();
|
return myFragments.get(position).get();
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public SendWizardFragment getItem(int position) {
|
public SendWizardFragment getItem(int position) {
|
||||||
Timber.d("getItem(%d) CREATE", position);
|
Timber.d("getItem(%d) CREATE", position);
|
||||||
@@ -415,7 +401,7 @@ public class SendFragment extends Fragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemPosition(Object object) {
|
public int getItemPosition(@NonNull Object object) {
|
||||||
Timber.d("getItemPosition %s", String.valueOf(object));
|
Timber.d("getItemPosition %s", String.valueOf(object));
|
||||||
if (object instanceof SendAddressWizardFragment) {
|
if (object instanceof SendAddressWizardFragment) {
|
||||||
// keep these pages
|
// keep these pages
|
||||||
@@ -563,22 +549,4 @@ public class SendFragment extends Fragment
|
|||||||
inflater.inflate(R.menu.send_menu, menu);
|
inflater.inflate(R.menu.send_menu, menu);
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
}
|
}
|
||||||
|
|
||||||
// xmr.to info box
|
|
||||||
private static final String PREF_SHOW_XMRTO_ENABLED = "info_xmrto_enabled_send";
|
|
||||||
|
|
||||||
boolean showXmrtoEnabled = true;
|
|
||||||
|
|
||||||
void loadPrefs() {
|
|
||||||
SharedPreferences sharedPref = activityCallback.getPrefs();
|
|
||||||
showXmrtoEnabled = sharedPref.getBoolean(PREF_SHOW_XMRTO_ENABLED, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void saveXmrToPrefs() {
|
|
||||||
SharedPreferences sharedPref = activityCallback.getPrefs();
|
|
||||||
SharedPreferences.Editor editor = sharedPref.edit();
|
|
||||||
editor.putBoolean(PREF_SHOW_XMRTO_ENABLED, showXmrtoEnabled);
|
|
||||||
editor.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -17,8 +17,6 @@
|
|||||||
package com.m2049r.xmrwallet.layout;
|
package com.m2049r.xmrwallet.layout;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -26,8 +24,13 @@ import android.widget.ImageButton;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.R;
|
import com.m2049r.xmrwallet.R;
|
||||||
import com.m2049r.xmrwallet.data.NodeInfo;
|
import com.m2049r.xmrwallet.data.NodeInfo;
|
||||||
|
import com.m2049r.xmrwallet.util.ColorHelper;
|
||||||
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
@@ -35,7 +38,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
@@ -44,6 +46,8 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
|
|||||||
|
|
||||||
public interface OnInteractionListener {
|
public interface OnInteractionListener {
|
||||||
void onInteraction(View view, NodeInfo item);
|
void onInteraction(View view, NodeInfo item);
|
||||||
|
|
||||||
|
void onLongInteraction(View view, NodeInfo item);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final List<NodeInfo> nodeItems = new ArrayList<>();
|
private final List<NodeInfo> nodeItems = new ArrayList<>();
|
||||||
@@ -106,8 +110,9 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
|
|||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
||||||
final ImageButton ibBookmark;
|
final ImageButton ibBookmark;
|
||||||
|
final View pbBookmark;
|
||||||
final TextView tvName;
|
final TextView tvName;
|
||||||
final TextView tvIp;
|
final TextView tvIp;
|
||||||
final ImageView ivPing;
|
final ImageView ivPing;
|
||||||
@@ -116,16 +121,20 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
|
|||||||
ViewHolder(View itemView) {
|
ViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
ibBookmark = itemView.findViewById(R.id.ibBookmark);
|
ibBookmark = itemView.findViewById(R.id.ibBookmark);
|
||||||
|
pbBookmark = itemView.findViewById(R.id.pbBookmark);
|
||||||
tvName = itemView.findViewById(R.id.tvName);
|
tvName = itemView.findViewById(R.id.tvName);
|
||||||
tvIp = itemView.findViewById(R.id.tvAddress);
|
tvIp = itemView.findViewById(R.id.tvAddress);
|
||||||
ivPing = itemView.findViewById(R.id.ivPing);
|
ivPing = itemView.findViewById(R.id.ivPing);
|
||||||
ibBookmark.setOnClickListener(new View.OnClickListener() {
|
ibBookmark.setOnClickListener(v -> {
|
||||||
@Override
|
nodeItem.toggleFavourite();
|
||||||
public void onClick(View v) {
|
showStar();
|
||||||
nodeItem.toggleFavourite();
|
if (!nodeItem.isFavourite()) {
|
||||||
showStar();
|
nodeItem.setSelected(false);
|
||||||
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
itemView.setOnClickListener(this);
|
||||||
|
itemView.setOnLongClickListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showStar() {
|
private void showStar() {
|
||||||
@@ -139,16 +148,22 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
|
|||||||
void bind(int position) {
|
void bind(int position) {
|
||||||
nodeItem = nodeItems.get(position);
|
nodeItem = nodeItems.get(position);
|
||||||
tvName.setText(nodeItem.getName());
|
tvName.setText(nodeItem.getName());
|
||||||
final String ts = TS_FORMATTER.format(new Date(nodeItem.getTimestamp() * 1000));
|
|
||||||
ivPing.setImageResource(getPingIcon(nodeItem));
|
ivPing.setImageResource(getPingIcon(nodeItem));
|
||||||
if (nodeItem.isValid()) {
|
if (nodeItem.isTested()) {
|
||||||
tvIp.setText(context.getString(R.string.node_height, ts));
|
if (nodeItem.isValid()) {
|
||||||
|
Helper.showTimeDifference(tvIp, nodeItem.getTimestamp());
|
||||||
|
} else {
|
||||||
|
tvIp.setText(getResponseErrorText(context, nodeItem.getResponseCode()));
|
||||||
|
tvIp.setTextColor(ColorHelper.getThemedColor(tvIp.getContext(), R.attr.colorError));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tvIp.setText(getResponseErrorText(context, nodeItem.getResponseCode()));
|
tvIp.setText(context.getResources().getString(R.string.node_testing, nodeItem.getHostAddress()));
|
||||||
}
|
}
|
||||||
itemView.setOnClickListener(this);
|
itemView.setSelected(nodeItem.isSelected());
|
||||||
itemView.setClickable(itemsClickable);
|
itemView.setClickable(itemsClickable);
|
||||||
|
itemView.setEnabled(itemsClickable);
|
||||||
ibBookmark.setClickable(itemsClickable);
|
ibBookmark.setClickable(itemsClickable);
|
||||||
|
pbBookmark.setVisibility(nodeItem.isSelecting() ? View.VISIBLE : View.INVISIBLE);
|
||||||
showStar();
|
showStar();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,10 +172,24 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
|
|||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
int position = getAdapterPosition(); // gets item position
|
int position = getAdapterPosition(); // gets item position
|
||||||
if (position != RecyclerView.NO_POSITION) { // Check if an item was deleted, but the user clicked it before the UI removed it
|
if (position != RecyclerView.NO_POSITION) { // Check if an item was deleted, but the user clicked it before the UI removed it
|
||||||
listener.onInteraction(view, nodeItems.get(position));
|
final NodeInfo node = nodeItems.get(position);
|
||||||
|
node.setSelecting(true);
|
||||||
|
allowClick(false);
|
||||||
|
listener.onInteraction(view, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onLongClick(View view) {
|
||||||
|
if (listener != null) {
|
||||||
|
int position = getAdapterPosition(); // gets item position
|
||||||
|
if (position != RecyclerView.NO_POSITION) { // Check if an item was deleted, but the user clicked it before the UI removed it
|
||||||
|
listener.onLongInteraction(view, nodeItems.get(position));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static public int getPingIcon(NodeInfo nodeInfo) {
|
static public int getPingIcon(NodeInfo nodeInfo) {
|
||||||
|
@@ -17,18 +17,20 @@
|
|||||||
package com.m2049r.xmrwallet.layout;
|
package com.m2049r.xmrwallet.layout;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.R;
|
import com.m2049r.xmrwallet.R;
|
||||||
|
import com.m2049r.xmrwallet.data.Crypto;
|
||||||
|
import com.m2049r.xmrwallet.data.UserNotes;
|
||||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.data.UserNotes;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -141,7 +143,13 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
|||||||
|
|
||||||
UserNotes userNotes = new UserNotes(infoItem.notes);
|
UserNotes userNotes = new UserNotes(infoItem.notes);
|
||||||
if (userNotes.xmrtoKey != null) {
|
if (userNotes.xmrtoKey != null) {
|
||||||
ivTxType.setVisibility(View.VISIBLE);
|
final Crypto crypto = Crypto.withSymbol(userNotes.xmrtoCurrency);
|
||||||
|
if (crypto != null) {
|
||||||
|
ivTxType.setImageResource(crypto.getIconEnabledId());
|
||||||
|
ivTxType.setVisibility(View.VISIBLE);
|
||||||
|
} else {// otherwirse pretend we don't know it's a shift
|
||||||
|
ivTxType.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ivTxType.setVisibility(View.GONE); // gives us more space for the amount
|
ivTxType.setVisibility(View.GONE); // gives us more space for the amount
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
* BTChip Bitcoin Hardware Wallet Java API
|
* BTChip Bitcoin Hardware Wallet Java API
|
||||||
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
|
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
|
||||||
* (c) m2049r
|
* Copyright (c) 2018 m2049r
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user