Verified Commit 611a9162 authored by Sarath's avatar Sarath
Browse files

SendActivity with PSBT compose

parent ba5e0f67
......@@ -68,9 +68,9 @@ android {
dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.multidex:multidex:2.0.0'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.baoyz.swipemenulistview:library:1.2.1'
implementation 'com.neovisionaries:nv-websocket-client:1.9'
implementation 'commons-codec:commons-codec:1.4'
......@@ -81,12 +81,26 @@ dependencies {
implementation 'com.lambdaworks:scrypt:1.4.0'
implementation 'net.i2p.android.ext:floatingactionbutton:1.9.0'
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.google.android.material:material:1.0.0'
implementation('com.google.zxing:core:3.3.0') {
implementation 'com.google.android.material:material:1.1.0'
implementation('com.google.zxing:core:3.4.0') {
transitive = true
}
implementation ('com.github.Samourai-Wallet:extlibj:0.0.11') {
exclude group:'com.google.code.findbugs', module:'jsr305'
exclude group:'com.google.protobuf', module:'protobuf-java'
exclude group:'net.jcip', module:'jcip-annotations'
exclude group:'com.squareup.okhttp', module:'okhttp'
//
exclude group:'org.apache.commons', module:'commons-lang3'
exclude group:'org.json', module:'json'
}
implementation ('com.github.Samourai-Wallet:boltzmann-java:develop-SNAPSHOT') {
exclude group: 'it.unimi.dsi', module: 'fastutil'
}
implementation 'com.yanzhenjie.zbar:camera:1.0.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'com.auth0.android:jwtdecode:1.1.1'
implementation 'com.squareup.okhttp3:okhttp:3.13.1'
......
......@@ -74,7 +74,7 @@
>
</activity>
<activity android:name="com.samourai.sentinel.BalanceActivity"
<activity android:name="com.samourai.sentinel.balance.BalanceActivity"
android:label="@string/app_name"
android:theme="@style/AppThemeV2"
android:configChanges="keyboardHidden|orientation|screenSize"
......@@ -97,13 +97,18 @@
<activity
android:name=".utxos.UTXOSActivity"
android:label=""
android:parentActivityName=".BalanceActivity"
android:parentActivityName=".balance.BalanceActivity"
android:theme="@style/SamouraiAppTheme" />
<activity
android:name=".send.SendActivity"
android:label=""
android:parentActivityName=".balance.BalanceActivity"
android:theme="@style/SamouraiAppTheme" />
<activity
android:name=".utxos.UTXODetailsActivity"
android:label=""
android:parentActivityName=".BalanceActivity"
android:parentActivityName=".balance.BalanceActivity"
android:theme="@style/SamouraiAppTheme" />
<activity android:name="com.samourai.sentinel.ExodusActivity"
......
......@@ -16,6 +16,7 @@ import android.widget.Toast;
import com.dm.zbar.android.scanner.ZBarConstants;
import com.samourai.sentinel.access.AccessFactory;
import com.samourai.sentinel.balance.BalanceActivity;
import com.samourai.sentinel.codescanner.CameraFragmentBottomSheet;
import com.samourai.sentinel.util.FormatsUtil;
......
......@@ -15,6 +15,7 @@ import android.widget.Toast;
//import android.util.Log;
import com.samourai.sentinel.api.APIFactory;
import com.samourai.sentinel.balance.BalanceActivity;
import com.samourai.sentinel.service.BackgroundManager;
import com.samourai.sentinel.service.WebSocketService;
import com.samourai.sentinel.util.AppUtil;
......
......@@ -8,6 +8,7 @@ import com.google.gson.Gson;
import com.samourai.sentinel.BuildConfig;
import com.samourai.sentinel.SamouraiSentinel;
import com.samourai.sentinel.SentinelApplication;
import com.samourai.sentinel.balance.BalanceActivity;
import com.samourai.sentinel.network.dojo.DojoUtil;
import com.samourai.sentinel.segwit.bech32.Bech32Util;
import com.samourai.sentinel.sweep.FeeUtil;
......@@ -385,8 +386,8 @@ public class APIFactory {
// Construct the output
MyTransactionOutPoint outPoint = new MyTransactionOutPoint(txHash, txOutputN, value, scriptBytes, address);
outPoint.setConfirmations(confirmations);
if (utxos.containsKey(key)) {
HashMap<String, UTXO> _existing = utxos.get(key);
if (_existing.containsKey(script)) {
......@@ -535,17 +536,17 @@ public class APIFactory {
for (String key : utxos.keySet()) {
UTXO u = new UTXO();
if (key.equals(xpub_active)) {
HashMap<String, UTXO> utxoHashMap = utxos.get(key);
for (String utxoValueKeys : utxoHashMap.keySet()) {
for (MyTransactionOutPoint out : utxoHashMap.get(utxoValueKeys).getOutpoints()) {
u.getOutpoints().add(out);
u.setPath(utxoHashMap.get(utxoValueKeys).getPath());
}
if (u.getOutpoints().size() > 0) {
unspents.add(u);
}
// if (key.equals(xpub_active)) {
HashMap<String, UTXO> utxoHashMap = utxos.get(key);
for (String utxoValueKeys : utxoHashMap.keySet()) {
for (MyTransactionOutPoint out : utxoHashMap.get(utxoValueKeys).getOutpoints()) {
u.getOutpoints().add(out);
u.setPath(utxoHashMap.get(utxoValueKeys).getPath());
}
if (u.getOutpoints().size() > 0) {
unspents.add(u);
}
// }
}
}
......@@ -553,6 +554,7 @@ public class APIFactory {
}
public List<UTXO> getUTXOsByXpub(int index) {
Log.i("UTXO", "JDJDJD ".concat(String.valueOf(index)));
List<UTXO> unspents = new ArrayList<UTXO>();
String xpub_active = "";
List<String> _xpubs = SamouraiSentinel.getInstance(context).getAllAddrsSorted();
......@@ -562,6 +564,8 @@ public class APIFactory {
} else {
xpub_active = _xpubs.get(index - 1);
}
Log.i("UTXO", "JDJDJD xpub_active".concat(String.valueOf(xpub_active)));
for (String key : utxos.keySet()) {
UTXO u = new UTXO();
......@@ -583,7 +587,9 @@ public class APIFactory {
}
public List<Tx> getTxs(int selected) {
return xpub_txs.get(selected);
final List<String> xpubList = SamouraiSentinel.getInstance(context).getAllAddrsSorted();
String selectedAddress = xpubList.get(selected);
return xpub_txs.get(selectedAddress);
}
......@@ -810,6 +816,10 @@ public class APIFactory {
}
public HashMap<String, String> getUnspentPaths() {
return unspentPaths;
}
public byte[] getXORKey() {
......
......@@ -13,6 +13,9 @@ public class Tx {
private long ts = 0L;
private Map<Integer,String> tags = null;
//For UI list sections
public String section = "";
public Tx(String hash, String address, double amount, long date, long confirmations) {
strHash = hash;
strAddress = address;
......@@ -103,4 +106,9 @@ public class Tx {
this.tags = tags;
}
public void setSection(String section) {
this.section = section;
}
}
package com.samourai.sentinel;
package com.samourai.sentinel.balance;
import android.Manifest;
import android.animation.ObjectAnimator;
......@@ -16,9 +16,11 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.appcompat.app.AppCompatActivity;
import android.text.InputType;
import android.util.Log;
import android.view.KeyEvent;
......@@ -42,6 +44,12 @@ import android.widget.TextView;
import android.widget.Toast;
import com.dm.zbar.android.scanner.ZBarConstants;
import com.samourai.sentinel.ExodusActivity;
import com.samourai.sentinel.R;
import com.samourai.sentinel.ReceiveActivity;
import com.samourai.sentinel.SamouraiSentinel;
import com.samourai.sentinel.SettingsActivity;
import com.samourai.sentinel.XPUBListActivity;
import com.samourai.sentinel.access.AccessFactory;
import com.samourai.sentinel.api.APIFactory;
import com.samourai.sentinel.api.Tx;
......@@ -52,6 +60,8 @@ import com.samourai.sentinel.hd.HD_WalletFactory;
import com.samourai.sentinel.network.dojo.DojoUtil;
import com.samourai.sentinel.network.dojo.Network;
import com.samourai.sentinel.permissions.PermissionsUtil;
import com.samourai.sentinel.send.SendActivity;
import com.samourai.sentinel.service.JobRefreshService;
import com.samourai.sentinel.service.WebSocketService;
import com.samourai.sentinel.sweep.PrivKeyReader;
import com.samourai.sentinel.sweep.SweepUtil;
......@@ -65,6 +75,7 @@ import com.samourai.sentinel.util.MonetaryUtil;
import com.samourai.sentinel.util.PrefsUtil;
import com.samourai.sentinel.util.TimeOutUtil;
import com.samourai.sentinel.util.TypefaceUtil;
import com.samourai.sentinel.utxos.UTXOSActivity;
import net.i2p.android.ext.floatingactionbutton.FloatingActionButton;
import net.i2p.android.ext.floatingactionbutton.FloatingActionsMenu;
......@@ -75,7 +86,6 @@ import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.crypto.BIP38PrivateKey;
import org.bitcoinj.crypto.MnemonicException;
import org.json.JSONException;
import java.io.IOException;
......@@ -84,6 +94,9 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
//import android.util.Log;
public class BalanceActivity extends AppCompatActivity {
......@@ -110,23 +123,24 @@ public class BalanceActivity extends AppCompatActivity {
private static ArrayAdapter<String> adapter = null;
public static final String ACTION_INTENT = "com.samourai.sentinel.BalanceFragment.REFRESH";
public static final String ACTION_INTENT_COMPLETE = "com.samourai.sentinel.BalanceFragment.ON_REFRESH_COMPLETE";
private ProgressDialog progress = null;
private CompositeDisposable compositeDisposable = new CompositeDisposable();
private Spinner accountSpinner;
protected BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.i("GOT INTEN", "inte ".concat(intent.toString()));
if (ACTION_INTENT.equals(intent.getAction())) {
BalanceActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
refreshTx(false);
}
});
BalanceActivity.this.runOnUiThread(() -> refreshTx());
}
if (ACTION_INTENT_COMPLETE.equals(intent.getAction())) {
BalanceActivity.this.runOnUiThread(() -> setTx());
}
}
};
......@@ -152,8 +166,8 @@ public class BalanceActivity extends AppCompatActivity {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
SamouraiSentinel.getInstance(BalanceActivity.this).setCurrentSelectedAccount(position);
refreshTx(false);
// refreshTx();
setTx();
}
@Override
......@@ -202,60 +216,45 @@ public class BalanceActivity extends AppCompatActivity {
txList = (ListView) findViewById(R.id.txList);
txAdapter = new TransactionAdapter();
txList.setAdapter(txAdapter);
txList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
if (position == 0) {
return;
}
txList.setOnItemClickListener((AdapterView.OnItemClickListener) (parent, view, position, id) -> {
long viewId = view.getId();
View v = (View) view.getParent();
Tx tx = txs.get(position - 1);
ImageView ivTxStatus = (ImageView) v.findViewById(R.id.TransactionStatus);
TextView tvConfirmationCount = (TextView) v.findViewById(R.id.ConfirmationCount);
if (position == 0) {
return;
}
if (viewId == R.id.ConfirmationCount || viewId == R.id.TransactionStatus) {
long viewId = view.getId();
View v = (View) view.getParent();
Tx tx = txs.get(position - 1);
ImageView ivTxStatus = (ImageView) v.findViewById(R.id.TransactionStatus);
TextView tvConfirmationCount = (TextView) v.findViewById(R.id.ConfirmationCount);
if (txStates.containsKey(tx.getHash()) && txStates.get(tx.getHash()) == true) {
txStates.put(tx.getHash(), false);
displayTxStatus(false, tx.getConfirmations(), tvConfirmationCount, ivTxStatus);
} else {
txStates.put(tx.getHash(), true);
displayTxStatus(true, tx.getConfirmations(), tvConfirmationCount, ivTxStatus);
}
if (viewId == R.id.ConfirmationCount || viewId == R.id.TransactionStatus) {
if (txStates.containsKey(tx.getHash()) && txStates.get(tx.getHash()) == true) {
txStates.put(tx.getHash(), false);
displayTxStatus(false, tx.getConfirmations(), tvConfirmationCount, ivTxStatus);
} else {
txStates.put(tx.getHash(), true);
displayTxStatus(true, tx.getConfirmations(), tvConfirmationCount, ivTxStatus);
}
String strTx = tx.getHash();
if (strTx != null) {
int sel = PrefsUtil.getInstance(BalanceActivity.this).getValue(PrefsUtil.BLOCK_EXPLORER, 0);
CharSequence url = BlockExplorerUtil.getInstance().getBlockExplorerUrls()[sel];
} else {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url + strTx));
startActivity(browserIntent);
}
String strTx = tx.getHash();
if (strTx != null) {
int sel = PrefsUtil.getInstance(BalanceActivity.this).getValue(PrefsUtil.BLOCK_EXPLORER, 0);
CharSequence url = BlockExplorerUtil.getInstance().getBlockExplorerUrls()[sel];
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url + strTx));
startActivity(browserIntent);
}
}
});
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swiperefresh);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new Handler().post(new Runnable() {
@Override
public void run() {
refreshTx(true);
}
});
}
});
swipeRefreshLayout = findViewById(R.id.swiperefresh);
swipeRefreshLayout.setOnRefreshListener(() -> new Handler().post(() -> refreshTx()));
swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
......@@ -270,9 +269,25 @@ public class BalanceActivity extends AppCompatActivity {
}
restoreWatchOnly();
loadWalletMeta();
}
private void loadWalletMeta() {
Disposable disposable = SamouraiSentinel.getInstance(getApplication()).getUTXOs()
.subscribe((s, throwable) -> {
if (s != null && !s.isEmpty() && throwable == null) {
APIFactory.getInstance(getApplication()).parseUnspentOutputs(s);
}
});
Disposable disposable1 = SamouraiSentinel.getInstance(getApplication())
.loadUTXOMeta()
.subscribe(() -> {
}, Throwable::printStackTrace);
compositeDisposable.add(disposable1);
compositeDisposable.add(disposable);
}
@Override
public void onResume() {
super.onResume();
......@@ -280,6 +295,9 @@ public class BalanceActivity extends AppCompatActivity {
IntentFilter filter = new IntentFilter(ACTION_INTENT);
LocalBroadcastManager.getInstance(BalanceActivity.this).registerReceiver(receiver, filter);
IntentFilter filterComplete = new IntentFilter(ACTION_INTENT_COMPLETE);
LocalBroadcastManager.getInstance(BalanceActivity.this).registerReceiver(receiver, filterComplete);
AppUtil.getInstance(BalanceActivity.this).checkTimeOut();
}
......@@ -311,6 +329,14 @@ public class BalanceActivity extends AppCompatActivity {
if (id == R.id.action_settings) {
doSettings();
}
if (id == R.id.utxos_menu) {
Intent intent = new Intent(this, UTXOSActivity.class);
startActivity(intent);
}
if (id == R.id.send_activity) {
Intent intent = new Intent(this, SendActivity.class);
startActivity(intent);
}
if (id == R.id.action_network) {
startActivity(new Intent(this, Network.class));
} else if (id == R.id.action_sweep) {
......@@ -541,71 +567,32 @@ public class BalanceActivity extends AppCompatActivity {
}
public void refreshTx(final boolean dragged) {
final Handler handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
APIFactory.getInstance(getApplicationContext()).stayingAlive();
runOnUiThread(() -> {
swipeRefreshLayout.setRefreshing(true);
});
int idx = SamouraiSentinel.getInstance(BalanceActivity.this).getCurrentSelectedAccount();
List<String> _xpubs = SamouraiSentinel.getInstance(BalanceActivity.this).getAllAddrsSorted();
if (idx == 0) {
APIFactory.getInstance(BalanceActivity.this).getXPUB(_xpubs.toArray(new String[_xpubs.size()]));
} else {
APIFactory.getInstance(BalanceActivity.this).getXPUB(new String[]{_xpubs.get(idx - 1)});
}
if (idx == 0) {
txs = APIFactory.getInstance(BalanceActivity.this).getAllXpubTxs();
} else {
txs = APIFactory.getInstance(BalanceActivity.this).getXpubTxs().get(_xpubs.get(idx - 1));
}
public void refreshTx() {
try {
if (HD_WalletFactory.getInstance(BalanceActivity.this).get() != null) {
HD_Wallet hdw = HD_WalletFactory.getInstance(BalanceActivity.this).get();
swipeRefreshLayout.setRefreshing(true);
Intent intent = new Intent(this, JobRefreshService.class);
JobRefreshService.enqueueWork(getApplicationContext(), intent);
for (int i = 0; i < hdw.getAccounts().size(); i++) {
HD_WalletFactory.getInstance(BalanceActivity.this).get().getAccount(i).getReceive().setAddrIdx(AddressFactory.getInstance().getHighestTxReceiveIdx(i));
}
}
} catch (IOException ioe) {
;
} catch (MnemonicException.MnemonicLengthException mle) {
;
}
if (!AppUtil.getInstance(BalanceActivity.this.getApplicationContext()).isServiceRunning(WebSocketService.class) && !DojoUtil.getInstance(getApplicationContext()).isDojoEnabled()) {
startService(new Intent(BalanceActivity.this.getApplicationContext(), WebSocketService.class));
}
PrefsUtil.getInstance(BalanceActivity.this).setValue(PrefsUtil.FIRST_RUN, false);
if (!AppUtil.getInstance(getApplication().getApplicationContext()).isServiceRunning(WebSocketService.class) && !DojoUtil.getInstance(getApplicationContext()).isDojoEnabled()) {
startService(new Intent(getApplication().getApplicationContext(), WebSocketService.class));
}
handler.post(new Runnable() {
public void run() {
swipeRefreshLayout.setRefreshing(false);
txAdapter.notifyDataSetChanged();
displayBalance();
}
});
Looper.loop();
}
}
}).start();
public void setTx() {
if (swipeRefreshLayout.isRefreshing()) {
swipeRefreshLayout.setRefreshing(false);
}
int idx = SamouraiSentinel.getInstance(BalanceActivity.this).getCurrentSelectedAccount();
if (idx == 0) {
txs = APIFactory.getInstance(BalanceActivity.this).getAllXpubTxs();
} else {
txs = APIFactory.getInstance(BalanceActivity.this).getTxs(idx - 1);
}
txAdapter.notifyDataSetChanged();
displayBalance();
}
......@@ -659,6 +646,17 @@ public class BalanceActivity extends AppCompatActivity {
}
@Override
protected void onDestroy() {
Disposable disposable = SamouraiSentinel.getInstance(getApplicationContext())
.saveUTXOMeta()
.subscribe(() -> {
}, throwable -> {
});
super.onDestroy();
}
private void displayTxStatus(boolean heads, long confirmations, TextView tvConfirmationCount, ImageView ivTxStatus) {
if (heads) {
......@@ -1029,7 +1027,7 @@ public class BalanceActivity extends AppCompatActivity {
SamouraiSentinel.getInstance(BalanceActivity.this).setCurrentSelectedAccount(0);
}
refreshTx(false);
refreshTx();
try {
SamouraiSentinel.getInstance(BalanceActivity.this).serialize(SamouraiSentinel.getInstance(BalanceActivity.this).toJSON(), null);
......
package com.samourai.sentinel.hd;
import android.content.Context;
import android.util.Log;
//import android.util.Log;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.HDKeyDerivation;
import org.bitcoinj.crypto.MnemonicCode;
import org.bitcoinj.crypto.MnemonicException;
import com.samourai.sentinel.SamouraiSentinel;
import com.samourai.sentinel.util.PrefsUtil;
import org.apache.commons.codec.DecoderException;
import org.bitcoinj.params.TestNet3Params;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class HD_WalletFactory {
......