Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
752a58b3bf
9 changed files with 330 additions and 25 deletions
|
@ -0,0 +1,56 @@
|
|||
package cy.agorise.crystalwallet.apigenerator;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.BroadcastTransaction;
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.GetEstimateFee;
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.GetTransactionByAddress;
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.GetTransactionData;
|
||||
import cy.agorise.crystalwallet.enums.CryptoNet;
|
||||
import cy.agorise.crystalwallet.network.CryptoNetManager;
|
||||
|
||||
public class InsightApiGenerator {
|
||||
|
||||
private static HashMap<CryptoNet,BroadcastTransaction> broadcaster = new HashMap();
|
||||
private static HashMap<CryptoNet,GetTransactionByAddress> transactionGetters = new HashMap();
|
||||
private static HashMap<CryptoNet,GetTransactionData> transacitonFollowers = new HashMap();
|
||||
|
||||
public static void getTransactionFromAddress(CryptoNet cryptoNet, String address,
|
||||
ApiRequest request, Context context,
|
||||
boolean subscribe){
|
||||
if(!transactionGetters.containsKey(cryptoNet)){
|
||||
//TODO change this line
|
||||
transactionGetters.put(cryptoNet,new GetTransactionByAddress(null,CryptoNetManager.getURL(cryptoNet),context));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void followTransaction(CryptoNet cryptoNet, String txid, Context context){
|
||||
|
||||
}
|
||||
|
||||
public static void broadcastTransaction(CryptoNet cryptoNet, String rawtx, ApiRequest request){
|
||||
if(!broadcaster.containsKey(cryptoNet)){
|
||||
//TODO change to multiple broadcast
|
||||
broadcaster.put(cryptoNet,new BroadcastTransaction(rawtx,null,
|
||||
CryptoNetManager.getURL(cryptoNet),null));
|
||||
broadcaster.get(cryptoNet).start();
|
||||
}
|
||||
}
|
||||
|
||||
public static void getEstimateFee(CryptoNet cryptoNet, final ApiRequest request){
|
||||
GetEstimateFee.getEstimateFee(CryptoNetManager.getURL(cryptoNet), new GetEstimateFee.estimateFeeListener() {
|
||||
@Override
|
||||
public void estimateFee(long value) {
|
||||
request.listener.success(value,request.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fail() {
|
||||
request.listener.fail(request.getId());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -22,29 +22,36 @@ public abstract class GetEstimateFee {
|
|||
|
||||
/**
|
||||
* The funciton to get the rate for the transaction be included in the next 2 blocks
|
||||
* @param coin The coin to get the rate
|
||||
* @param serverUrl The url of the insight server
|
||||
* @param listener the listener to this answer
|
||||
*/
|
||||
public static void getEstimateFee(final CryptoCoin coin, String serverUrl, final estimateFeeListener listener) {
|
||||
InsightApiServiceGenerator serviceGenerator = new InsightApiServiceGenerator(serverUrl);
|
||||
InsightApiService service = serviceGenerator.getService(InsightApiService.class);
|
||||
Call<JsonObject> call = service.estimateFee(InsightApiConstants.getPath(coin));
|
||||
final JsonObject answer = new JsonObject();
|
||||
call.enqueue(new Callback<JsonObject>() {
|
||||
@Override
|
||||
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
|
||||
listener.estimateFee((long) (answer.get("answer").getAsDouble()));
|
||||
public static void getEstimateFee(String serverUrl, final estimateFeeListener listener) {
|
||||
try {
|
||||
InsightApiServiceGenerator serviceGenerator = new InsightApiServiceGenerator(serverUrl);
|
||||
InsightApiService service = serviceGenerator.getService(InsightApiService.class);
|
||||
Call<JsonObject> call = service.estimateFee(serverUrl);
|
||||
final JsonObject answer = new JsonObject();
|
||||
call.enqueue(new Callback<JsonObject>() {
|
||||
@Override
|
||||
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
|
||||
listener.estimateFee((long) (answer.get("answer").getAsDouble()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<JsonObject> call, Throwable t) {
|
||||
listener.estimateFee(-1);
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onFailure(Call<JsonObject> call, Throwable t) {
|
||||
listener.fail();
|
||||
listener.estimateFee(-1);
|
||||
}
|
||||
});
|
||||
}catch(Exception e){
|
||||
listener.fail();
|
||||
}
|
||||
}
|
||||
|
||||
public static interface estimateFeeListener{
|
||||
public void estimateFee(long value);
|
||||
public void fail();
|
||||
}
|
||||
|
||||
}
|
|
@ -24,7 +24,7 @@ public interface TransactionDao {
|
|||
"LEFT JOIN crypto_net_account cna ON cct.account_id = cna.id " +
|
||||
"LEFT JOIN contact c ON c.id = (SELECT ca.contact_id FROM contact_address ca WHERE ca.address LIKE (CASE is_input WHEN 1 THEN cct.\"from\" ELSE cct.\"to\" END) LIMIT 1) " +
|
||||
"LEFT JOIN bitshares_account_name_cache banc ON banc.account_id = (CASE is_input WHEN 1 THEN cct.\"from\" ELSE cct.\"to\" END) " +
|
||||
"WHERE user_account_name LIKE '%'||:search||'%' OR contact_name LIKE '%'||:search||'%' OR cct.\"from\" LIKE '%'||:search||'%' OR cct.\"to\" LIKE '%'||:search||'%'";
|
||||
"WHERE user_account_name LIKE '%'||:search||'%' OR contact_name LIKE '%'||:search||'%' OR cct.\"from\" LIKE '%'||:search||'%' OR cct.\"to\" LIKE '%'||:search||'%' OR banc.name LIKE '%'||:search||'%'";
|
||||
|
||||
@Query("SELECT * FROM crypto_coin_transaction")
|
||||
LiveData<List<CryptoCoinTransaction>> getAll();
|
||||
|
|
|
@ -194,15 +194,15 @@ public class ImportAccountOptionsFragment extends DialogFragment {
|
|||
}
|
||||
});
|
||||
|
||||
FileServiceRequests.getInstance().addRequest(importBackupRequest);
|
||||
|
||||
/*
|
||||
* Show loading dialog
|
||||
* */
|
||||
* Show loading dialog
|
||||
* */
|
||||
crystalDialog = new CrystalDialog((Activity) getContext());
|
||||
crystalDialog.setText(getContext().getString(R.string.Restoring_backup_from_file));
|
||||
crystalDialog.progress();
|
||||
crystalDialog.show();
|
||||
|
||||
FileServiceRequests.getInstance().addRequest(importBackupRequest);
|
||||
}
|
||||
})
|
||||
.setNegativeButton("Cancel",
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.support.design.widget.FloatingActionButton;
|
|||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.Editable;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -112,9 +113,9 @@ public class TransactionsFragment extends Fragment {
|
|||
TransactionOrderSpinnerAdapter.TransactionOrderSpinnerItem orderSelected =
|
||||
(TransactionOrderSpinnerAdapter.TransactionOrderSpinnerItem)(spTransactionsOrder.getSelectedItem());
|
||||
|
||||
if (transactionsLiveData != null){
|
||||
transactionsLiveData.removeObservers(this);
|
||||
}
|
||||
//if (transactionsLiveData != null){
|
||||
//transactionsLiveData.removeObservers(this);
|
||||
//}
|
||||
transactionListViewModel.initTransactionList(orderSelected.getField(),etTransactionSearch.getText().toString());
|
||||
transactionsLiveData = transactionListViewModel.getTransactionList();
|
||||
|
||||
|
@ -122,6 +123,7 @@ public class TransactionsFragment extends Fragment {
|
|||
transactionsLiveData.observe(this, new Observer<PagedList<CryptoCoinTransactionExtended>>() {
|
||||
@Override
|
||||
public void onChanged(@Nullable PagedList<CryptoCoinTransactionExtended> cryptoCoinTransactions) {
|
||||
Log.i("Transactions","Transactions have change! Count:"+cryptoCoinTransactions.size());
|
||||
transactionListView.setData(cryptoCoinTransactions, fragment);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
package cy.agorise.crystalwallet.manager;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionOutPoint;
|
||||
import org.bitcoinj.crypto.ChildNumber;
|
||||
import org.bitcoinj.crypto.HDKeyDerivation;
|
||||
import org.bitcoinj.script.Script;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import cy.agorise.crystalwallet.apigenerator.ApiRequest;
|
||||
import cy.agorise.crystalwallet.apigenerator.ApiRequestListener;
|
||||
import cy.agorise.crystalwallet.apigenerator.InsightApiGenerator;
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.BroadcastTransaction;
|
||||
import cy.agorise.crystalwallet.models.CryptoNetAccount;
|
||||
import cy.agorise.crystalwallet.models.GTxIO;
|
||||
import cy.agorise.crystalwallet.models.GeneralCoinAddress;
|
||||
import cy.agorise.crystalwallet.models.GeneralTransaction;
|
||||
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequest;
|
||||
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestsListener;
|
||||
import cy.agorise.crystalwallet.requestmanagers.GeneralAccountSendRequest;
|
||||
import cy.agorise.graphenej.Util;
|
||||
|
||||
public class GeneralAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener {
|
||||
|
||||
@Override
|
||||
public void createAccountFromSeed(CryptoNetAccount account, ManagerRequest request, Context context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importAccountFromSeed(CryptoNetAccount account, Context context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAccountFromDB(CryptoNetAccount account, Context context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewRequest(CryptoNetInfoRequest request) {
|
||||
|
||||
}
|
||||
|
||||
public void send(final GeneralAccountSendRequest request){
|
||||
//TODO check server connection
|
||||
//TODO validate to address
|
||||
|
||||
InsightApiGenerator.getEstimateFee(request.getAccount().getCryptoNet(),new ApiRequest(1, new ApiRequestListener() {
|
||||
@Override
|
||||
public void success(Object answer, int idPetition) {
|
||||
Transaction tx = new Transaction(request.getAccount().getNetworkParam());
|
||||
long currentAmount = 0;
|
||||
long fee = -1;
|
||||
long feeRate = (Long) answer;
|
||||
fee = 226 * feeRate;
|
||||
|
||||
List<GeneralCoinAddress> addresses = request.getAccount().getAddresses();
|
||||
List<GTxIO> utxos = new ArrayList();
|
||||
for(GeneralCoinAddress address : addresses){
|
||||
List<GTxIO> addrUtxos = address.getUTXos();
|
||||
for(GTxIO addrUtxo : addrUtxos){
|
||||
utxos.add(addrUtxo);
|
||||
currentAmount += addrUtxo.getAmount();
|
||||
if(currentAmount >= request.getAmount()+ fee){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(currentAmount >= request.getAmount() + fee){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(currentAmount< request.getAmount() + fee){
|
||||
request.setStatus(GeneralAccountSendRequest.StatusCode.NO_BALANCE);
|
||||
return;
|
||||
}
|
||||
|
||||
//String to an address
|
||||
Address toAddr = Address.fromBase58(request.getAccount().getNetworkParam(), request.getToAccount());
|
||||
tx.addOutput(Coin.valueOf(request.getAmount()), toAddr);
|
||||
|
||||
if(request.getMemo()!= null && !request.getMemo().isEmpty()){
|
||||
String memo = request.getMemo();
|
||||
if(request.getMemo().length()>40){
|
||||
memo = memo.substring(0,40);
|
||||
}
|
||||
byte[]scriptByte = new byte[memo.length()+2];
|
||||
scriptByte[0] = 0x6a;
|
||||
scriptByte[1] = (byte) memo.length();
|
||||
System.arraycopy(memo.getBytes(),0,scriptByte,2,memo.length());
|
||||
Script memoScript = new Script(scriptByte);
|
||||
tx.addOutput(Coin.valueOf(0),memoScript);
|
||||
}
|
||||
|
||||
//Change address
|
||||
long remain = currentAmount - request.getAmount() - fee;
|
||||
if( remain > 0 ) {
|
||||
Address changeAddr = Address.fromBase58(request.getAccount().getNetworkParam(), request.getAccount().getNextChangeAddress());
|
||||
tx.addOutput(Coin.valueOf(remain), changeAddr);
|
||||
}
|
||||
|
||||
for(GTxIO utxo: utxos) {
|
||||
Sha256Hash txHash = Sha256Hash.wrap(utxo.getTransaction().getTxid());
|
||||
Script script = new Script(Util.hexToBytes(utxo.getScriptHex()));
|
||||
TransactionOutPoint outPoint = new TransactionOutPoint(request.getAccount().getNetworkParam(), utxo.getIndex(), txHash);
|
||||
if(utxo.getAddress().getKey().isPubKeyOnly()){
|
||||
if(utxo.getAddress().isIsChange()){
|
||||
utxo.getAddress().setKey(HDKeyDerivation.deriveChildKey(request.getAccount().getChangeKey(), new ChildNumber(utxo.getAddress().getIndex(), false)));
|
||||
}else{
|
||||
utxo.getAddress().setKey(HDKeyDerivation.deriveChildKey(request.getAccount().getExternalKey(), new ChildNumber(utxo.getAddress().getIndex(), false)));
|
||||
}
|
||||
}
|
||||
tx.addSignedInput(outPoint, script, utxo.getAddress().getKey(), Transaction.SigHash.ALL, true);
|
||||
}
|
||||
|
||||
InsightApiGenerator.broadcastTransaction(request.getAccount().getCryptoNet(),Util.bytesToHex(tx.bitcoinSerialize()),new ApiRequest(1, new ApiRequestListener() {
|
||||
@Override
|
||||
public void success(Object answer, int idPetition) {
|
||||
request.setStatus(GeneralAccountSendRequest.StatusCode.SUCCEEDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fail(int idPetition) {
|
||||
request.setStatus(GeneralAccountSendRequest.StatusCode.PETITION_FAILED);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fail(int idPetition) {
|
||||
request.setStatus(GeneralAccountSendRequest.StatusCode.NO_FEE);
|
||||
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
|
@ -323,6 +323,14 @@ public abstract class GeneralCoinAccount extends CryptoNetAccount {
|
|||
|
||||
public abstract List<CryptoNetBalance> getBalance();
|
||||
|
||||
public DeterministicKey getChangeKey() {
|
||||
return mChangeKey;
|
||||
}
|
||||
|
||||
public DeterministicKey getExternalKey() {
|
||||
return mExternalKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the transaction, to order it for the list of transaction
|
||||
*/
|
||||
|
|
|
@ -37,7 +37,9 @@ public abstract class CryptoNetInfoRequest {
|
|||
}
|
||||
|
||||
protected void _fireOnCarryOutEvent(){
|
||||
listener.onCarryOut();
|
||||
if (listener != null) {
|
||||
listener.onCarryOut();
|
||||
}
|
||||
CryptoNetInfoRequests.getInstance().removeRequest(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package cy.agorise.crystalwallet.requestmanagers;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import cy.agorise.crystalwallet.enums.CryptoCoin;
|
||||
import cy.agorise.crystalwallet.models.GeneralCoinAccount;
|
||||
|
||||
public class GeneralAccountSendRequest extends CryptoNetInfoRequest {
|
||||
/**
|
||||
* The status code of this request
|
||||
*/
|
||||
public enum StatusCode{
|
||||
NOT_STARTED,
|
||||
SUCCEEDED,
|
||||
NO_INTERNET,
|
||||
NO_SERVER_CONNECTION,
|
||||
BAD_TO_ADDRESS,
|
||||
NO_FEE,
|
||||
NO_BALANCE,
|
||||
PETITION_FAILED
|
||||
}
|
||||
|
||||
// The app context
|
||||
private Context mContext;
|
||||
//The soruce Account
|
||||
private GeneralCoinAccount mAccount;
|
||||
// The destination account address
|
||||
private String mToAccount;
|
||||
// The amount of the transaction
|
||||
private long mAmount;
|
||||
// The memo, can be null
|
||||
private String mMemo;
|
||||
// The state of this request
|
||||
private StatusCode status = StatusCode.NOT_STARTED;
|
||||
|
||||
public GeneralAccountSendRequest(CryptoCoin coin, Context context, GeneralCoinAccount account, String toAccount, long amount, String memo) {
|
||||
super(coin);
|
||||
this.mContext = context;
|
||||
this.mAccount = account;
|
||||
this.mToAccount = toAccount;
|
||||
this.mAmount = amount;
|
||||
this.mMemo = memo;
|
||||
}
|
||||
|
||||
public GeneralAccountSendRequest(CryptoCoin coin, Context context, GeneralCoinAccount account, String toAccount, long amount) {
|
||||
this(coin,context,account,toAccount,amount,null);
|
||||
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public GeneralCoinAccount getAccount() {
|
||||
return mAccount;
|
||||
}
|
||||
|
||||
public String getToAccount() {
|
||||
return mToAccount;
|
||||
}
|
||||
|
||||
public long getAmount() {
|
||||
return mAmount;
|
||||
}
|
||||
|
||||
public String getMemo() {
|
||||
return mMemo;
|
||||
}
|
||||
|
||||
public void validate(){
|
||||
if ((this.status != StatusCode.NOT_STARTED)){
|
||||
this._fireOnCarryOutEvent();
|
||||
}
|
||||
}
|
||||
|
||||
public void setStatus(StatusCode code){
|
||||
this.status = code;
|
||||
this._fireOnCarryOutEvent();
|
||||
}
|
||||
|
||||
public StatusCode getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue