diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/SendTransactionActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/SendTransactionActivity.java index 26adfa1..e82e88a 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/activities/SendTransactionActivity.java +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/SendTransactionActivity.java @@ -41,7 +41,7 @@ public class SendTransactionActivity extends AppCompatActivity implements UIVali SendTransactionValidator sendTransactionValidator; @BindView(R.id.spFrom) - MaterialSpinner spFrom; + Spinner spFrom; @BindView(R.id.tvFromError) TextView tvFromError; @BindView(R.id.etTo) diff --git a/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java b/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java index 297017a..4383d8e 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java +++ b/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java @@ -82,6 +82,25 @@ public abstract class GrapheneApiGenerator { */ private static HashMap currentBitsharesListener = new HashMap<>(); + + // The message broker for Steem + private static SubscriptionMessagesHub steemSubscriptionHub = new SubscriptionMessagesHub("", "", true, new NodeErrorListener() { + @Override + public void onError(BaseResponse.Error error) { + //TODO subcription hub error + System.out.println("GrapheneAPI error"); + } + }); + + /** + * The Steem subscription thread for the real time updates + */ + private static WebSocketThread steemSubscriptionThread = new WebSocketThread(steemSubscriptionHub, CryptoNetManager.getURL(CryptoNet.STEEM)); + /** + * This is used for manager each Steem listener in the subscription thread + */ + private static HashMap currentSteemListener = new HashMap<>(); + /** * Retrieves the data of an account searching by it's id * @@ -164,7 +183,7 @@ public abstract class GrapheneApiGenerator { * @param request The Api request object, to answer this petition */ public static void getAccountTransaction(String accountGrapheneId, int start, int stop, - int limit, final ApiRequest request){ + int limit, CryptoNet cryptoNet, final ApiRequest request){ WebSocketThread thread = new WebSocketThread(new GetRelativeAccountHistory(new UserAccount(accountGrapheneId), start, limit, stop, new WitnessResponseListener() { @Override @@ -176,7 +195,7 @@ public abstract class GrapheneApiGenerator { public void onError(BaseResponse.Error error) { request.getListener().fail(request.getId()); } - }),CryptoNetManager.getURL(CryptoNet.BITSHARES)); + }),CryptoNetManager.getURL(cryptoNet)); thread.start(); } @@ -381,7 +400,7 @@ public abstract class GrapheneApiGenerator { if(operation instanceof TransferOperation){ final TransferOperation tOperation = (TransferOperation) operation; if(tOperation.getFrom().getObjectId().equals(accountBitsharesId) || tOperation.getTo().getObjectId().equals(accountBitsharesId)){ - GrapheneApiGenerator.getAccountBalance(accountId,accountBitsharesId,context); + GrapheneApiGenerator.getAccountBalance(accountId,accountBitsharesId,CryptoNet.BITSHARES,context); final CryptoCoinTransaction transaction = new CryptoCoinTransaction(); transaction.setAccountId(accountId); transaction.setAmount(tOperation.getAssetAmount().getAmount().longValue()); @@ -444,6 +463,99 @@ public abstract class GrapheneApiGenerator { } } + /** + * Subscribe a steem account to receive real time updates + * + * @param accountId The id opf the database of the account + * @param accountSteemId The steem id of the account + * @param context The android context of this application + */ + public static void subscribeSteemAccount(final long accountId, final String accountSteemId, + final Context context){ + if(!currentSteemListener.containsKey(accountId)){ + CrystalDatabase db = CrystalDatabase.getAppDatabase(context); + final BitsharesAssetDao bitsharesAssetDao = db.bitsharesAssetDao(); + final CryptoCurrencyDao cryptoCurrencyDao = db.cryptoCurrencyDao(); + SubscriptionListener balanceListener = new SubscriptionListener() { + @Override + public ObjectType getInterestObjectType() { + return ObjectType.TRANSACTION_OBJECT; + } + + @Override + public void onSubscriptionUpdate(SubscriptionResponse response) { + List updatedObjects = (List) response.params.get(1); + if(updatedObjects.size() > 0){ + for(Serializable update : updatedObjects){ + if(update instanceof BroadcastedTransaction){ + BroadcastedTransaction transactionUpdate = (BroadcastedTransaction) update; + for(BaseOperation operation : transactionUpdate.getTransaction().getOperations()){ + if(operation instanceof TransferOperation){ + final TransferOperation tOperation = (TransferOperation) operation; + if(tOperation.getFrom().getObjectId().equals(accountSteemId) || tOperation.getTo().getObjectId().equals(accountSteemId)){ + GrapheneApiGenerator.getAccountBalance(accountId,accountSteemId,CryptoNet.STEEM,context); + final CryptoCoinTransaction transaction = new CryptoCoinTransaction(); + transaction.setAccountId(accountId); + transaction.setAmount(tOperation.getAssetAmount().getAmount().longValue()); + BitsharesAssetInfo info = bitsharesAssetDao.getBitsharesAssetInfoById(tOperation.getAssetAmount().getAsset().getObjectId()); + if (info == null) { + //The cryptoCurrency is not in the database, queringfor its data + ApiRequest assetRequest = new ApiRequest(0, new ApiRequestListener() { + @Override + public void success(Object answer, int idPetition) { + ArrayList assets = (ArrayList) answer; + for(BitsharesAsset asset : assets){ + + long currencyId = -1; + CryptoCurrency cryptoCurrencyDb = cryptoCurrencyDao.getByNameAndCryptoNet(((BitsharesAsset) answer).getName(),((BitsharesAsset) answer).getCryptoNet().name()); + + if (cryptoCurrencyDb != null){ + currencyId = cryptoCurrencyDb.getId(); + } else { + long idCryptoCurrency = cryptoCurrencyDao.insertCryptoCurrency(asset)[0]; + currencyId = idCryptoCurrency; + } + + BitsharesAssetInfo info = new BitsharesAssetInfo(asset); + info.setCryptoCurrencyId(currencyId); + asset.setId((int)currencyId); + bitsharesAssetDao.insertBitsharesAssetInfo(info); + saveTransaction(transaction,cryptoCurrencyDao.getById(info.getCryptoCurrencyId()),accountSteemId,tOperation,context); + } + } + + @Override + public void fail(int idPetition) { + //TODO error retrieving asset + } + }); + ArrayList assets = new ArrayList<>(); + assets.add(tOperation.getAssetAmount().getAsset().getObjectId()); + GrapheneApiGenerator.getAssetById(assets,assetRequest); + }else{ + saveTransaction(transaction,cryptoCurrencyDao.getById(info.getCryptoCurrencyId()),accountSteemId,tOperation,context); + } + } + } + } + } + } + + } + } + }; + + currentSteemListener.put(accountId,balanceListener); + steemSubscriptionHub.addSubscriptionListener(balanceListener); + + if(!steemSubscriptionThread.isConnected()){ + steemSubscriptionThread.start(); + }else if(!steemSubscriptionHub.isSubscribed()){ + steemSubscriptionHub.resubscribe(); + } + } + } + /** * Function to save a transaction retrieved from the update * @param transaction The transaction db object @@ -482,7 +594,7 @@ public abstract class GrapheneApiGenerator { * @param context The android context of this application */ public static void getAccountBalance(final long accountId, final String accountGrapheneId, - final Context context){ + CryptoNet cryptoNet, final Context context){ CrystalDatabase db = CrystalDatabase.getAppDatabase(context); final CryptoCoinBalanceDao balanceDao = db.cryptoCoinBalanceDao(); @@ -542,7 +654,7 @@ public abstract class GrapheneApiGenerator { public void onError(BaseResponse.Error error) { } - }),CryptoNetManager.getURL(CryptoNet.BITSHARES)); + }),CryptoNetManager.getURL(cryptoNet)); thread.start(); diff --git a/app/src/main/java/cy/agorise/crystalwallet/enums/CryptoCoin.java b/app/src/main/java/cy/agorise/crystalwallet/enums/CryptoCoin.java index bafc31f..e7bed2a 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/enums/CryptoCoin.java +++ b/app/src/main/java/cy/agorise/crystalwallet/enums/CryptoCoin.java @@ -18,7 +18,8 @@ public enum CryptoCoin implements Serializable { LITECOIN(CryptoNet.LITECOIN,"LTC",8,2,null), DASH(CryptoNet.DASH,"DASH",8,5,null), DOGECOIN(CryptoNet.DOGECOIN,"DOGE",8,3,null), - BITSHARES(CryptoNet.BITSHARES,"BTS",5,0,null); + BITSHARES(CryptoNet.BITSHARES,"BTS",5,0,null), + STEEM(CryptoNet.STEEM,"BTS",5,0,null); protected CryptoNet cryptoNet; protected String label; diff --git a/app/src/main/java/cy/agorise/crystalwallet/enums/CryptoNet.java b/app/src/main/java/cy/agorise/crystalwallet/enums/CryptoNet.java index 6ee7fef..d959cce 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/enums/CryptoNet.java +++ b/app/src/main/java/cy/agorise/crystalwallet/enums/CryptoNet.java @@ -4,6 +4,8 @@ import java.io.Serializable; import java.util.HashMap; import java.util.Map; +import cy.agorise.crystalwallet.R; + /** * CryptoNet Enumeration, a Crypto Net is define as the net where a CryptoCoin works, iniside the * CrypotNet is where the transaction and balance works, using the CryptoCoin Assets @@ -11,7 +13,14 @@ import java.util.Map; * Created by Henry Varona on 12/9/2017. */ public enum CryptoNet implements Serializable { - UNKNOWN("UNKNOWN",6,-1), BITCOIN("BITCOIN",6,1), BITCOIN_TEST("BITCOIN(TEST)",6,2), LITECOIN("LITECOIN",6,3), DASH("DASH",6,5), DOGECOIN("DOGECOIN",6,4), BITSHARES("BITSHARES",1,6), STEEM("STEEM",1,7); + UNKNOWN("UNKNOWN",6,-1,android.R.drawable .ic_menu_help), + BITCOIN("BITCOIN",6,1,R.drawable.coin_icon_bitcoin), + BITCOIN_TEST("BITCOIN(TEST)",6,2,R.drawable.coin_icon_bitcoin), + LITECOIN("LITECOIN",6,3,R.drawable.coin_icon_litecoin), + DASH("DASH",6,5,R.drawable.coin_icon_dash), + DOGECOIN("DOGECOIN",6,4,R.drawable.coin_icon_doge), + BITSHARES("BITSHARES",1,6,R.drawable.bts), + STEEM("STEEM",1,7,R.drawable.coin_icon_steem); protected String label; @@ -19,6 +28,8 @@ public enum CryptoNet implements Serializable { protected int bip44Index; + protected int iconImageResource; + private static Map bip44Map = new HashMap(); static { for (CryptoNet cryptoNetEnum : CryptoNet.values()) { @@ -26,10 +37,11 @@ public enum CryptoNet implements Serializable { } } - CryptoNet(String label,int confirmationsNeeded, int bip44Index){ + CryptoNet(String label,int confirmationsNeeded, int bip44Index, int iconImageResource){ this.label = label; this.confirmationsNeeded = confirmationsNeeded; this.bip44Index = bip44Index; + this.iconImageResource = iconImageResource; } public String getLabel(){ @@ -44,6 +56,10 @@ public enum CryptoNet implements Serializable { return this.bip44Index; } + public int getIconImageResource() { + return this.iconImageResource; + } + public static CryptoNet fromBip44Index(int index){ if (bip44Map.containsKey(index)) { return bip44Map.get(index); diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/GeneralAccountSeedCoinSettingsFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/GeneralAccountSeedCoinSettingsFragment.java index 45917d1..630f5e7 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/fragments/GeneralAccountSeedCoinSettingsFragment.java +++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/GeneralAccountSeedCoinSettingsFragment.java @@ -10,11 +10,13 @@ import android.view.ViewGroup; import android.widget.Toast; import java.util.ArrayList; +import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; import cy.agorise.crystalwallet.R; import cy.agorise.crystalwallet.dao.CrystalDatabase; +import cy.agorise.crystalwallet.enums.CryptoCoin; import cy.agorise.crystalwallet.enums.CryptoNet; import cy.agorise.crystalwallet.models.AccountSeed; import cy.agorise.crystalwallet.models.CryptoNetSelection; @@ -98,7 +100,10 @@ public class GeneralAccountSeedCoinSettingsFragment extends Fragment implements @Override public void onCryptoNetSelectionChecked(CryptoNetSelection source) { //Toast.makeText(this.getContext(),"the coin "+source.getCryptoNet().name()+" was "+(source.getSelected()?"selected":"unselected"),Toast.LENGTH_LONG).show(); - final CreateBitcoinAccountRequest request = new CreateBitcoinAccountRequest(this.accountSeed,this.getContext(),source.getCryptoNet()); + List cryptoCoins = CryptoCoin.getByCryptoNet(source.getCryptoNet()); + + + final CreateBitcoinAccountRequest request = new CreateBitcoinAccountRequest(this.accountSeed,this.getContext(),cryptoCoins.get(0)); request.setListener(new CryptoNetInfoRequestListener() { @Override diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/ReceiveTransactionFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/ReceiveTransactionFragment.java index acc1872..dbf3848 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/fragments/ReceiveTransactionFragment.java +++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/ReceiveTransactionFragment.java @@ -23,6 +23,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; +import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; @@ -35,6 +36,8 @@ import com.google.zxing.WriterException; import com.google.zxing.common.BitMatrix; import butterknife.OnClick; +import cy.agorise.crystalwallet.enums.CryptoCoin; +import cy.agorise.crystalwallet.enums.CryptoNet; import cy.agorise.crystalwallet.util.CircularImageView; import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountListViewModel; import cy.agorise.crystalwallet.views.CryptoNetAccountAdapter; @@ -135,46 +138,13 @@ public class ReceiveTransactionFragment extends DialogFragment implements UIVali db = CrystalDatabase.getAppDatabase(this.getContext()); this.cryptoNetAccount = db.cryptoNetAccountDao().getById(this.cryptoNetAccountId); - /* - * this is only for graphene accounts. - * - **/ - this.grapheneAccount = new GrapheneAccount(this.cryptoNetAccount); - this.grapheneAccount.loadInfo(db.grapheneAccountInfoDao().getByAccountId(this.cryptoNetAccountId)); - - final LiveData> balancesList = db.cryptoCoinBalanceDao().getBalancesFromAccount(cryptoNetAccountId); - balancesList.observe(this, new Observer>() { - @Override - public void onChanged(@Nullable List cryptoCoinBalances) { - ArrayList assetIds = new ArrayList(); - for (CryptoCoinBalance nextBalance : balancesList.getValue()) { - assetIds.add(nextBalance.getCryptoCurrencyId()); - } - List cryptoCurrencyList = db.cryptoCurrencyDao().getByIds(assetIds); - - /* - * Test - * */ - CryptoCurrency crypto1 = new CryptoCurrency(); - crypto1.setId(1); - crypto1.setName("BITCOIN"); - crypto1.setPrecision(1); - cryptoCurrencyList.add(crypto1); - - - CryptoCurrencyAdapter assetAdapter = new CryptoCurrencyAdapter(getContext(), android.R.layout.simple_spinner_item, cryptoCurrencyList); - spAsset.setAdapter(assetAdapter); - } - }); - - receiveTransactionValidator = new ReceiveTransactionValidator(this.getContext(), this.cryptoNetAccount, spAsset, etAmount); - receiveTransactionValidator.setListener(this); - CryptoNetAccountListViewModel cryptoNetAccountListViewModel = ViewModelProviders.of(this).get(CryptoNetAccountListViewModel.class); List cryptoNetAccounts = cryptoNetAccountListViewModel.getCryptoNetAccountList(); CryptoNetAccountAdapter toSpinnerAdapter = new CryptoNetAccountAdapter(this.getContext(), android.R.layout.simple_spinner_item, cryptoNetAccounts); spTo.setAdapter(toSpinnerAdapter); spTo.setSelection(0); + + setAccountUI(); } builder.setView(view); @@ -247,8 +217,59 @@ public class ReceiveTransactionFragment extends DialogFragment implements UIVali } } + public void setAccountUI(){ + if (this.cryptoNetAccount.getCryptoNet() == CryptoNet.BITSHARES) { + /* + * this is only for graphene accounts. + * + **/ + this.grapheneAccount = new GrapheneAccount(this.cryptoNetAccount); + this.grapheneAccount.loadInfo(db.grapheneAccountInfoDao().getByAccountId(this.cryptoNetAccountId)); + + final LiveData> balancesList = db.cryptoCoinBalanceDao().getBalancesFromAccount(cryptoNetAccountId); + balancesList.observe(this, new Observer>() { + @Override + public void onChanged(@Nullable List cryptoCoinBalances) { + ArrayList assetIds = new ArrayList(); + for (CryptoCoinBalance nextBalance : balancesList.getValue()) { + assetIds.add(nextBalance.getCryptoCurrencyId()); + } + List cryptoCurrencyList = db.cryptoCurrencyDao().getByIds(assetIds); + + /* + * Test + * */ + //CryptoCurrency crypto1 = new CryptoCurrency(); + //crypto1.setId(1); + //crypto1.setName("BITCOIN"); + //crypto1.setPrecision(1); + //cryptoCurrencyList.add(crypto1); + + + CryptoCurrencyAdapter assetAdapter = new CryptoCurrencyAdapter(getContext(), android.R.layout.simple_spinner_item, cryptoCurrencyList); + spAsset.setAdapter(assetAdapter); + } + }); + + receiveTransactionValidator = new ReceiveTransactionValidator(this.getContext(), this.cryptoNetAccount, spAsset, etAmount); + receiveTransactionValidator.setListener(this); + } else { + CryptoCoin cryptoCoin = CryptoCoin.getByCryptoNet(this.cryptoNetAccount.getCryptoNet()).get(0); + + List currencyList = new ArrayList<>(); + currencyList.add(cryptoCoin.getLabel()); + ArrayAdapter arrayAdapter = new ArrayAdapter(this.getContext(),android.R.layout.simple_list_item_1,currencyList); + spAsset.setAdapter(arrayAdapter); + + receiveTransactionValidator = new ReceiveTransactionValidator(this.getContext(), this.cryptoNetAccount, spAsset, etAmount); + receiveTransactionValidator.setListener(this); + } + } + @OnItemSelected(R.id.spTo) public void afterToSelected(Spinner spinner, int position) { + this.cryptoNetAccount = (CryptoNetAccount)spinner.getSelectedItem(); + setAccountUI(); this.receiveTransactionValidator.validate(); } @@ -260,7 +281,10 @@ public class ReceiveTransactionFragment extends DialogFragment implements UIVali @OnItemSelected(R.id.spAsset) public void afterAssetSelected(Spinner spinner, int position) { - this.cryptoCurrency = (CryptoCurrency)spinner.getSelectedItem(); + if (spinner.getSelectedItem() instanceof CryptoCurrency) { + this.cryptoCurrency = (CryptoCurrency) spinner.getSelectedItem(); + } + this.receiveTransactionValidator.validate(); } diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/SendTransactionFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/SendTransactionFragment.java index 767c87f..e3490f4 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/fragments/SendTransactionFragment.java +++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/SendTransactionFragment.java @@ -32,6 +32,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; +import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; @@ -44,6 +45,8 @@ import com.google.zxing.Result; import com.jaredrummler.materialspinner.MaterialSpinner; import com.vincent.filepicker.ToastUtil; +import org.bitcoinj.wallet.SendRequest; + import java.io.File; import java.math.RoundingMode; import java.text.DecimalFormat; @@ -61,7 +64,11 @@ import cy.agorise.crystalwallet.R; import cy.agorise.crystalwallet.application.CrystalSecurityMonitor; import cy.agorise.crystalwallet.dialogs.material.CrystalDialog; import cy.agorise.crystalwallet.dialogs.material.ToastIt; +import cy.agorise.crystalwallet.enums.CryptoCoin; +import cy.agorise.crystalwallet.enums.CryptoNet; import cy.agorise.crystalwallet.interfaces.OnResponse; +import cy.agorise.crystalwallet.requestmanagers.BitcoinSendRequest; +import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequest; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestListener; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests; import cy.agorise.crystalwallet.requestmanagers.ValidateBitsharesSendRequest; @@ -90,7 +97,7 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat SendTransactionValidator sendTransactionValidator; @BindView(R.id.spFrom) - MaterialSpinner spFrom; + Spinner spFrom; @BindView(R.id.tvFromError) TextView tvFromError; @BindView(R.id.etTo) @@ -195,40 +202,6 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat db = CrystalDatabase.getAppDatabase(this.getContext()); this.cryptoNetAccount = db.cryptoNetAccountDao().getById(this.cryptoNetAccountId); - /* - * this is only for graphene accounts. - * - **/ - this.grapheneAccount = new GrapheneAccount(this.cryptoNetAccount); - this.grapheneAccount.loadInfo(db.grapheneAccountInfoDao().getByAccountId(this.cryptoNetAccountId)); - - final LiveData> balancesList = db.cryptoCoinBalanceDao().getBalancesFromAccount(cryptoNetAccountId); - balancesList.observe(this, new Observer>() { - @Override - public void onChanged(@Nullable List cryptoCoinBalances) { - ArrayList assetIds = new ArrayList(); - for (CryptoCoinBalance nextBalance : balancesList.getValue()) { - assetIds.add(nextBalance.getCryptoCurrencyId()); - } - List cryptoCurrencyList = db.cryptoCurrencyDao().getByIds(assetIds); - - /* - * Test - * */ - /*CryptoCurrency crypto1 = new CryptoCurrency(); - crypto1.setId(1); - crypto1.setName("BITCOIN"); - crypto1.setPrecision(1); - cryptoCurrencyList.add(crypto1);*/ - - assetAdapter = new CryptoCurrencyAdapter(getContext(), android.R.layout.simple_spinner_item, cryptoCurrencyList); - spAsset.setAdapter(assetAdapter); - } - }); - // TODO SendTransactionValidator to accept spFrom - sendTransactionValidator = new SendTransactionValidator(this.getContext(), this.cryptoNetAccount, spFrom, etTo, spAsset, etAmount, etMemo); - sendTransactionValidator.setListener(this); - CryptoNetAccountListViewModel cryptoNetAccountListViewModel = ViewModelProviders.of(this).get(CryptoNetAccountListViewModel.class); List cryptoNetAccounts = cryptoNetAccountListViewModel.getCryptoNetAccountList(); CryptoNetAccountAdapter fromSpinnerAdapter = new CryptoNetAccountAdapter(this.getContext(), android.R.layout.simple_spinner_item, cryptoNetAccounts); @@ -236,30 +209,31 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat /* * If only one account block the control * */ - if(cryptoNetAccounts.size()==1){ - spFrom.setEnabled(false); - } + //if(cryptoNetAccounts.size()==1){ + // spFrom.setEnabled(false); + //} spFrom.setAdapter(fromSpinnerAdapter); - //spFrom.setSelection(0); + spFrom.setSelection(0); + setAccountUI(); /* * Custom material spinner implementation * */ - spFrom.setItems(cryptoNetAccounts); + //spFrom.setItems(cryptoNetAccounts); //spFrom.setSelectedIndex(0); - spFrom.setOnItemSelectedListener(new MaterialSpinner.OnItemSelectedListener() { - @Override - public void onItemSelected(MaterialSpinner view, int position, long id, CryptoNetAccount item) { - sendTransactionValidator.validate(); - } - }); - spFrom.setOnNothingSelectedListener(new MaterialSpinner.OnNothingSelectedListener() { + //spFrom.setOnItemSelectedListener(new MaterialSpinner.OnItemSelectedListener() { + // @Override + // public void onItemSelected(MaterialSpinner view, int position, long id, CryptoNetAccount item) { + // sendTransactionValidator.validate(); + // } + //}); + //spFrom.setOnNothingSelectedListener(new MaterialSpinner.OnNothingSelectedListener() { - @Override public void onNothingSelected(MaterialSpinner spinner) { + // @Override public void onNothingSelected(MaterialSpinner spinner) { - } - }); + // } + //}); // etFrom.setText(this.grapheneAccount.getName()); } @@ -305,6 +279,58 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat return builder.setView(view).create(); } + public void setAccountUI(){ + if (this.cryptoNetAccount.getCryptoNet() == CryptoNet.BITSHARES) { + /* + * this is only for graphene accounts. + * + **/ + this.grapheneAccount = new GrapheneAccount(this.cryptoNetAccount); + this.grapheneAccount.loadInfo(db.grapheneAccountInfoDao().getByAccountId(this.cryptoNetAccountId)); + + final LiveData> balancesList = db.cryptoCoinBalanceDao().getBalancesFromAccount(cryptoNetAccountId); + balancesList.observe(this, new Observer>() { + @Override + public void onChanged(@Nullable List cryptoCoinBalances) { + ArrayList assetIds = new ArrayList(); + for (CryptoCoinBalance nextBalance : balancesList.getValue()) { + assetIds.add(nextBalance.getCryptoCurrencyId()); + } + List cryptoCurrencyList = db.cryptoCurrencyDao().getByIds(assetIds); + + /* + * Test + * */ + //CryptoCurrency crypto1 = new CryptoCurrency(); + //crypto1.setId(1); + //crypto1.setName("BITCOIN"); + //crypto1.setPrecision(1); + //cryptoCurrencyList.add(crypto1); + + + CryptoCurrencyAdapter assetAdapter = new CryptoCurrencyAdapter(getContext(), android.R.layout.simple_spinner_item, cryptoCurrencyList); + spAsset.setAdapter(assetAdapter); + } + }); + + // TODO SendTransactionValidator to accept spFrom + sendTransactionValidator = new SendTransactionValidator(this.getContext(), this.cryptoNetAccount, spFrom, etTo, spAsset, etAmount, etMemo); + sendTransactionValidator.setListener(this); + + } else { + CryptoCoin cryptoCoin = CryptoCoin.getByCryptoNet(this.cryptoNetAccount.getCryptoNet()).get(0); + + List currencyList = new ArrayList<>(); + currencyList.add(cryptoCoin.getLabel()); + ArrayAdapter arrayAdapter = new ArrayAdapter(this.getContext(),android.R.layout.simple_list_item_1,currencyList); + spAsset.setAdapter(arrayAdapter); + + // TODO SendTransactionValidator to accept spFrom + sendTransactionValidator = new SendTransactionValidator(this.getContext(), this.cryptoNetAccount, spFrom, etTo, spAsset, etAmount, etMemo); + sendTransactionValidator.setListener(this); + + } + } private void requestPermission() { @@ -430,10 +456,12 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat } } - /*@OnItemSelected(R.id.spFrom) + @OnItemSelected(R.id.spFrom) public void afterFromSelected(Spinner spinner, int position) { + this.cryptoNetAccount = (CryptoNetAccount)spinner.getSelectedItem(); + setAccountUI(); this.sendTransactionValidator.validate(); - }*/ + } @OnTextChanged(value = R.id.etTo, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) @@ -576,49 +604,85 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat @OnClick(R.id.btnSend) public void sendTransaction(){ final SendTransactionFragment thisFragment = this; + final CryptoNetInfoRequest sendRequest; if (this.sendTransactionValidator.isValid()) { - CryptoNetAccount fromAccountSelected = (CryptoNetAccount) spFrom.getItems().get(spFrom.getSelectedIndex()); + //CryptoNetAccount fromAccountSelected = (CryptoNetAccount) spFrom.getItems().get(spFrom.getSelectedIndex()); + CryptoNetAccount fromAccountSelected = (CryptoNetAccount) spFrom.getSelectedItem(); - /* - * this is only for graphene accounts. - * - **/ - GrapheneAccount grapheneAccountSelected = new GrapheneAccount(fromAccountSelected); - grapheneAccountSelected.loadInfo(db.grapheneAccountInfoDao().getByAccountId(fromAccountSelected.getId())); + if (fromAccountSelected.getCryptoNet() == CryptoNet.BITSHARES) { + /* + * this is only for graphene accounts. + * + **/ + GrapheneAccount grapheneAccountSelected = new GrapheneAccount(fromAccountSelected); + grapheneAccountSelected.loadInfo(db.grapheneAccountInfoDao().getByAccountId(fromAccountSelected.getId())); + //TODO convert the amount to long type using the precision of the currency + Double amountFromEditText = Double.parseDouble(this.etAmount.getText().toString()); + Long amount = (long) Math.floor(amountFromEditText * Math.round(Math.pow(10, ((CryptoCurrency) spAsset.getSelectedItem()).getPrecision()))); - //TODO convert the amount to long type using the precision of the currency - Double amountFromEditText = Double.parseDouble(this.etAmount.getText().toString()); - Long amount = (long)Math.floor(amountFromEditText*Math.round(Math.pow(10,((CryptoCurrency)spAsset.getSelectedItem()).getPrecision()))); + /*final ValidateBitsharesSendRequest*/ + sendRequest = new ValidateBitsharesSendRequest( + this.getContext(), + grapheneAccountSelected, + this.etTo.getText().toString(), + amount, + ((CryptoCurrency) spAsset.getSelectedItem()).getName(), + etMemo.getText().toString() + ); - final ValidateBitsharesSendRequest sendRequest = new ValidateBitsharesSendRequest( - this.getContext(), - grapheneAccountSelected, - this.etTo.getText().toString(), - amount, - ((CryptoCurrency)spAsset.getSelectedItem()).getName(), - etMemo.getText().toString() - ); - - sendRequest.setListener(new CryptoNetInfoRequestListener() { - @Override - public void onCarryOut() { - if (sendRequest.getStatus().equals(ValidateBitsharesSendRequest.StatusCode.SUCCEEDED)){ - try { - crystalDialog.dismiss(); - thisFragment.dismiss(); - //thisFragment.finalize(); - } catch (Throwable throwable) { - throwable.printStackTrace(); + sendRequest.setListener(new CryptoNetInfoRequestListener() { + @Override + public void onCarryOut() { + if (((ValidateBitsharesSendRequest)sendRequest).getStatus().equals(ValidateBitsharesSendRequest.StatusCode.SUCCEEDED)) { + try { + crystalDialog.dismiss(); + thisFragment.dismiss(); + //thisFragment.finalize(); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + } else { + Toast.makeText(getContext(), getContext().getString(R.string.unable_to_send_amount), Toast.LENGTH_LONG); } - } else { - Toast.makeText(getContext(), getContext().getString(R.string.unable_to_send_amount), Toast.LENGTH_LONG); } - } - }); + }); + } else { + CryptoCoin cryptoCoin = CryptoCoin.getByCryptoNet(this.cryptoNetAccount.getCryptoNet()).get(0); + + //TODO convert the amount to long type using the precision of the currency + Double amountFromEditText = Double.parseDouble(this.etAmount.getText().toString()); + Long amount = (long) Math.floor(amountFromEditText * (Math.pow(10, cryptoCoin.getPrecision()))); + + sendRequest = new BitcoinSendRequest( + this.getContext(), + this.cryptoNetAccount, + this.etTo.getText().toString(), + amount, + cryptoCoin, + etMemo.getText().toString() + ); + + sendRequest.setListener(new CryptoNetInfoRequestListener() { + @Override + public void onCarryOut() { + if (((BitcoinSendRequest)sendRequest).getStatus().equals(ValidateBitsharesSendRequest.StatusCode.SUCCEEDED)) { + try { + crystalDialog.dismiss(); + thisFragment.dismiss(); + //thisFragment.finalize(); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + } else { + Toast.makeText(getContext(), getContext().getString(R.string.unable_to_send_amount), Toast.LENGTH_LONG); + } + } + }); + } /* * If exists mode scurity show it and valide events in case of success or fail diff --git a/app/src/main/java/cy/agorise/crystalwallet/manager/BitsharesAccountManager.java b/app/src/main/java/cy/agorise/crystalwallet/manager/BitsharesAccountManager.java index eee12bf..ba6ada9 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/manager/BitsharesAccountManager.java +++ b/app/src/main/java/cy/agorise/crystalwallet/manager/BitsharesAccountManager.java @@ -213,7 +213,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI private void subscribeBitsharesAccount(long accountId, String accountBitsharesID, Context context){ GrapheneApiGenerator.subscribeBitsharesAccount(accountId,accountBitsharesID,context); BitsharesAccountManager.refreshAccountTransactions(accountId,context); - GrapheneApiGenerator.getAccountBalance(accountId,accountBitsharesID,context); + GrapheneApiGenerator.getAccountBalance(accountId,accountBitsharesID,CryptoNet.BITSHARES,context); } /** @@ -693,7 +693,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI int stop = start + limit; ApiRequest transactionRequest = new ApiRequest(0, new TransactionRequestListener(start, stop, limit, grapheneAccount, db)); - GrapheneApiGenerator.getAccountTransaction(grapheneAccount.getAccountId(), start, stop, limit, transactionRequest); + GrapheneApiGenerator.getAccountTransaction(grapheneAccount.getAccountId(), start, stop, limit, CryptoNet.BITSHARES,transactionRequest); } } @@ -796,7 +796,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI int newStart= start + limit; int newStop= stop + limit; ApiRequest transactionRequest = new ApiRequest(newStart/limit, new TransactionRequestListener(newStart,newStop,limit,account,db)); - GrapheneApiGenerator.getAccountTransaction(account.getAccountId(),newStart,newStop,limit,transactionRequest); + GrapheneApiGenerator.getAccountTransaction(account.getAccountId(),newStart,newStop,limit,CryptoNet.BITSHARES,transactionRequest); } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/manager/GeneralAccountManager.java b/app/src/main/java/cy/agorise/crystalwallet/manager/GeneralAccountManager.java index c051b8b..9b49076 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/manager/GeneralAccountManager.java +++ b/app/src/main/java/cy/agorise/crystalwallet/manager/GeneralAccountManager.java @@ -3,6 +3,7 @@ package cy.agorise.crystalwallet.manager; import android.content.Context; import org.bitcoinj.core.Address; +import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.Coin; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.Sha256Hash; @@ -41,6 +42,7 @@ import cy.agorise.crystalwallet.requestmanagers.CreateBitcoinAccountRequest; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequest; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestsListener; import cy.agorise.crystalwallet.requestmanagers.NextBitcoinAccountAddressRequest; +import cy.agorise.crystalwallet.requestmanagers.ValidateBitcoinAddressRequest; import cy.agorise.graphenej.Util; public class GeneralAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener { @@ -93,6 +95,12 @@ public class GeneralAccountManager implements CryptoAccountManager, CryptoNetInf final DeterministicKey changeKey = HDKeyDerivation.deriveChildKey(accountKey, new ChildNumber(1, false)); + CryptoCoinBalance balance = new CryptoCoinBalance(); + balance.setBalance(0); + balance.setCryptoCurrencyId(db.cryptoCurrencyDao().getByName(cryptoCoin.name(),cryptoCoin.name()).getId()); + balance.setAccountId(account.getId()); + db.cryptoCoinBalanceDao().insertCryptoCoinBalance(balance); + long indexExternal = db.bitcoinAddressDao().getLastExternalAddress(account.getId()); if(indexExternal > 0){ for(int i = 0; i < indexExternal;i++){ @@ -143,6 +151,8 @@ public class GeneralAccountManager implements CryptoAccountManager, CryptoNetInf this.createGeneralAccount((CreateBitcoinAccountRequest) request); }else if(request instanceof NextBitcoinAccountAddressRequest){ this.getNextAddress((NextBitcoinAccountAddressRequest) request); + }else if(request instanceof ValidateBitcoinAddressRequest){ + this.validateAddress((ValidateBitcoinAddressRequest) request); }else{ System.out.println("Invalid " +this.cryptoCoin.getLabel() + " request "); } @@ -325,6 +335,16 @@ public class GeneralAccountManager implements CryptoAccountManager, CryptoNetInf db.cryptoCoinBalanceDao().insertCryptoCoinBalance(balance); } + private void validateAddress(ValidateBitcoinAddressRequest request){ + try{ + Address address = Address.fromBase58(this.cryptoCoin.getParameters(), request.getAddress()); + request.setAddressValid(true); + }catch(AddressFormatException ex){ + request.setAddressValid(false); + } + request.validate(); + } + public void send(final BitcoinSendRequest request){ //TODO check server connection //TODO validate to address diff --git a/app/src/main/java/cy/agorise/crystalwallet/manager/SteemAccountManager.java b/app/src/main/java/cy/agorise/crystalwallet/manager/SteemAccountManager.java new file mode 100644 index 0000000..997b1ce --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/manager/SteemAccountManager.java @@ -0,0 +1,825 @@ +package cy.agorise.crystalwallet.manager; + +import android.annotation.SuppressLint; +import android.content.Context; + +import com.google.common.primitives.UnsignedLong; + +import org.bitcoinj.core.ECKey; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.TimeZone; + +import cy.agorise.crystalwallet.apigenerator.ApiRequest; +import cy.agorise.crystalwallet.apigenerator.ApiRequestListener; +import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator; +import cy.agorise.crystalwallet.dao.AccountSeedDao; +import cy.agorise.crystalwallet.dao.CrystalDatabase; +import cy.agorise.crystalwallet.dao.TransactionDao; +import cy.agorise.crystalwallet.enums.CryptoCoin; +import cy.agorise.crystalwallet.enums.CryptoNet; +import cy.agorise.crystalwallet.enums.SeedType; +import cy.agorise.crystalwallet.models.AccountSeed; +import cy.agorise.crystalwallet.models.BitsharesAccountNameCache; +import cy.agorise.crystalwallet.models.BitsharesAsset; +import cy.agorise.crystalwallet.models.BitsharesAssetInfo; +import cy.agorise.crystalwallet.models.CryptoCoinTransaction; +import cy.agorise.crystalwallet.models.CryptoCurrency; +import cy.agorise.crystalwallet.models.CryptoNetAccount; +import cy.agorise.crystalwallet.models.GrapheneAccount; +import cy.agorise.crystalwallet.models.GrapheneAccountInfo; +import cy.agorise.crystalwallet.models.seed.BIP39; +import cy.agorise.crystalwallet.network.CryptoNetManager; +import cy.agorise.crystalwallet.requestmanagers.CryptoNetEquivalentRequest; +import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequest; +import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestsListener; +import cy.agorise.crystalwallet.requestmanagers.GetBitsharesAccountNameCacheRequest; +import cy.agorise.crystalwallet.requestmanagers.ImportBitsharesAccountRequest; +import cy.agorise.crystalwallet.requestmanagers.ValidateBitsharesSendRequest; +import cy.agorise.crystalwallet.requestmanagers.ValidateExistBitsharesAccountRequest; +import cy.agorise.crystalwallet.requestmanagers.ValidateImportBitsharesAccountRequest; +import cy.agorise.graphenej.Address; +import cy.agorise.graphenej.Asset; +import cy.agorise.graphenej.AssetAmount; +import cy.agorise.graphenej.BaseOperation; +import cy.agorise.graphenej.BrainKey; +import cy.agorise.graphenej.PublicKey; +import cy.agorise.graphenej.Transaction; +import cy.agorise.graphenej.UserAccount; +import cy.agorise.graphenej.models.AccountProperties; +import cy.agorise.graphenej.models.BlockHeader; +import cy.agorise.graphenej.models.HistoricalTransfer; +import cy.agorise.graphenej.operations.TransferOperationBuilder; + +/** + * The manager for the Bitshare CryptoCoin + * + * Created by henry on 26/9/2017. + */ +public class SteemAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener { + + private final static String SIMPLE_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; + private final static String DEFAULT_TIME_ZONE = "GMT"; + + @Override + public void createAccountFromSeed(CryptoNetAccount account, final ManagerRequest request, final Context context) { + //TODO error, can't create steem accounts + } + + @Override + public void importAccountFromSeed(CryptoNetAccount account, final Context context) { + if(account instanceof GrapheneAccount) { + final GrapheneAccount grapheneAccount = (GrapheneAccount) account; + + if(grapheneAccount.getAccountId() == null){ + this.getAccountInfoByName(grapheneAccount.getName(), new ManagerRequest() { + @Override + public void success(Object answer) { + GrapheneAccount fetch = (GrapheneAccount) answer; + grapheneAccount.setAccountId(fetch.getAccountId()); + CrystalDatabase db = CrystalDatabase.getAppDatabase(context); + long[] idAccount = db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount); + grapheneAccount.setId(idAccount[0]); + db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount)); + subscribeSteemAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context); + } + + @Override + public void fail() { + //TODO get account data fail + } + }); + + }else if(grapheneAccount.getName() == null){ + this.getAccountInfoById(grapheneAccount.getAccountId(), new ManagerRequest() { + @Override + public void success(Object answer) { + GrapheneAccount fetch = (GrapheneAccount) answer; + grapheneAccount.setName(fetch.getName()); + CrystalDatabase db = CrystalDatabase.getAppDatabase(context); + db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount); + db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount)); + subscribeSteemAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context); + } + + @Override + public void fail() { + //TODO get account data fail + } + }); + }else { + CrystalDatabase db = CrystalDatabase.getAppDatabase(context); + db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount); + db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount)); + subscribeSteemAccount(grapheneAccount.getId(), grapheneAccount.getAccountId(), context); + } + } + } + + @Override + public void loadAccountFromDB(CryptoNetAccount account, final Context context) { + if(account instanceof GrapheneAccount){ + final GrapheneAccount grapheneAccount = (GrapheneAccount) account; + final CrystalDatabase db = CrystalDatabase.getAppDatabase(context); + final GrapheneAccountInfo info = db.grapheneAccountInfoDao().getByAccountId(account.getId()); + grapheneAccount.loadInfo(info); + if(grapheneAccount.getAccountId() == null){ + this.getAccountInfoByName(grapheneAccount.getName(), new ManagerRequest() { + @Override + public void success(Object answer) { + GrapheneAccount fetch = (GrapheneAccount) answer; + info.setAccountId(fetch.getAccountId()); + grapheneAccount.setAccountId(fetch.getAccountId()); + db.grapheneAccountInfoDao().insertGrapheneAccountInfo(info); + subscribeSteemAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context); + } + + @Override + public void fail() { + //TODO account data retrieve failed + } + }); + }else if(grapheneAccount.getName() == null){ + this.getAccountInfoById(grapheneAccount.getAccountId(), new ManagerRequest() { + @Override + public void success(Object answer) { + GrapheneAccount fetch = (GrapheneAccount) answer; + info.setName(fetch.getName()); + grapheneAccount.setName(fetch.getName()); + db.grapheneAccountInfoDao().insertGrapheneAccountInfo(info); + subscribeSteemAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context); + } + + @Override + public void fail() { + //TODO account data retrieve failed + } + }); + }else{ + subscribeSteemAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context); + } + } + } + + private void subscribeSteemAccount(long accountId, String accountSteemID, Context context){ + GrapheneApiGenerator.subscribeSteemAccount(accountId,accountSteemID,context); + SteemAccountManager.refreshAccountTransactions(accountId,context); + GrapheneApiGenerator.getAccountBalance(accountId,accountSteemID,CryptoNet.STEEM,context); + } + + /** + * Process the bitshares manager request + * @param request The request Object + */ + @Override + public void onNewRequest(CryptoNetInfoRequest request) { + if(request.getCoin().equals(CryptoCoin.STEEM)) { + if (request instanceof ImportBitsharesAccountRequest) { + this.importAccount((ImportBitsharesAccountRequest) request); + } else if (request instanceof ValidateImportBitsharesAccountRequest) { + this.validateImportAccount((ValidateImportBitsharesAccountRequest) request); + } else if (request instanceof ValidateExistBitsharesAccountRequest) { + this.validateExistAcccount((ValidateExistBitsharesAccountRequest) request); + } else if (request instanceof ValidateBitsharesSendRequest) { + this.validateSendRequest((ValidateBitsharesSendRequest) request); + } else if (request instanceof CryptoNetEquivalentRequest) { + this.getEquivalentValue((CryptoNetEquivalentRequest) request); + } else if (request instanceof GetBitsharesAccountNameCacheRequest) { + this.getBitsharesAccountNameCacheRequest((GetBitsharesAccountNameCacheRequest) request); + } else { + + //TODO not implemented + System.out.println("Error request not implemented " + request.getClass().getName()); + } + } + } + + private void importAccount(final ImportBitsharesAccountRequest importRequest){ + final CrystalDatabase db = CrystalDatabase.getAppDatabase(importRequest.getContext()); + final AccountSeedDao accountSeedDao = db.accountSeedDao(); + ApiRequest getAccountNamesBK = new ApiRequest(0, new ApiRequestListener() { + @Override + public void success(Object answer, int idPetition) { + if(answer != null && importRequest.getStatus().equals(ImportBitsharesAccountRequest.StatusCode.NOT_STARTED)) { + UserAccount userAccount = (UserAccount) answer; + importRequest.setSeedType(SeedType.BRAINKEY); + importRequest.setStatus(ImportBitsharesAccountRequest.StatusCode.SUCCEEDED); + + AccountSeed seed = new AccountSeed(); + seed.setName(userAccount.getName()); + seed.setType(importRequest.getSeedType()); + seed.setMasterSeed(importRequest.getMnemonic()); + long idSeed = accountSeedDao.insertAccountSeed(seed); + if (idSeed >= 0) { + GrapheneAccount account = new GrapheneAccount(); + account.setCryptoNet(CryptoNet.BITSHARES); + account.setAccountIndex(0); + account.setSeedId(idSeed); + account.setAccountId(userAccount.getObjectId()); + importAccountFromSeed(account, importRequest.getContext()); + } + } + } + + @Override + public void fail(int idPetition) { + BIP39 bip39 = new BIP39(-1, importRequest.getMnemonic()); + ApiRequest getAccountNamesBP39 = new ApiRequest(0, new ApiRequestListener() { + @Override + public void success(Object answer, int idPetition) { + if(answer != null && importRequest.getStatus().equals(ImportBitsharesAccountRequest.StatusCode.NOT_STARTED)) { + UserAccount userAccount = (UserAccount) answer; + importRequest.setSeedType(SeedType.BIP39); + importRequest.setStatus(ImportBitsharesAccountRequest.StatusCode.SUCCEEDED); + + AccountSeed seed = new AccountSeed(); + seed.setName(userAccount.getName()); + seed.setType(importRequest.getSeedType()); + seed.setMasterSeed(importRequest.getMnemonic()); + long idSeed = accountSeedDao.insertAccountSeed(seed); + if (idSeed >= 0) { + GrapheneAccount account = new GrapheneAccount(); + account.setCryptoNet(CryptoNet.BITSHARES); + account.setAccountIndex(0); + account.setSeedId(idSeed); + account.setAccountId(userAccount.getObjectId()); + importAccountFromSeed(account, importRequest.getContext()); + } + } + } + + @Override + public void fail(int idPetition) { + importRequest.setStatus(ImportBitsharesAccountRequest.StatusCode.BAD_SEED); + } + }); + GrapheneApiGenerator.getAccountByOwnerOrActiveAddress(new Address(ECKey.fromPublicOnly(bip39.getBitsharesActiveKey(0).getPubKey())),getAccountNamesBP39); + } + }); + + + + BrainKey bk = new BrainKey(importRequest.getMnemonic(), 0); + + GrapheneApiGenerator.getAccountByOwnerOrActiveAddress(bk.getPublicAddress("BTS"),getAccountNamesBK); + + + } + + /** + * Process the import account request + */ + private void validateImportAccount(final ValidateImportBitsharesAccountRequest importRequest){ + //TODO check internet and server status + final CrystalDatabase db = CrystalDatabase.getAppDatabase(importRequest.getContext()); + final AccountSeedDao accountSeedDao = db.accountSeedDao(); + + ApiRequest checkAccountName = new ApiRequest(0, new ApiRequestListener() { + @Override + public void success(Object answer, int idPetition) { + ApiRequest getAccountInfo = new ApiRequest(1,new ApiRequestListener(){ + @Override + public void success(Object answer, int idPetition) { + if(answer != null && answer instanceof AccountProperties) { + AccountProperties prop = (AccountProperties) answer; + BrainKey bk = new BrainKey(importRequest.getMnemonic(), 0); + for(PublicKey activeKey : prop.owner.getKeyAuthList()){ + if((new Address(activeKey.getKey(),"BTS")).toString().equals(bk.getPublicAddress("BTS").toString())){ + importRequest.setSeedType(SeedType.BRAINKEY); + importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.SUCCEEDED); + + break; + } + } + BIP39 bip39 = new BIP39(-1, importRequest.getMnemonic()); + for(PublicKey activeKey : prop.active.getKeyAuthList()){ + if((new Address(activeKey.getKey(),"BTS")).toString().equals(new Address(ECKey.fromPublicOnly(bip39.getBitsharesActiveKey(0).getPubKey())).toString())){ + importRequest.setSeedType(SeedType.BIP39); + importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.SUCCEEDED); + break; + } + } + + if ((importRequest.getStatus() == ValidateImportBitsharesAccountRequest.StatusCode.SUCCEEDED)){ + if (importRequest.addAccountIfValid()) { + AccountSeed seed = new AccountSeed(); + seed.setName(importRequest.getAccountName()); + seed.setType(importRequest.getSeedType()); + seed.setMasterSeed(importRequest.getMnemonic()); + long idSeed = accountSeedDao.insertAccountSeed(seed); + if (idSeed >= 0) { + GrapheneAccount account = new GrapheneAccount(); + account.setCryptoNet(CryptoNet.BITSHARES); + account.setAccountIndex(0); + account.setSeedId(idSeed); + account.setName(importRequest.getAccountName()); + importAccountFromSeed(account, importRequest.getContext()); + } + } + return; + } + + importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.BAD_SEED); + } + importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.PETITION_FAILED); + + } + + @Override + public void fail(int idPetition) { + // + importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.NO_ACCOUNT_DATA); + } + }); + GrapheneApiGenerator.getAccountById((String)answer,getAccountInfo); + } + + @Override + public void fail(int idPetition) { + // + importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.ACCOUNT_DOESNT_EXIST); + } + }); + + GrapheneApiGenerator.getAccountIdByName(importRequest.getAccountName(),checkAccountName); + } + + /** + * Process the account exist request, it consults the bitshares api for the account name. + * + * This can be used to know if the name is avaible, or the account to be send fund exists + */ + private void validateExistAcccount(final ValidateExistBitsharesAccountRequest validateRequest){ + ApiRequest checkAccountName = new ApiRequest(0, new ApiRequestListener() { + @Override + public void success(Object answer, int idPetition) { + if (answer != null) { + validateRequest.setAccountExists(true); + } else { + validateRequest.setAccountExists(false); + } + } + + @Override + public void fail(int idPetition) { + //TODO verified + validateRequest.setAccountExists(false); + } + }); + GrapheneApiGenerator.getAccountIdByName(validateRequest.getAccountName(),checkAccountName); + } + + /** + * Broadcast a transaction request + */ + private void validateSendRequest(final ValidateBitsharesSendRequest sendRequest) { + //TODO check internet, server connection + //TODO feeAsset + CrystalDatabase db = CrystalDatabase.getAppDatabase(sendRequest.getContext()); + CryptoCurrency currency = db.cryptoCurrencyDao().getByNameAndCryptoNet(sendRequest.getAsset(), CryptoNet.BITSHARES.name()); + if (currency == null){ + getAssetInfoByName(sendRequest.getAsset(), new ManagerRequest() { + @Override + public void success(Object answer) { + validateSendRequest(sendRequest, ((BitsharesAsset) answer).getBitsharesId()); + } + + @Override + public void fail() { + sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.NO_ASSET_INFO_DB); + } + }); + }else{ + BitsharesAssetInfo info = db.bitsharesAssetDao().getBitsharesAssetInfo(currency.getId()); + if (info == null || info.getBitsharesId() == null || info.getBitsharesId().isEmpty()){ + getAssetInfoByName(sendRequest.getAsset(), new ManagerRequest() { + @Override + public void success(Object answer) { + validateSendRequest(sendRequest, ((BitsharesAsset) answer).getBitsharesId()); + } + + @Override + public void fail() { + sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.NO_ASSET_INFO); + } + }); + }else { + this.validateSendRequest(sendRequest, info.getBitsharesId()); + } + } + } + + /** + * Broadcast a send asset request, the idAsset is already fetched + * @param sendRequest The petition for transfer + * @param idAsset The Bitshares Asset's id + */ + private void validateSendRequest(final ValidateBitsharesSendRequest sendRequest , final String idAsset){ + final Asset feeAsset = new Asset(idAsset); + final UserAccount fromUserAccount =new UserAccount(sendRequest.getSourceAccount().getAccountId()); + + final CrystalDatabase db = CrystalDatabase.getAppDatabase(sendRequest.getContext()); + BitsharesAccountNameCache cacheAccount = db.bitsharesAccountNameCacheDao().getByAccountName(sendRequest.getToAccount()); + if(cacheAccount == null) { + this.getAccountInfoByName(sendRequest.getToAccount(), new ManagerRequest() { + + @Override + public void success(Object answer) { + GrapheneAccount toUserGrapheneAccount = (GrapheneAccount) answer; + UserAccount toUserAccount = new UserAccount(toUserGrapheneAccount.getAccountId()); + try { + BitsharesAccountNameCache cacheAccount = new BitsharesAccountNameCache(); + cacheAccount.setName(sendRequest.getToAccount()); + cacheAccount.setAccountId(toUserAccount.getObjectId()); + db.bitsharesAccountNameCacheDao().insertBitsharesAccountNameCache(cacheAccount); + }catch(Exception e){ + e.printStackTrace(); + } + validateSendRequest(sendRequest,fromUserAccount,toUserAccount,feeAsset,idAsset); + } + + @Override + public void fail() { + sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.NO_TO_USER_INFO); + } + }); + }else { + UserAccount toUserAccount = new UserAccount(cacheAccount.getAccountId()); + this.validateSendRequest(sendRequest,fromUserAccount,toUserAccount,feeAsset,idAsset); + } + } + + /** + * Broadcast a transaction request + * @param sendRequest The petition for transfer operation + * @param fromUserAccount The source account + * @param toUserAccount The receiver account + * @param feeAsset The Fee Asset + * @param idAsset The id of the asset to be used on the operation + */ + private void validateSendRequest(final ValidateBitsharesSendRequest sendRequest, UserAccount fromUserAccount, + UserAccount toUserAccount, Asset feeAsset, String idAsset){ + TransferOperationBuilder builder = new TransferOperationBuilder() + .setSource(fromUserAccount) + .setDestination(toUserAccount) + .setTransferAmount(new AssetAmount(UnsignedLong.valueOf(sendRequest.getAmount()), new Asset(idAsset))) + .setFee(new AssetAmount(UnsignedLong.valueOf(0), feeAsset)); + if (sendRequest.getMemo() != null) { + //builder.setMemo(new Memo(fromUserAccount,toUserAccount,0,sendRequest.getMemo().getBytes())); + //TODO memo + System.out.println("transaction has memo"); + } + ArrayList operationList = new ArrayList<>(); + operationList.add(builder.build()); + + ECKey privateKey = sendRequest.getSourceAccount().getActiveKey(sendRequest.getContext()); + + Transaction transaction = new Transaction(privateKey, null, operationList); + transaction.setChainId(CryptoNetManager.getChaindId(CryptoNet.BITSHARES)); + + ApiRequest transactionRequest = new ApiRequest(0, new ApiRequestListener() { + @Override + public void success(Object answer, int idPetition) { + sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.SUCCEEDED); + } + + @Override + public void fail(int idPetition) { + sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.PETITION_FAILED); + } + }); + + GrapheneApiGenerator.broadcastTransaction(transaction, feeAsset, transactionRequest); + } + + private void getBitsharesAccountNameCacheRequest(final GetBitsharesAccountNameCacheRequest request){ + final CrystalDatabase db = CrystalDatabase.getAppDatabase(request.getContext()); + BitsharesAccountNameCache cacheAccount = db.bitsharesAccountNameCacheDao().getByAccountId(request.getAccountId()); + if(cacheAccount == null) { + this.getAccountInfoById(request.getAccountId(), new ManagerRequest() { + + @Override + public void success(Object answer) { + GrapheneAccount userGrapheneAccount = (GrapheneAccount) answer; + BitsharesAccountNameCache cacheAccount = new BitsharesAccountNameCache(); + cacheAccount.setName(userGrapheneAccount.getName()); + cacheAccount.setAccountId(request.getAccountId()); + db.bitsharesAccountNameCacheDao().insertBitsharesAccountNameCache(cacheAccount); + request.setAccountName(userGrapheneAccount.getName()); + } + + @Override + public void fail() { + //TODO error + } + }); + }else { + request.setAccountName(cacheAccount.getName()); + } + } + + /** + * Returns the account info from a graphene id + * @param grapheneId The graphene id of the account + */ + private void getAccountInfoById(String grapheneId, ManagerRequest request){ + + AccountIdOrNameListener listener = new AccountIdOrNameListener(request); + + ApiRequest accountRequest = new ApiRequest(0, listener); + GrapheneApiGenerator.getAccountById(grapheneId,accountRequest); + } + + /** + * Gets account info by its name + * @param grapheneName The name of the account to retrieve + */ + private void getAccountInfoByName(String grapheneName, ManagerRequest request){ + + AccountIdOrNameListener listener = new AccountIdOrNameListener(request); + + ApiRequest accountRequest = new ApiRequest(0, listener); + GrapheneApiGenerator.getAccountByName(grapheneName,accountRequest); + + } + + //TODO expand function to be more generic + private void getAssetInfoByName(String assetName, ManagerRequest request){ + + AssetIdOrNameListener nameListener = new AssetIdOrNameListener(request); + ApiRequest assetRequest = new ApiRequest(0, nameListener); + ArrayList assetNames = new ArrayList<>(); + assetNames.add(assetName); + GrapheneApiGenerator.getAssetByName(assetNames, assetRequest); + + } + + /** + * Refresh the transactions of an account, important to notice, it return nothing, to get the changes tuse the LiveData + * @param idAccount database id of the account + * @param context The android context of this application + */ + private static void refreshAccountTransactions(long idAccount, Context context){ + CrystalDatabase db = CrystalDatabase.getAppDatabase(context); + List transactions = db.transactionDao().getByIdAccount(idAccount); + CryptoNetAccount account = db.cryptoNetAccountDao().getById(idAccount); + if(account.getCryptoNet() == CryptoNet.STEEM) { + + GrapheneAccount grapheneAccount = new GrapheneAccount(account); + grapheneAccount.loadInfo(db.grapheneAccountInfoDao().getByAccountId(idAccount)); + + int start = transactions.size(); + int limit = 50; + int stop = start + limit; + + ApiRequest transactionRequest = new ApiRequest(0, new TransactionRequestListener(start, stop, limit, grapheneAccount, db)); + GrapheneApiGenerator.getAccountTransaction(grapheneAccount.getAccountId(), start, stop, limit, CryptoNet.STEEM,transactionRequest); + } + } + + /** + * Class that handles the transactions request + */ + private static class TransactionRequestListener implements ApiRequestListener{ + + /** + * Start index + */ + int start; + /** + * End index + */ + int stop; + /** + * Limit of transasction to fetch + */ + int limit; + /** + * The grapheneaccount with all data CryptoCurrnecy + info + */ + GrapheneAccount account; + /** + * The database + */ + CrystalDatabase db; + + /** + * Basic consturctor + */ + TransactionRequestListener(int start, int stop, int limit, GrapheneAccount account, CrystalDatabase db) { + this.start = start; + this.stop = stop; + this.limit = limit; + this.account = account; + this.db = db; + } + + /** + * Handles the success request of the transaction, if the amount of transaction is equal to the limit, ask for more transaction + * @param answer The answer, this object depends on the kind of request is made to the api + * @param idPetition the id of the ApiRequest petition + */ + @Override + public void success(Object answer, int idPetition) { + List transfers = (List) answer ; + for(final HistoricalTransfer transfer : transfers) { + if (transfer.getOperation() != null){ + final CryptoCoinTransaction transaction = new CryptoCoinTransaction(); + transaction.setAccountId(account.getId()); + transaction.setAmount(transfer.getOperation().getAssetAmount().getAmount().longValue()); + BitsharesAssetInfo info = db.bitsharesAssetDao().getBitsharesAssetInfoById(transfer.getOperation().getAssetAmount().getAsset().getObjectId()); + + if (info == null) { + //The cryptoCurrency is not in the database, queringfor its data + ApiRequest assetRequest = new ApiRequest(0, new ApiRequestListener() { + @Override + public void success(Object answer, int idPetition) { + ArrayList assets = (ArrayList) answer; + for(BitsharesAsset asset : assets){ + long currencyId = -1; + CryptoCurrency cryptoCurrencyDb = db.cryptoCurrencyDao().getByNameAndCryptoNet(asset.getName(),asset.getCryptoNet().name()); + + if (cryptoCurrencyDb != null){ + currencyId = cryptoCurrencyDb.getId(); + } else { + long idCryptoCurrency = db.cryptoCurrencyDao().insertCryptoCurrency(asset)[0]; + currencyId = idCryptoCurrency; + } + + + BitsharesAssetInfo info = new BitsharesAssetInfo(asset); + info.setCryptoCurrencyId(currencyId); + asset.setId((int)currencyId); + db.bitsharesAssetDao().insertBitsharesAssetInfo(info); + saveTransaction(transaction,info,transfer); + } + + } + + @Override + public void fail(int idPetition) { + //TODO Error + } + }); + ArrayList assets = new ArrayList<>(); + assets.add(transfer.getOperation().getAssetAmount().getAsset().getObjectId()); + GrapheneApiGenerator.getAssetById(assets,assetRequest); + + }else{ + saveTransaction(transaction,info,transfer); + } + + } + } + if(transfers.size()>= limit){ + // The amount of transaction in the answer is equal to the limit, we need to query to see if there is more transactions + int newStart= start + limit; + int newStop= stop + limit; + ApiRequest transactionRequest = new ApiRequest(newStart/limit, new TransactionRequestListener(newStart,newStop,limit,account,db)); + GrapheneApiGenerator.getAccountTransaction(account.getAccountId(),newStart,newStop,limit,CryptoNet.STEEM,transactionRequest); + } + } + + @Override + public void fail(int idPetition) { + + } + + private void saveTransaction(CryptoCoinTransaction transaction, BitsharesAssetInfo info, HistoricalTransfer transfer){ + transaction.setIdCurrency((int)info.getCryptoCurrencyId()); + transaction.setConfirmed(true); //graphene transaction are always confirmed + transaction.setFrom(transfer.getOperation().getFrom().getObjectId()); + transaction.setInput(!transfer.getOperation().getFrom().getObjectId().equals(account.getAccountId())); + transaction.setTo(transfer.getOperation().getTo().getObjectId()); + + GrapheneApiGenerator.getBlockHeaderTime(transfer.getBlockNum(), new ApiRequest(0, new GetTransactionDate(transaction, db.transactionDao()))); + } + } + + /** + * Gets the current change from two assets + */ + private void getEquivalentValue(final CryptoNetEquivalentRequest request){ + if(request.getFromCurrency() instanceof BitsharesAsset && request.getToCurrency() instanceof BitsharesAsset) { + BitsharesAsset fromAsset = (BitsharesAsset) request.getFromCurrency(); + BitsharesAsset toAsset = (BitsharesAsset) request.getToCurrency(); + ApiRequest getEquivalentRequest = new ApiRequest(0, new ApiRequestListener() { + @Override + public void success(Object answer, int idPetition) { + request.setPrice((Long)answer); + } + + @Override + public void fail(int idPetition) { + //TODO error + } + }); + GrapheneApiGenerator.getEquivalentValue(fromAsset.getBitsharesId(),toAsset.getBitsharesId(), getEquivalentRequest); + }else{ + //TODO error + System.out.println("Equivalent Value error "); + } + } + + /** + * Class to retrieve the account id or the account name, if one of those is missing + */ + private class AccountIdOrNameListener implements ApiRequestListener{ + final ManagerRequest request; + + GrapheneAccount account; + + AccountIdOrNameListener(ManagerRequest request) { + this.request = request; + } + + @Override + public void success(Object answer, int idPetition) { + if(answer instanceof AccountProperties){ + AccountProperties props = (AccountProperties) answer; + account = new GrapheneAccount(); + account.setAccountId(props.id); + account.setName(props.name); + } + + request.success(account); + } + + @Override + public void fail(int idPetition) { + request.fail(); + } + } + + /** + * Class to retrieve the asset id or the asset name, if one of those is missing + */ + private class AssetIdOrNameListener implements ApiRequestListener{ + final ManagerRequest request; + + BitsharesAsset asset; + + AssetIdOrNameListener(ManagerRequest request) { + this.request = request; + } + + @Override + public void success(Object answer, int idPetition) { + if(answer instanceof ArrayList) { + if (((ArrayList) answer).get(0) instanceof BitsharesAsset) { + asset = (BitsharesAsset) ((ArrayList) answer).get(0); + request.success(asset); + } + } + } + + @Override + public void fail(int idPetition) { + //TODO fail asset retrieve + } + } + + /** + * Class to retrieve the transaction date + */ + public static class GetTransactionDate implements ApiRequestListener{ + /** + * The transaction to retrieve + */ + private CryptoCoinTransaction transaction; + /** + * The DAO to insert or update the transaction + */ + TransactionDao transactionDao; + + GetTransactionDate(CryptoCoinTransaction transaction, TransactionDao transactionDao) { + this.transaction = transaction; + this.transactionDao = transactionDao; + } + + @Override + public void success(Object answer, int idPetition) { + if(answer instanceof BlockHeader){ + @SuppressLint("SimpleDateFormat") SimpleDateFormat dateFormat = new SimpleDateFormat(SIMPLE_DATE_FORMAT); + dateFormat.setTimeZone(TimeZone.getTimeZone(DEFAULT_TIME_ZONE)); + try { + transaction.setDate(dateFormat.parse(((BlockHeader) answer).timestamp)); + if (transactionDao.getByTransaction(transaction.getDate(),transaction.getFrom(),transaction.getTo(),transaction.getAmount()) == null) { + transactionDao.insertTransaction(transaction); + } + } catch (ParseException e) { + e.printStackTrace(); + } + } + } + + @Override + public void fail(int idPetition) { + + } + } + +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/network/GetDatabaseVersion.java b/app/src/main/java/cy/agorise/crystalwallet/network/GetDatabaseVersion.java new file mode 100644 index 0000000..6625030 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/network/GetDatabaseVersion.java @@ -0,0 +1,68 @@ +package cy.agorise.crystalwallet.network; + +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.neovisionaries.ws.client.WebSocket; +import com.neovisionaries.ws.client.WebSocketFrame; + +import java.io.Serializable; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import cy.agorise.graphenej.RPC; +import cy.agorise.graphenej.api.BaseGrapheneHandler; +import cy.agorise.graphenej.interfaces.WitnessResponseListener; +import cy.agorise.graphenej.models.ApiCall; +import cy.agorise.graphenej.models.WitnessResponse; + +/** + * Created by henry on 28/2/2018. + */ + +public class GetDatabaseVersion extends BaseGrapheneHandler { + + private final WitnessResponseListener mListener; + + public GetDatabaseVersion(WitnessResponseListener listener) { + super(listener); + this.mListener = listener; + } + + @Override + public void onConnected(WebSocket websocket, Map> headers) throws Exception { + ApiCall getAccountByName = new ApiCall(0, "database_api.get_version", new ArrayList(), RPC.VERSION, 1); + websocket.sendText(getAccountByName.toJsonString()); + } + + @Override + public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception { + System.out.println("<<< "+frame.getPayloadText()); + String response = frame.getPayloadText(); + + Type GetChainIdResponse = new TypeToken>(){}.getType(); + GsonBuilder builder = new GsonBuilder(); + WitnessResponse witnessResponse = builder.create().fromJson(response, GetChainIdResponse); + if(witnessResponse.error != null){ + this.mListener.onError(witnessResponse.error); + }else{ + this.mListener.onSuccess(witnessResponse); + } + + websocket.disconnect(); + } + + @Override + public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception { + if(frame.isTextFrame()) + System.out.println(">>> "+frame.getPayloadText()); + } + + public class VersionResponse{ + public String blockchain_version; + public String steem_revision; + public String fc_revision; + public String chain_id; + } +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/network/SteemCryptoNetVerifier.java b/app/src/main/java/cy/agorise/crystalwallet/network/SteemCryptoNetVerifier.java new file mode 100644 index 0000000..3374883 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/network/SteemCryptoNetVerifier.java @@ -0,0 +1,49 @@ +package cy.agorise.crystalwallet.network; + +import cy.agorise.crystalwallet.enums.CryptoNet; +import cy.agorise.graphenej.interfaces.WitnessResponseListener; +import cy.agorise.graphenej.models.BaseResponse; +import cy.agorise.graphenej.models.WitnessResponse; + +/** + * + * Created by henry on 28/2/2018. + */ + +public class SteemCryptoNetVerifier extends CryptoNetVerifier { + private final CryptoNet cryptoNet = CryptoNet.STEEM; + private final String CHAIN_ID = "0000000000000000000000000000000000000000000000000000000000000000";//mainnet + + @Override + public void checkURL(final String url) { + final long startTime = System.currentTimeMillis(); + WebSocketThread thread = new WebSocketThread(new GetChainId(new WitnessResponseListener() { + @Override + public void onSuccess(WitnessResponse response) { + if(response.result instanceof GetDatabaseVersion.VersionResponse) { + GetDatabaseVersion.VersionResponse result = (GetDatabaseVersion.VersionResponse) response.result; + if(result.chain_id.equals(CHAIN_ID)) { + CryptoNetManager.verifiedCryptoNetURL(cryptoNet, url, System.currentTimeMillis() - startTime); + }else{ + System.out.println(" BitsharesCryptoNetVerifier Error we are not in the net current chain id " + result.chain_id + " excepted " + CHAIN_ID); + //TODO handle error bad chain + } + }else{ + //TODO handle error bad answer + } + } + + @Override + public void onError(BaseResponse.Error error) { + //TODO handle error + System.out.println("Bad server response " + url); + } + }),url); + thread.start(); + } + + @Override + public String getChainId() { + return CHAIN_ID; + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/requestmanagers/CreateBitcoinAccountRequest.java b/app/src/main/java/cy/agorise/crystalwallet/requestmanagers/CreateBitcoinAccountRequest.java index 805bb33..14f1c7c 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/requestmanagers/CreateBitcoinAccountRequest.java +++ b/app/src/main/java/cy/agorise/crystalwallet/requestmanagers/CreateBitcoinAccountRequest.java @@ -2,6 +2,8 @@ package cy.agorise.crystalwallet.requestmanagers; import android.content.Context; +import java.util.List; + import cy.agorise.crystalwallet.enums.CryptoCoin; import cy.agorise.crystalwallet.enums.CryptoNet; import cy.agorise.crystalwallet.models.AccountSeed; @@ -32,10 +34,10 @@ public class CreateBitcoinAccountRequest extends CryptoNetInfoRequest { // The state of this request private StatusCode status = StatusCode.NOT_STARTED; - public CreateBitcoinAccountRequest(AccountSeed accountSeed, Context context, CryptoNet cryptoNet){ - super(CryptoCoin.BITSHARES); + public CreateBitcoinAccountRequest(AccountSeed accountSeed, Context context, CryptoCoin cryptoCoin){ + super(cryptoCoin); this.accountSeed = accountSeed; - this.accountCryptoNet = cryptoNet; + this.accountCryptoNet = cryptoCoin.getCryptoNet(); this.context = context; } diff --git a/app/src/main/java/cy/agorise/crystalwallet/requestmanagers/ValidateBitcoinAddressRequest.java b/app/src/main/java/cy/agorise/crystalwallet/requestmanagers/ValidateBitcoinAddressRequest.java new file mode 100644 index 0000000..6bdea4b --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/requestmanagers/ValidateBitcoinAddressRequest.java @@ -0,0 +1,42 @@ +package cy.agorise.crystalwallet.requestmanagers; + +import cy.agorise.crystalwallet.enums.CryptoCoin; + +/** + * This class validates that an account name exist, this can be used to verified the existing accounts + * or to verified if the name is available to create an Account + * + * Created by henry on 8/10/2017. + */ + +public class ValidateBitcoinAddressRequest extends CryptoNetInfoRequest { + // The account name to validate + private String address; + // The result of the validation, or null if there isn't a response + private Boolean addressValid; + + public ValidateBitcoinAddressRequest(CryptoCoin cryptoCoin, String address) { + super(cryptoCoin); + this.address = address; + } + + public boolean getAddressValid(){ + return this.addressValid; + } + + public void setAddressValid(boolean value){ + this.addressValid = value; + this.validate(); + } + + public void validate(){ + if ((this.addressValid != null)){ + this._fireOnCarryOutEvent(); + } + } + + public String getAddress() { + return address; + } + +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/service/CrystalWalletService.java b/app/src/main/java/cy/agorise/crystalwallet/service/CrystalWalletService.java index ecffcc3..6e1bbce 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/service/CrystalWalletService.java +++ b/app/src/main/java/cy/agorise/crystalwallet/service/CrystalWalletService.java @@ -16,7 +16,9 @@ import java.util.ArrayList; import java.util.List; import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator; +import cy.agorise.crystalwallet.enums.CryptoCoin; import cy.agorise.crystalwallet.manager.FileBackupManager; +import cy.agorise.crystalwallet.manager.GeneralAccountManager; import cy.agorise.crystalwallet.models.BitsharesAccountNameCache; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests; import cy.agorise.crystalwallet.dao.CrystalDatabase; @@ -43,6 +45,7 @@ public class CrystalWalletService extends LifecycleService { private Looper mServiceLooper; private ServiceHandler mServiceHandler; private BitsharesAccountManager bitsharesAccountManager; + private GeneralAccountManager generalAccountManager; private Thread LoadAccountTransactionsThread; private Thread LoadBitsharesAccountNamesThread; private EquivalencesThread LoadEquivalencesThread; @@ -188,11 +191,13 @@ public class CrystalWalletService extends LifecycleService { this.cryptoNetInfoRequests = CryptoNetInfoRequests.getInstance(); this.fileServiceRequests = FileServiceRequests.getInstance(); this.bitsharesAccountManager = new BitsharesAccountManager(); + this.generalAccountManager = new GeneralAccountManager(CryptoCoin.BITCOIN,this.getApplicationContext()); this.fileBackupManager = new FileBackupManager(); //Add the managers as listeners of the CryptoNetInfoRequest so //they can carry out the info requests from the ui this.cryptoNetInfoRequests.addListener(this.bitsharesAccountManager); + this.cryptoNetInfoRequests.addListener(this.generalAccountManager); this.fileServiceRequests.addListener(this.fileBackupManager); } diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/SendTransactionValidator.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/SendTransactionValidator.java index 3cd67d9..28e719b 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/SendTransactionValidator.java +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/SendTransactionValidator.java @@ -22,7 +22,7 @@ public class SendTransactionValidator extends UIValidator { private CryptoNetAccount account; - public SendTransactionValidator(Context context, CryptoNetAccount account, MaterialSpinner fromEdit, EditText toEdit, Spinner assetSpinner, EditText amountEdit, EditText memoEdit){ + public SendTransactionValidator(Context context, CryptoNetAccount account, Spinner fromEdit, EditText toEdit, Spinner assetSpinner, EditText amountEdit, EditText memoEdit){ super(context); this.account = account; this.addField(new FromValidationField(fromEdit)); diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/AssetValidationField.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/AssetValidationField.java index 15f7688..1216aa4 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/AssetValidationField.java +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/AssetValidationField.java @@ -18,16 +18,22 @@ public class AssetValidationField extends ValidationField { } public void validate(){ - final CryptoCurrency cryptoCurrencySelected = (CryptoCurrency) this.assetField.getSelectedItem(); - if (cryptoCurrencySelected != null) { - final String newValue = "" + cryptoCurrencySelected.getId(); + if (this.assetField.getSelectedItem() instanceof CryptoCurrency) { + final CryptoCurrency cryptoCurrencySelected = (CryptoCurrency) this.assetField.getSelectedItem(); + if (cryptoCurrencySelected != null) { + final String newValue = "" + cryptoCurrencySelected.getId(); + this.setLastValue(newValue); + setValidForValue(newValue, true); + } else { + final String newValue = "" + -1; + setMessageForValue(newValue, "Select a currency"); + this.setLastValue(newValue); + setValidForValue(newValue, false); + } + } else { + final String newValue = "" + -1; this.setLastValue(newValue); setValidForValue(newValue, true); - } else { - final String newValue = ""+-1; - setMessageForValue(newValue,"Select a currency"); - this.setLastValue(newValue); - setValidForValue(newValue, false); } } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/FromValidationField.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/FromValidationField.java index 4f18033..c857b85 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/FromValidationField.java +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/FromValidationField.java @@ -17,9 +17,9 @@ import cy.agorise.crystalwallet.requestmanagers.ValidateExistBitsharesAccountReq public class FromValidationField extends ValidationField { //private EditText fromField; - private MaterialSpinner fromField; + private Spinner fromField; - public FromValidationField(MaterialSpinner fromField){ + public FromValidationField(Spinner fromField){ super(fromField); this.fromField = fromField; } @@ -27,8 +27,8 @@ public class FromValidationField extends ValidationField { public void validate(){ final String newValue; - if (fromField.getSelectedIndex() != -1) { - final CryptoNetAccount cryptoNetAccount = (CryptoNetAccount) fromField.getItems().get(fromField.getSelectedIndex()); + if (fromField.getSelectedItem() instanceof CryptoNetAccount){ + final CryptoNetAccount cryptoNetAccount = (CryptoNetAccount) fromField.getSelectedItem(); newValue = cryptoNetAccount.getName(); } else { newValue = ""; diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ToValidationField.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ToValidationField.java index 9c06402..18af291 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ToValidationField.java +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ToValidationField.java @@ -6,6 +6,7 @@ import android.widget.Spinner; import com.jaredrummler.materialspinner.MaterialSpinner; import cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.enums.CryptoNet; import cy.agorise.crystalwallet.models.CryptoNetAccount; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestListener; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests; @@ -17,10 +18,10 @@ import cy.agorise.crystalwallet.requestmanagers.ValidateExistBitsharesAccountReq public class ToValidationField extends ValidationField { - private MaterialSpinner fromField; + private Spinner fromField; private EditText toField; - public ToValidationField(MaterialSpinner fromField, EditText toField){ + public ToValidationField(Spinner fromField, EditText toField){ super(toField); this.fromField = fromField; this.toField = toField; @@ -28,8 +29,9 @@ public class ToValidationField extends ValidationField { public void validate(){ final String fromNewValue; - if (fromField.getSelectedIndex() != -1) { - final CryptoNetAccount cryptoNetAccount = (CryptoNetAccount) fromField.getItems().get(fromField.getSelectedIndex()); + CryptoNetAccount cryptoNetAccount = null; + if (fromField.getSelectedItem() instanceof CryptoNetAccount){ + cryptoNetAccount = (CryptoNetAccount) fromField.getSelectedItem(); fromNewValue = cryptoNetAccount.getName(); } else { fromNewValue = ""; @@ -45,19 +47,30 @@ public class ToValidationField extends ValidationField { setValidForValue(mixedValue, false); } else { - final ValidateExistBitsharesAccountRequest request = new ValidateExistBitsharesAccountRequest(toNewValue); - request.setListener(new CryptoNetInfoRequestListener() { - @Override - public void onCarryOut() { - if (!request.getAccountExists()) { - setMessageForValue(mixedValue, validator.getContext().getResources().getString(R.string.account_name_not_exist,"'"+toNewValue+"'")); - setValidForValue(mixedValue, false); - } else { + if (cryptoNetAccount != null) { + if (cryptoNetAccount.getCryptoNet() == CryptoNet.BITSHARES) { + final ValidateExistBitsharesAccountRequest request = new ValidateExistBitsharesAccountRequest(toNewValue); + request.setListener(new CryptoNetInfoRequestListener() { + @Override + public void onCarryOut() { + if (!request.getAccountExists()) { + setMessageForValue(mixedValue, validator.getContext().getResources().getString(R.string.account_name_not_exist, "'" + toNewValue + "'")); + setValidForValue(mixedValue, false); + } else { + setValidForValue(mixedValue, true); + } + } + }); + CryptoNetInfoRequests.getInstance().addRequest(request); + } else { + //if (addressIsValid(toNewValue)) { setValidForValue(mixedValue, true); - } + //} else { + // setMessageForValue(mixedValue, "Is not a valid address"); + // setValidForValue(mixedValue, false); + //} } - }); - CryptoNetInfoRequests.getInstance().addRequest(request); + } } } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/CryptoNetAccountAdapter.java b/app/src/main/java/cy/agorise/crystalwallet/views/CryptoNetAccountAdapter.java index 9ab9c2b..1917046 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/views/CryptoNetAccountAdapter.java +++ b/app/src/main/java/cy/agorise/crystalwallet/views/CryptoNetAccountAdapter.java @@ -6,6 +6,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.ImageView; import android.widget.TextView; import java.util.List; @@ -42,9 +43,11 @@ public class CryptoNetAccountAdapter extends ArrayAdapter { LayoutInflater inflater = (LayoutInflater)this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View v = inflater.inflate(R.layout.crypto_net_account_adapter_item, parent, false); TextView tvCryptoNetAccountName = v.findViewById(R.id.tvCryptoNetAccountName); + ImageView ivCryptoNetIcon = v.findViewById(R.id.ivCryptoNetIcon); CryptoNetAccount cryptoNetAccount = getItem(position); tvCryptoNetAccountName.setText(cryptoNetAccount.getName()); + ivCryptoNetIcon.setImageResource(cryptoNetAccount.getCryptoNet().getIconImageResource()); return v; } diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/CryptoNetBalanceViewHolder.java b/app/src/main/java/cy/agorise/crystalwallet/views/CryptoNetBalanceViewHolder.java index 132c312..0e2e900 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/views/CryptoNetBalanceViewHolder.java +++ b/app/src/main/java/cy/agorise/crystalwallet/views/CryptoNetBalanceViewHolder.java @@ -201,7 +201,7 @@ public class CryptoNetBalanceViewHolder extends RecyclerView.ViewHolder { } else { final CryptoNetBalanceViewHolder thisViewHolder = this; this.cryptoNetAccountId = balance.getAccountId(); - + this.cryptoNetIcon.setImageResource(balance.getCryptoNet().getIconImageResource()); /* * The first letter should be in Uppercase * */ diff --git a/app/src/main/res/drawable/coin_icon_bitcoin.png b/app/src/main/res/drawable/coin_icon_bitcoin.png new file mode 100644 index 0000000..5d15cf1 Binary files /dev/null and b/app/src/main/res/drawable/coin_icon_bitcoin.png differ diff --git a/app/src/main/res/drawable/coin_icon_dash.png b/app/src/main/res/drawable/coin_icon_dash.png new file mode 100644 index 0000000..3601ac0 Binary files /dev/null and b/app/src/main/res/drawable/coin_icon_dash.png differ diff --git a/app/src/main/res/drawable/coin_icon_doge.png b/app/src/main/res/drawable/coin_icon_doge.png new file mode 100644 index 0000000..ff3c452 Binary files /dev/null and b/app/src/main/res/drawable/coin_icon_doge.png differ diff --git a/app/src/main/res/drawable/coin_icon_litecoin.png b/app/src/main/res/drawable/coin_icon_litecoin.png new file mode 100644 index 0000000..27ff738 Binary files /dev/null and b/app/src/main/res/drawable/coin_icon_litecoin.png differ diff --git a/app/src/main/res/drawable/coin_icon_steem.png b/app/src/main/res/drawable/coin_icon_steem.png new file mode 100644 index 0000000..6a46224 Binary files /dev/null and b/app/src/main/res/drawable/coin_icon_steem.png differ diff --git a/app/src/main/res/layout/crypto_net_account_adapter_item.xml b/app/src/main/res/layout/crypto_net_account_adapter_item.xml index 5214572..17be0d3 100644 --- a/app/src/main/res/layout/crypto_net_account_adapter_item.xml +++ b/app/src/main/res/layout/crypto_net_account_adapter_item.xml @@ -8,9 +8,27 @@ android:paddingRight="10dp" android:paddingTop="10dp"> - + android:layout_alignParentStart="true" + android:layout_alignParentTop="true"> + + + \ No newline at end of file diff --git a/app/src/main/res/layout/send_transaction.xml b/app/src/main/res/layout/send_transaction.xml index 062f47a..921da5e 100644 --- a/app/src/main/res/layout/send_transaction.xml +++ b/app/src/main/res/layout/send_transaction.xml @@ -46,7 +46,8 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - +