- Created the cryptocuyrrency dao

- Refresh Bitshares balances and transactions
- Created the bitshares faucet api to implement the faucet transasctions (work in progress)
- Import bitshares account in Account manager
- Load bitshares account from db
This commit is contained in:
henry 2017-10-15 20:23:44 -04:00
parent 814d724096
commit a1ec57044c
9 changed files with 320 additions and 33 deletions

View file

@ -0,0 +1,8 @@
package cy.agorise.crystalwallet.apigenerator;
/**
* Created by henry on 15/10/2017.
*/
public class BitsharesFaucetApiGenerator {
}

View file

@ -1,16 +1,24 @@
package cy.agorise.crystalwallet.apigenerator;
import android.arch.lifecycle.LiveData;
import android.content.Context;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.models.BitsharesAsset;
import cy.agorise.crystalwallet.models.CryptoCoinBalance;
import cy.agorise.crystalwallet.models.CryptoNetBalance;
import cy.agorise.crystalwallet.network.WebSocketThread;
import cy.agorise.graphenej.Address;
import cy.agorise.graphenej.Asset;
import cy.agorise.graphenej.AssetAmount;
import cy.agorise.graphenej.ObjectType;
import cy.agorise.graphenej.Transaction;
import cy.agorise.graphenej.UserAccount;
import cy.agorise.graphenej.api.GetAccountBalances;
import cy.agorise.graphenej.api.GetAccountByName;
import cy.agorise.graphenej.api.GetAccounts;
import cy.agorise.graphenej.api.GetKeyReferences;
@ -36,7 +44,7 @@ import cy.agorise.graphenej.models.WitnessResponse;
* Created by henry on 26/9/2017.
*/
public class GrapheneApiGenerator {
public abstract class GrapheneApiGenerator {
//TODO network connections
//TODO make to work with all Graphene stype, not only bitshares
@ -114,18 +122,18 @@ public class GrapheneApiGenerator {
/**
* Gets the Transaction for an Account
*
* @param accountId The account id to search
* @param accountGrapheneId The account id to search
* @param start The start index of the transaction list
* @param stop The stop index of the transaction list
* @param limit the maximun transactions to retrieve
* @param request The Api request object, to answer this petition
*/
public static void getAccountTransaction(String accountId, int start, int stop, int limit, final ApiRequest request){
WebSocketThread thread = new WebSocketThread(new GetRelativeAccountHistory(new UserAccount(accountId), stop, limit, start, new WitnessResponseListener() {
public static void getAccountTransaction(String accountGrapheneId, int start, int stop, int limit, final ApiRequest request){
WebSocketThread thread = new WebSocketThread(new GetRelativeAccountHistory(new UserAccount(accountGrapheneId), stop, limit, start, new WitnessResponseListener() {
@Override
public void onSuccess(WitnessResponse response) {
WitnessResponse<List<HistoricalTransfer>> resp = response;
request.getListener().success(resp,request.getId());
request.getListener().success(resp.result,request.getId());
}
@Override
@ -136,6 +144,32 @@ public class GrapheneApiGenerator {
thread.start();
}
/**
* Retrieves the account id by the name of the account
*
* @param accountName The account Name to find
* @param request The Api request object, to answer this petition
*/
public static void getAccountByName(String accountName, final ApiRequest request){
WebSocketThread thread = new WebSocketThread(new GetAccountByName(accountName, new WitnessResponseListener() {
@Override
public void onSuccess(WitnessResponse response) {
AccountProperties accountProperties = ((WitnessResponse<AccountProperties>) response).result;
if(accountProperties == null){
request.getListener().fail(request.getId());
}else{
request.getListener().success(accountProperties,request.getId());
}
}
@Override
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
}),url);
thread.start();
}
/**
* Retrieves the account id by the name of the account
*
@ -221,14 +255,12 @@ public class GrapheneApiGenerator {
}
public static void getAssetById(ArrayList<String> assetIds, final ApiRequest request){
//TODO the graphenej library needs a way to creates the Asset object only with the symbol
ArrayList<Asset> assets = new ArrayList<>();
for(String assetId : assetIds){
Asset asset = new Asset(assetId);
assets.add(asset);
}
//TODO the graphenj library needs to add the lookupAssetSymbols to be able to search the asset by symbol
// this can be done with the same lookupassetsymbol, but passing only the symbol not the objetcid
WebSocketThread thread = new WebSocketThread(new LookupAssetSymbols(assets, new WitnessResponseListener() {
@Override
public void onSuccess(WitnessResponse response) {
@ -263,7 +295,8 @@ public class GrapheneApiGenerator {
thread.start();
}
public static void subscribeBitsharesAccount(final String accountId){
public static void subscribeBitsharesAccount(long accountId, final String accountBitsharesId, Context context){
final LiveData<List<CryptoCoinBalance>> balances = CrystalDatabase.getAppDatabase(context).cryptoCoinBalanceDao().getBalancesFromAccount(accountId);
SubscriptionListener balanceListener = new SubscriptionListener() {
@Override
public ObjectType getInterestObjectType() {
@ -277,7 +310,10 @@ public class GrapheneApiGenerator {
for(Serializable update : updatedObjects){
if(update instanceof AccountBalanceUpdate){
AccountBalanceUpdate balanceUpdate = (AccountBalanceUpdate) update;
if(balanceUpdate.owner.equals(accountId)){
if(balanceUpdate.owner.equals(accountBitsharesId)){
boolean find = false;
for(CryptoCoinBalance balance : balances.getValue()){
}
//TODO balance function
//TODO refresh transactions
}
@ -300,4 +336,31 @@ public class GrapheneApiGenerator {
bitsharesSubscriptionHub.cancelSubscriptions();
}
public static void getAccountBalance(final long accountId, final String accountGrapheneId, final Context context){
WebSocketThread thread = new WebSocketThread(new GetAccountBalances(new UserAccount(accountGrapheneId), null, new WitnessResponseListener() {
@Override
public void onSuccess(WitnessResponse response) {
List<AssetAmount> balances = (List<AssetAmount>) response.result;
for(AssetAmount balance : balances){
CryptoCoinBalance ccBalance = new CryptoCoinBalance();
ccBalance.setAccountId(accountId);
ccBalance.setBalance(balance.getAmount().longValue());
//TODO cryptocyrrency
CrystalDatabase.getAppDatabase(context).cryptoCoinBalanceDao().insertCryptoCoinBalance(ccBalance);
}
}
@Override
public void onError(BaseResponse.Error error) {
}
}),url);
thread.start();
}
}

View file

@ -0,0 +1,28 @@
package cy.agorise.crystalwallet.dao;
import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Insert;
import android.arch.persistence.room.OnConflictStrategy;
import android.arch.persistence.room.Query;
import java.util.List;
import cy.agorise.crystalwallet.models.CryptoCurrency;
/**
* Created by henry on 15/10/2017.
*/
@Dao
public interface CryptoCurrencyDao {
@Query("SELECT * FROM crypto_currency")
List<CryptoCurrency> getAll();
@Query("SELECT * FROM crypto_currency WHERE id := id")
CryptoCurrency getById(int id);
@Insert(onConflict = OnConflictStrategy.REPLACE)
public long[] insertCryptoCurrency(CryptoCurrency... currencies);
}

View file

@ -30,6 +30,7 @@ public abstract class CrystalDatabase extends RoomDatabase {
public abstract CryptoNetAccountDao cryptoNetAccountDao();
public abstract TransactionDao transactionDao();
public abstract CryptoCoinBalanceDao cryptoCoinBalanceDao();
public abstract CryptoCurrencyDao cryptoCurrencyDao();
public static CrystalDatabase getAppDatabase(Context context) {
if (instance == null) {

View file

@ -23,6 +23,9 @@ public interface TransactionDao {
@Query("SELECT * FROM crypto_coin_transaction ORDER BY date ASC")
LivePagedListProvider<Integer, CryptoCoinTransaction> transactionsByDate();
@Query("SELECT * FROM crypto_coin_transaction WHERE account_id := idAccount ORDER BY date ASC")
LiveData<List<CryptoCoinTransaction>> getByIdAccount(long idAccount);
@Insert(onConflict = OnConflictStrategy.REPLACE)
public long[] insertTransaction(CryptoCoinTransaction... transactions);

View file

@ -1,12 +1,15 @@
package cy.agorise.crystalwallet.manager;
import android.arch.lifecycle.LiveData;
import android.content.Context;
import com.google.common.primitives.UnsignedLong;
import org.bitcoinj.core.ECKey;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import cy.agorise.crystalwallet.apigenerator.ApiRequest;
import cy.agorise.crystalwallet.apigenerator.ApiRequestListener;
@ -18,6 +21,9 @@ import cy.agorise.crystalwallet.cryptonetinforequests.ValidateExistBitsharesAcco
import cy.agorise.crystalwallet.cryptonetinforequests.ValidateImportBitsharesAccountRequest;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.BitsharesAsset;
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.graphenej.Address;
@ -29,6 +35,7 @@ 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.HistoricalTransfer;
import cy.agorise.graphenej.operations.TransferOperationBuilder;
/**
@ -37,37 +44,52 @@ import cy.agorise.graphenej.operations.TransferOperationBuilder;
public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener {
@Override
public CryptoNetAccount createAccountFromSeed(CryptoNetAccount account) {
public CryptoNetAccount createAccountFromSeed(CryptoNetAccount account, Context context) {
//TODO generate account keys
//TODO register account
//TODO register account faucet api
//TODO save account on DB
//TODO subscribe account
return null;
}
@Override
public CryptoNetAccount importAccountFromSeed(CryptoNetAccount account) {
//TODO check account info
//TODO save account on DB
//TODO subscribe account
//TODO refresh account transaction
//TODO refresh account balance
public CryptoNetAccount importAccountFromSeed(CryptoNetAccount account, Context context) {
if(account instanceof GrapheneAccount) {
GrapheneAccount grapheneAccount = (GrapheneAccount) account;
if(grapheneAccount.getAccountId() == null){
grapheneAccount = this.getAccountInfoByName(grapheneAccount.getName());
}else if(grapheneAccount.getName() == null){
grapheneAccount = this.getAccountInfoById(grapheneAccount.getAccountId());
}
//TODO grapaheneAccount null, error fetching
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
//TODO save account on DB
GrapheneApiGenerator.subscribeBitsharesAccount(grapheneAccount.getId(), grapheneAccount.getAccountId(), context);
this.refreshAccountTransactions(account.getId(), context);
GrapheneApiGenerator.getAccountBalance(grapheneAccount.getId(), grapheneAccount.getAccountId(), context);
return grapheneAccount;
}
return null;
}
@Override
public void loadAccountFromDB(CryptoNetAccount account) {
public void loadAccountFromDB(CryptoNetAccount account, Context context) {
if(account instanceof GrapheneAccount){
GrapheneAccount grapheneAccount = (GrapheneAccount) account;
if(grapheneAccount.getAccountId() == null){
//TODO find account data by name
grapheneAccount = this.getAccountInfoByName(grapheneAccount.getName());
}else if(grapheneAccount.getName() == null){
//TODO find account data by id
grapheneAccount = this.getAccountInfoById(grapheneAccount.getAccountId());
}
//TODO grapaheneAccount null, error fetching
GrapheneApiGenerator.subscribeBitsharesAccount(grapheneAccount.getAccountId());
//TODO refresh account transactions
//TODO refresh account balance
GrapheneApiGenerator.subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
this.refreshAccountTransactions(account.getId(),context);
GrapheneApiGenerator.getAccountBalance(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
}
@ -77,6 +99,8 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
this.validateImportAccount((ValidateImportBitsharesAccountRequest) request);
} else if (request instanceof ValidateExistBitsharesAccountRequest){
this.validateExistAcccount((ValidateExistBitsharesAccountRequest) request);
} else if (request instanceof ValidateBitsharesSendRequest){
this.validateSendRequest((ValidateBitsharesSendRequest) request);
}
}
@ -168,4 +192,162 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
GrapheneApiGenerator.broadcastTransaction(transaction,feeAsset, transactionRequest);
}
private GrapheneAccount getAccountInfoById(String grapheneId){
final Object SYNC = new Object();
long timeout = 60000;
AccountIdOrNameListener listener = new AccountIdOrNameListener(SYNC);
ApiRequest request = new ApiRequest(0, listener);
GrapheneApiGenerator.getAccountById(grapheneId,request);
long cTime = System.currentTimeMillis();
while(!listener.ready && (System.currentTimeMillis()-cTime) < timeout){
synchronized (SYNC){
try {
SYNC.wait(100);
} catch (InterruptedException e) {}
}
}
return listener.account;
}
private GrapheneAccount getAccountInfoByName(String grapheneName){
final Object SYNC = new Object();
long timeout = 60000;
AccountIdOrNameListener listener = new AccountIdOrNameListener(SYNC);
ApiRequest request = new ApiRequest(0, listener);
GrapheneApiGenerator.getAccountByName(grapheneName,request);
long cTime = System.currentTimeMillis();
while(!listener.ready && (System.currentTimeMillis()-cTime) < timeout){
synchronized (SYNC){
try {
SYNC.wait(100);
} catch (InterruptedException e) {}
}
}
return listener.account;
}
public void refreshAccountTransactions(final long idAccount, Context context){
final CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
final LiveData<List<CryptoCoinTransaction>> transactions = db.transactionDao().getByIdAccount(idAccount);
final LiveData<GrapheneAccount> account = null;
//TODO find account
int start = transactions.getValue().size();
int limit = 50;
int stop = start + limit;
ApiRequest transactionRequest = new ApiRequest(0, new TransactionRequestListener(start,stop,limit,account.getValue(),db));
GrapheneApiGenerator.getAccountTransaction(account.getValue().getName(),start,stop,limit,transactionRequest);
}
private class TransactionRequestListener implements ApiRequestListener{
int start;
int stop;
int limit;
GrapheneAccount account;
CrystalDatabase db;
public 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;
}
@Override
public void success(Object answer, int idPetition) {
List<HistoricalTransfer> transfers = (List<HistoricalTransfer>) answer ;
for(HistoricalTransfer transfer : transfers){
CryptoCoinTransaction transaction = new CryptoCoinTransaction();
transaction.setAccountId(account.getId());
transaction.setAmount(transfer.getOperation().getAssetAmount().getAmount().longValue());
BitsharesAsset currency = null;
//TODO find currency by symbol db.cryptoCurrencyDao().getBySymbol(transfer.getOperation().getAssetAmount().getAsset().getSymbol())
if(currency == null){
//CryptoCurrency not in database
Asset asset = transfer.getOperation().getAssetAmount().getAsset();
BitsharesAsset.Type assetType = null;
if(asset.getAssetType() == Asset.AssetType.CORE_ASSET){
assetType = BitsharesAsset.Type.CORE;
}else if(asset.getAssetType() == Asset.AssetType.SMART_COIN){
assetType = BitsharesAsset.Type.SMART_COIN;
}else if(asset.getAssetType() == Asset.AssetType.PREDICTION_MARKET){
assetType = BitsharesAsset.Type.PREDICTION_MARKET;
}else if(asset.getAssetType() == Asset.AssetType.UIA){
assetType = BitsharesAsset.Type.UIA;
}
currency = new BitsharesAsset(asset.getSymbol(),asset.getPrecision(),asset.getObjectId(), assetType);
db.cryptoCurrencyDao().insertCryptoCurrency(currency);
}
transaction.setIdCurrency(currency.getId());
transaction.setConfirmed(true); //graphene transaction are always confirmed
//TODO date of the transaction transaction.setDate(transfer.);
transaction.setFrom(transfer.getOperation().getFrom().getName());
transaction.setInput(!transfer.getOperation().getFrom().getName().equals(account.getName()));
transaction.setTo(transfer.getOperation().getTo().getName());
db.transactionDao().insertTransaction(transaction);
}
if(transfers.size()>= limit){
int newStart= start + limit;
int newStop= stop + limit;
ApiRequest transactionRequest = new ApiRequest(newStart/limit, new TransactionRequestListener(newStart,newStop,limit,account,db));
GrapheneApiGenerator.getAccountTransaction(account.getName(),newStart,newStop,limit,transactionRequest);
}
}
@Override
public void fail(int idPetition) {
}
}
private class AccountIdOrNameListener implements ApiRequestListener{
Object SYNC;
boolean ready = false;
GrapheneAccount account;
public AccountIdOrNameListener(Object SYNC) {
this.SYNC = SYNC;
}
@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);
}
synchronized (SYNC){
ready = true;
SYNC.notifyAll();
}
}
@Override
public void fail(int idPetition) {
synchronized (SYNC){
ready = true;
SYNC.notifyAll();
}
}
}
}

View file

@ -1,5 +1,7 @@
package cy.agorise.crystalwallet.manager;
import android.content.Context;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
@ -16,19 +18,19 @@ public interface CryptoAccountManager {
* @param account The values to be created,
* @returnThe CruptoNetAccount created, or null if it couldn't be created
*/
public CryptoNetAccount createAccountFromSeed(CryptoNetAccount account);
public CryptoNetAccount createAccountFromSeed(CryptoNetAccount account, Context context);
/**
* Imports a CryptoCoin account from a seed
* @param account A CryptoNetAccount with the parameters to be imported
* @returnThe CruptoNetAccount imported
*/
public CryptoNetAccount importAccountFromSeed(CryptoNetAccount account);
public CryptoNetAccount importAccountFromSeed(CryptoNetAccount account, Context context);
/**
* Loads account data from the database
*
* @param account The CryptoNetAccount to be loaded
*/
public void loadAccountFromDB(CryptoNetAccount account);
public void loadAccountFromDB(CryptoNetAccount account, Context context);
}

View file

@ -38,7 +38,7 @@ public class CryptoCoinBalance {
private CryptoCoin mCoin;
@ColumnInfo(name = "balance")
private int mBalance;
private long mBalance;
public long getId() {
return mId;
@ -64,11 +64,11 @@ public class CryptoCoinBalance {
this.mCoin = coin;
}
public int getBalance() {
public long getBalance() {
return mBalance;
}
public void setBalance(int balance) {
public void setBalance(long balance) {
this.mBalance = balance;
}

View file

@ -59,7 +59,7 @@ public class CryptoCoinTransaction {
* The amount of asset is moved in this transaction
*/
@ColumnInfo(name="amount")
protected int amount;
protected long amount;
/**
* The id of the Crypto Currency to use in the database
@ -136,9 +136,9 @@ public class CryptoCoinTransaction {
public void setConfirmed(boolean confirmed) { isConfirmed = confirmed; }
public int getAmount() { return amount; }
public long getAmount() { return amount; }
public void setAmount(int amount) { this.amount = amount; }
public void setAmount(long amount) { this.amount = amount; }
public int getIdCurrency() { return idCurrency; }