Merge branch 'feat_ui_improvements' into feat_ui_improvements
This commit is contained in:
commit
0514e3984e
41 changed files with 3359 additions and 13 deletions
|
@ -101,4 +101,11 @@ dependencies {
|
|||
implementation 'id.zelory:compressor:2.1.0'
|
||||
implementation 'com.vincent.filepicker:MultiTypeFilePicker:1.0.7'
|
||||
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'
|
||||
implementation 'commons-codec:commons-codec:1.11'
|
||||
|
||||
implementation ('io.socket:socket.io-client:0.8.3') {
|
||||
// excluding org.json which is provided by Android
|
||||
exclude group: 'org.json', module: 'json'
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -55,6 +55,21 @@
|
|||
<activity android:name=".activities.PatternRequestActivity"
|
||||
android:noHistory="true">
|
||||
</activity>
|
||||
<activity android:name=".activities.PocketRequestActivity"
|
||||
android:noHistory="true">
|
||||
<!--<meta-data
|
||||
android:name="android.nfc.action.TECH_DISCOVERED"
|
||||
android:resource="@xml/tech" />-->
|
||||
<intent-filter>
|
||||
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data
|
||||
android:scheme="https"
|
||||
android:host="my.yubico.com"
|
||||
android:pathPrefix="/neo"/>
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
<activity android:name=".activities.CryptoNetAccountSettingsActivity"
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:windowSoftInputMode="adjustPan">
|
||||
|
|
|
@ -108,6 +108,7 @@ public class IntroActivity extends CustomActivity {
|
|||
} else {
|
||||
//Intent intent = new Intent(this, CreateSeedActivity.class);
|
||||
Intent intent = new Intent(this, BoardActivity.class);
|
||||
//Intent intent = new Intent(this, PocketRequestActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import butterknife.BindView;
|
|||
import butterknife.ButterKnife;
|
||||
import butterknife.OnTextChanged;
|
||||
import cy.agorise.crystalwallet.R;
|
||||
import cy.agorise.crystalwallet.application.CrystalSecurityMonitor;
|
||||
import cy.agorise.crystalwallet.models.GeneralSetting;
|
||||
import cy.agorise.crystalwallet.util.PasswordManager;
|
||||
import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
|
||||
|
@ -69,7 +70,11 @@ public class PatternRequestActivity extends AppCompatActivity {
|
|||
@Override
|
||||
public void onComplete(List<PatternLockView.Dot> pattern) {
|
||||
if (PasswordManager.checkPassword(patternEncrypted,patternToString(pattern))){
|
||||
thisActivity.finish();
|
||||
if (CrystalSecurityMonitor.getInstance(null).is2ndFactorSet()) {
|
||||
CrystalSecurityMonitor.getInstance(null).call2ndFactor(thisActivity);
|
||||
} else {
|
||||
thisActivity.finish();
|
||||
}
|
||||
} else {
|
||||
patternLockView.clearPattern();
|
||||
patternLockView.requestFocus();
|
||||
|
|
|
@ -15,6 +15,8 @@ import butterknife.BindView;
|
|||
import butterknife.ButterKnife;
|
||||
import butterknife.OnTextChanged;
|
||||
import cy.agorise.crystalwallet.R;
|
||||
import cy.agorise.crystalwallet.application.CrystalSecurityMonitor;
|
||||
import cy.agorise.crystalwallet.models.AccountSeed;
|
||||
import cy.agorise.crystalwallet.models.GeneralSetting;
|
||||
import cy.agorise.crystalwallet.util.PasswordManager;
|
||||
import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
|
||||
|
@ -61,7 +63,11 @@ public class PinRequestActivity extends AppCompatActivity {
|
|||
callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
|
||||
void afterPasswordChanged(Editable editable) {
|
||||
if (PasswordManager.checkPassword(passwordEncrypted, etPassword.getText().toString())) {
|
||||
this.finish();
|
||||
if (CrystalSecurityMonitor.getInstance(null).is2ndFactorSet()) {
|
||||
CrystalSecurityMonitor.getInstance(null).call2ndFactor(this);
|
||||
} else {
|
||||
this.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
package cy.agorise.crystalwallet.activities;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.arch.lifecycle.LiveData;
|
||||
import android.arch.lifecycle.Observer;
|
||||
import android.arch.lifecycle.ViewModelProviders;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.nfc.Tag;
|
||||
import android.nfc.tech.IsoDep;
|
||||
import android.nfc.tech.MifareClassic;
|
||||
import android.nfc.tech.NdefFormatable;
|
||||
import android.nfc.tech.NfcA;
|
||||
import android.nfc.tech.NfcF;
|
||||
import android.nfc.tech.NfcV;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.andrognito.patternlockview.PatternLockView;
|
||||
import com.andrognito.patternlockview.listener.PatternLockViewListener;
|
||||
|
||||
import org.apache.commons.codec.binary.Base32;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import cy.agorise.crystalwallet.R;
|
||||
import cy.agorise.crystalwallet.application.CrystalSecurityMonitor;
|
||||
import cy.agorise.crystalwallet.models.GeneralSetting;
|
||||
import cy.agorise.crystalwallet.util.PasswordManager;
|
||||
import cy.agorise.crystalwallet.util.yubikey.Algorithm;
|
||||
import cy.agorise.crystalwallet.util.yubikey.OathType;
|
||||
import cy.agorise.crystalwallet.util.yubikey.TOTP;
|
||||
import cy.agorise.crystalwallet.util.yubikey.YkOathApi;
|
||||
import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
|
||||
|
||||
public class PocketRequestActivity extends AppCompatActivity {
|
||||
|
||||
private NfcAdapter mNfcAdapter;
|
||||
private PendingIntent pendingIntent;
|
||||
private IntentFilter ndef;
|
||||
IntentFilter[] intentFiltersArray;
|
||||
String[][] techList;
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
//Do nothing to prevent the user to use the back button
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_pocket_request);
|
||||
ButterKnife.bind(this);
|
||||
|
||||
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
|
||||
|
||||
this.configureForegroundDispatch();
|
||||
}
|
||||
|
||||
public void configureForegroundDispatch(){
|
||||
if (mNfcAdapter != null) {
|
||||
pendingIntent = PendingIntent.getActivity(
|
||||
this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
|
||||
|
||||
ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
|
||||
try {
|
||||
ndef.addDataType("*/*"); /* Handles all MIME based dispatches.
|
||||
You should specify only the ones that you need. */
|
||||
} catch (IntentFilter.MalformedMimeTypeException e) {
|
||||
throw new RuntimeException("fail", e);
|
||||
}
|
||||
intentFiltersArray = new IntentFilter[]{ndef,};
|
||||
techList = new String[][]{ new String[] {IsoDep.class.getName(), NfcA.class.getName(), MifareClassic.class.getName(), NdefFormatable.class.getName()} };
|
||||
|
||||
} else {
|
||||
Toast.makeText(this, "This device doesn't support NFC.", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (mNfcAdapter != null) {
|
||||
mNfcAdapter.disableForegroundDispatch(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (mNfcAdapter != null) {
|
||||
mNfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techList);
|
||||
}
|
||||
}
|
||||
|
||||
public void onNewIntent(Intent intent) {
|
||||
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
|
||||
IsoDep tagIsoDep = IsoDep.get(tagFromIntent);
|
||||
Log.i("Tag from nfc","New Intent");
|
||||
String yubikeySecret = CrystalSecurityMonitor.getInstance(null).get2ndFactorValue();
|
||||
|
||||
try {
|
||||
tagIsoDep.connect();
|
||||
YkOathApi ykOathApi = new YkOathApi(tagIsoDep);
|
||||
|
||||
long unixTime = System.currentTimeMillis() / 1000L;
|
||||
byte[] timeStep = ByteBuffer.allocate(8).putLong(unixTime / 30L).array();
|
||||
byte[] response;
|
||||
response = ykOathApi.calculate("cy.agorise.crystalwallet",timeStep,true);
|
||||
ByteBuffer responseBB = ByteBuffer.wrap(response);
|
||||
int digits = (int)responseBB.get();
|
||||
String challengeString = ""+(responseBB.getInt());
|
||||
String challenge = challengeString.substring(challengeString.length()-digits);
|
||||
while (challenge.length() < digits){
|
||||
challenge = '0'+challenge;
|
||||
}
|
||||
|
||||
String storedChallenge = PasswordManager.totpd(yubikeySecret, unixTime, ykOathApi.getDeviceSalt());
|
||||
|
||||
Toast.makeText(this, "Secret:"+yubikeySecret+" StoredChallenge:"+storedChallenge+" Yubikey:"+challenge , Toast.LENGTH_LONG).show();
|
||||
|
||||
Log.i("TOTP","Secret: "+yubikeySecret);
|
||||
Log.i("TOTP", "Unixtime: "+unixTime);
|
||||
Log.i("TOTP", "Step: "+unixTime/30L);
|
||||
Log.i("TOTP", "StoredChallenge: "+storedChallenge);
|
||||
Log.i("TOTP", "Yubikey: "+challenge);
|
||||
|
||||
tagIsoDep.close();
|
||||
//ykOathApi.
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package cy.agorise.crystalwallet.activities;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
|
@ -47,6 +48,8 @@ public class SettingsActivity extends AppCompatActivity{
|
|||
@BindView(R.id.tvBuildVersion)
|
||||
public TextView tvBuildVersion;
|
||||
|
||||
private SecuritySettingsFragment securitySettingsFragment;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
@ -97,7 +100,8 @@ public class SettingsActivity extends AppCompatActivity{
|
|||
case 0:
|
||||
return new GeneralSettingsFragment();
|
||||
case 1:
|
||||
return new SecuritySettingsFragment();
|
||||
securitySettingsFragment = new SecuritySettingsFragment();
|
||||
return securitySettingsFragment;
|
||||
case 2:
|
||||
return new BackupsSettingsFragment();
|
||||
//case 3:
|
||||
|
@ -123,4 +127,12 @@ public class SettingsActivity extends AppCompatActivity{
|
|||
public void goBack(){
|
||||
onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
if (this.securitySettingsFragment != null){
|
||||
this.securitySettingsFragment.onNewIntent(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
|
@ -217,8 +218,9 @@ public abstract class BitsharesFaucetApiGenerator {
|
|||
|
||||
public interface IWebService {
|
||||
@Headers({"Content-Type: application/json"})
|
||||
@POST("/api/v1/accounts")
|
||||
@POST("/faucet/api/v1/accounts")
|
||||
Call<RegisterAccountResponse> getReg(@Body Map<String, HashMap> params);
|
||||
|
||||
}
|
||||
|
||||
public class RegisterAccountResponse {
|
||||
|
|
|
@ -610,7 +610,7 @@ public abstract class GrapheneApiGenerator {
|
|||
public void onError(BaseResponse.Error error) {
|
||||
request.getListener().fail(request.getId());
|
||||
}
|
||||
}), BitsharesConstant.EQUIVALENT_URL); //todo change equivalent url for current server url
|
||||
}), CryptoNetManager.getURL(CryptoNet.BITSHARES)); //todo change equivalent url for current server url
|
||||
thread.start();
|
||||
}
|
||||
|
||||
|
@ -626,7 +626,7 @@ public abstract class GrapheneApiGenerator {
|
|||
for (BitsharesAsset quoteAsset : quoteAssets) {
|
||||
WebSocketThread thread = new WebSocketThread(new GetLimitOrders(baseAsset.getBitsharesId(),
|
||||
quoteAsset.getBitsharesId(), 10, new EquivalentValueListener(baseAsset,
|
||||
quoteAsset, context)), BitsharesConstant.EQUIVALENT_URL); //todo change equivalent url for current server url
|
||||
quoteAsset, context)), CryptoNetManager.getURL(CryptoNet.BITSHARES)); //todo change equivalent url for current server url
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
package cy.agorise.crystalwallet.apigenerator.insightapi;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import cy.agorise.crystalwallet.models.GeneralCoinAccount;
|
||||
import io.socket.client.IO;
|
||||
import io.socket.client.Socket;
|
||||
import io.socket.emitter.Emitter;
|
||||
|
||||
/**
|
||||
* Handles all the calls for the Socket.IO of the insight api
|
||||
*
|
||||
* Only gets new transaction in real time for each address of an Account
|
||||
*
|
||||
*/
|
||||
|
||||
public class AccountActivityWatcher {
|
||||
|
||||
/**
|
||||
* The mAccount to be monitor
|
||||
*/
|
||||
private final GeneralCoinAccount mAccount;
|
||||
/**
|
||||
* The list of address to monitor
|
||||
*/
|
||||
private List<String> mWatchAddress = new ArrayList<>();
|
||||
/**
|
||||
* the Socket.IO
|
||||
*/
|
||||
private Socket mSocket;
|
||||
/**
|
||||
* This app mContext, used to save on the DB
|
||||
*/
|
||||
private final Context mContext;
|
||||
|
||||
/**
|
||||
* Handles the address/transaction notification.
|
||||
* Then calls the GetTransactionData to get the info of the new transaction
|
||||
*/
|
||||
private final Emitter.Listener onAddressTransaction = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(Object... os) {
|
||||
try {
|
||||
System.out.println("Receive accountActivtyWatcher " + os[0].toString() );
|
||||
String txid = ((JSONObject) os[0]).getString(InsightApiConstants.sTxTag);
|
||||
new GetTransactionData(txid, mAccount, mContext).start();
|
||||
} catch (JSONException ex) {
|
||||
Logger.getLogger(AccountActivityWatcher.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the connect of the Socket.IO
|
||||
*/
|
||||
private final Emitter.Listener onConnect = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(Object... os) {
|
||||
System.out.println("Connected to accountActivityWatcher");
|
||||
JSONArray array = new JSONArray();
|
||||
for(String addr : mWatchAddress) {
|
||||
array.put(addr);
|
||||
}
|
||||
mSocket.emit(InsightApiConstants.sSubscribeEmmit, InsightApiConstants.sChangeAddressRoom, array);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the disconnect of the Socket.Io
|
||||
* Reconcects the mSocket
|
||||
*/
|
||||
private final Emitter.Listener onDisconnect = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(Object... os) {
|
||||
System.out.println("Disconnected to accountActivityWatcher");
|
||||
mSocket.connect();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Error handler, doesn't need reconnect, the mSocket.io do that by default
|
||||
*/
|
||||
private final Emitter.Listener onError = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(Object... os) {
|
||||
System.out.println("Error to accountActivityWatcher ");
|
||||
for(Object ob : os) {
|
||||
System.out.println("accountActivityWatcher " + ob.toString());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Basic constructor
|
||||
*
|
||||
* @param mAccount The mAccount to be monitor
|
||||
* @param mContext This app mContext
|
||||
*/
|
||||
public AccountActivityWatcher(GeneralCoinAccount mAccount, Context mContext) {
|
||||
//String serverUrl = InsightApiConstants.protocol + "://" + InsightApiConstants.getAddress(mAccount.getCoin()) + ":" + InsightApiConstants.getPort(mAccount.getCoin()) + "/"+InsightApiConstants.getRawPath(mAccount.getCoin())+"/mSocket.io/";
|
||||
String serverUrl = InsightApiConstants.sProtocolSocketIO + "://" + InsightApiConstants.getAddress(mAccount.getCryptoCoin()) + ":" + InsightApiConstants.getPort(mAccount.getCryptoCoin()) + "/";
|
||||
this.mAccount = mAccount;
|
||||
this.mContext = mContext;
|
||||
System.out.println("accountActivityWatcher " + serverUrl);
|
||||
try {
|
||||
IO.Options opts = new IO.Options();
|
||||
System.out.println("accountActivityWatcher default path " + opts.path);
|
||||
this.mSocket = IO.socket(serverUrl);
|
||||
this.mSocket.on(Socket.EVENT_CONNECT, onConnect);
|
||||
this.mSocket.on(Socket.EVENT_DISCONNECT, onDisconnect);
|
||||
this.mSocket.on(Socket.EVENT_ERROR, onError);
|
||||
this.mSocket.on(Socket.EVENT_CONNECT_ERROR, onError);
|
||||
this.mSocket.on(Socket.EVENT_CONNECT_TIMEOUT, onError);
|
||||
this.mSocket.on(InsightApiConstants.sChangeAddressRoom, onAddressTransaction);
|
||||
} catch (URISyntaxException e) {
|
||||
//TODO change exception handler
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an address to be monitored, it can be used after the connect
|
||||
* @param address The String address to monitor
|
||||
*/
|
||||
public void addAddress(String address) {
|
||||
mWatchAddress.add(address);
|
||||
if (this.mSocket.connected()) {
|
||||
mSocket.emit(InsightApiConstants.sSubscribeEmmit, InsightApiConstants.sChangeAddressRoom, new String[]{address});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects the Socket
|
||||
*/
|
||||
public void connect() {
|
||||
//TODO change to use log
|
||||
System.out.println("accountActivityWatcher connecting");
|
||||
try{
|
||||
this.mSocket.connect();
|
||||
}catch(Exception e){
|
||||
//TODO change exception handler
|
||||
System.out.println("accountActivityWatcher exception " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects the Socket
|
||||
*/
|
||||
public void disconnect() {this.mSocket.disconnect();}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package cy.agorise.crystalwallet.apigenerator.insightapi;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Txi;
|
||||
import cy.agorise.crystalwallet.models.GeneralCoinAccount;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* Broadcast a transaction, using the InsightApi
|
||||
*
|
||||
*/
|
||||
|
||||
public class BroadcastTransaction extends Thread implements Callback<Txi> {
|
||||
/**
|
||||
* The rawTX as Hex String
|
||||
*/
|
||||
private String mRawTx;
|
||||
/**
|
||||
* The serviceGenerator to call
|
||||
*/
|
||||
private InsightApiServiceGenerator mServiceGenerator;
|
||||
/**
|
||||
* This app context, used to save on the DB
|
||||
*/
|
||||
private Context mContext;
|
||||
/**
|
||||
* The account who sign the transaction
|
||||
*/
|
||||
private GeneralCoinAccount mAccount;
|
||||
|
||||
/**
|
||||
* Basic Consturctor
|
||||
* @param RawTx The RawTX in Hex String
|
||||
* @param account The account who signs the transaction
|
||||
* @param context This app context
|
||||
*/
|
||||
public BroadcastTransaction(String RawTx, GeneralCoinAccount account, Context context){
|
||||
String serverUrl = InsightApiConstants.sProtocol + "://" + InsightApiConstants.getAddress(account.getCryptoCoin()) +"/";
|
||||
this.mServiceGenerator = new InsightApiServiceGenerator(serverUrl);
|
||||
this.mContext = context;
|
||||
this.mRawTx = RawTx;
|
||||
this.mAccount = account;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the response of the call
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void onResponse(Call<Txi> call, Response<Txi> response) {
|
||||
if (response.isSuccessful()) {
|
||||
//TODO invalidated send
|
||||
//TODO call getTransactionData
|
||||
GetTransactionData trData = new GetTransactionData(response.body().txid,this.mAccount,this.mContext);
|
||||
trData.start();
|
||||
} else {
|
||||
System.out.println("SENDTEST: not succesful " + response.message());
|
||||
//TODO change how to handle invalid transaction
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the failures of the call
|
||||
*/
|
||||
@Override
|
||||
public void onFailure(Call<Txi> call, Throwable t) {
|
||||
//TODO change how to handle invalid transaction
|
||||
System.out.println("SENDTEST: sendError " + t.getMessage() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the call of the service
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
InsightApiService service = this.mServiceGenerator.getService(InsightApiService.class);
|
||||
Call<Txi> broadcastTransaction = service.broadcastTransaction(InsightApiConstants.getPath(this.mAccount.getCryptoCoin()),this.mRawTx);
|
||||
broadcastTransaction.enqueue(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package cy.agorise.crystalwallet.apigenerator.insightapi;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import cy.agorise.crystalwallet.enums.CryptoCoin;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* Get the estimete fee amount from an insight api server.
|
||||
* This class gets the rate of the fee for a giving coin in about to block for a transaction to be
|
||||
* confirmated.
|
||||
*
|
||||
* This ammount is giving as amount of currency / kbytes, as example btc / kbytes
|
||||
*
|
||||
*/
|
||||
|
||||
public abstract class GetEstimateFee {
|
||||
|
||||
//TODO add a funciton to get the rate of a specific port
|
||||
|
||||
/**
|
||||
* The funciton to get the rate for the transaction be included in the next 2 blocks
|
||||
* @param coin The coin to get the rate
|
||||
* @return The rate number (coin/kbytes)
|
||||
* @throws IOException If the server answer null, or the rate couldn't be calculated
|
||||
*/
|
||||
public static long getEstimateFee(final CryptoCoin coin) throws IOException {
|
||||
String serverUrl = InsightApiConstants.sProtocol + "://"
|
||||
+ InsightApiConstants.getAddress(coin) + "/";
|
||||
InsightApiServiceGenerator serviceGenerator = new InsightApiServiceGenerator(serverUrl);
|
||||
InsightApiService service = serviceGenerator.getService(InsightApiService.class);
|
||||
Call<JsonObject> call = service.estimateFee(InsightApiConstants.getPath(coin));
|
||||
final Object SYNC = new Object();
|
||||
final JsonObject answer = new JsonObject();
|
||||
call.enqueue(new Callback<JsonObject>() {
|
||||
@Override
|
||||
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
|
||||
synchronized (SYNC) {
|
||||
answer.addProperty("answer",
|
||||
(long) (response.body().get("2").getAsDouble()* Math.pow(10, coin.getPrecision())));
|
||||
SYNC.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<JsonObject> call, Throwable t) {
|
||||
synchronized (SYNC) {
|
||||
SYNC.notifyAll();
|
||||
}
|
||||
}
|
||||
});
|
||||
synchronized (SYNC){
|
||||
for(int i = 0; i < 6; i++) {
|
||||
try {
|
||||
SYNC.wait(5000);
|
||||
} catch (InterruptedException e) {
|
||||
// this interruption never rises
|
||||
}
|
||||
if(answer.get("answer")!=null){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(answer.get("answer")==null){
|
||||
throw new IOException("");
|
||||
}
|
||||
return (long) (answer.get("answer").getAsDouble());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
package cy.agorise.crystalwallet.apigenerator.insightapi;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.models.AddressTxi;
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Txi;
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vin;
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vout;
|
||||
import cy.agorise.crystalwallet.models.GTxIO;
|
||||
import cy.agorise.crystalwallet.models.GeneralCoinAccount;
|
||||
import cy.agorise.crystalwallet.models.GeneralCoinAddress;
|
||||
import cy.agorise.crystalwallet.models.GeneralTransaction;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* Get all the transaction data of the addresses of an account
|
||||
*
|
||||
*/
|
||||
|
||||
public class GetTransactionByAddress extends Thread implements Callback<AddressTxi> {
|
||||
/**
|
||||
* The account to be query
|
||||
*/
|
||||
private GeneralCoinAccount mAccount;
|
||||
/**
|
||||
* The list of address to query
|
||||
*/
|
||||
private List<GeneralCoinAddress> mAddresses = new ArrayList<>();
|
||||
/**
|
||||
* The serviceGenerator to call
|
||||
*/
|
||||
private InsightApiServiceGenerator mServiceGenerator;
|
||||
/**
|
||||
* This app context, used to save on the DB
|
||||
*/
|
||||
private Context mContext;
|
||||
|
||||
|
||||
/**
|
||||
* Basic consturcotr
|
||||
* @param account The account to be query
|
||||
* @param context This app context
|
||||
*/
|
||||
public GetTransactionByAddress(GeneralCoinAccount account, Context context) {
|
||||
String serverUrl = InsightApiConstants.sProtocol + "://" + InsightApiConstants.getAddress(account.getCryptoCoin()) +"/";
|
||||
this.mAccount = account;
|
||||
this.mServiceGenerator = new InsightApiServiceGenerator(serverUrl);
|
||||
this.mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* add an address to be query
|
||||
* @param address the address to be query
|
||||
*/
|
||||
public void addAddress(GeneralCoinAddress address) {
|
||||
this.mAddresses.add(address);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle the response
|
||||
* @param call The call with the addresTxi object
|
||||
* @param response the response status object
|
||||
*/
|
||||
@Override
|
||||
public void onResponse(Call<AddressTxi> call, Response<AddressTxi> response) {
|
||||
if (response.isSuccessful()) {
|
||||
boolean changed = false;
|
||||
AddressTxi addressTxi = response.body();
|
||||
|
||||
for (Txi txi : addressTxi.items) {
|
||||
GeneralCoinAccount tempAccount = null;
|
||||
GeneralTransaction transaction = new GeneralTransaction();
|
||||
transaction.setAccount(this.mAccount);
|
||||
transaction.setTxid(txi.txid);
|
||||
transaction.setBlock(txi.blockheight);
|
||||
transaction.setDate(new Date(txi.time * 1000));
|
||||
transaction.setFee((long) (txi.fee * Math.pow(10,this.mAccount.getCryptoCoin().getPrecision())));
|
||||
transaction.setConfirm(txi.confirmations);
|
||||
transaction.setType(this.mAccount.getCryptoCoin());
|
||||
transaction.setBlockHeight(txi.blockheight);
|
||||
|
||||
for (Vin vin : txi.vin) {
|
||||
GTxIO input = new GTxIO();
|
||||
input.setAmount((long) (vin.value * Math.pow(10,this.mAccount.getCryptoCoin().getPrecision())));
|
||||
input.setTransaction(transaction);
|
||||
input.setOut(true);
|
||||
input.setType(this.mAccount.getCryptoCoin());
|
||||
String addr = vin.addr;
|
||||
input.setAddressString(addr);
|
||||
input.setIndex(vin.n);
|
||||
input.setScriptHex(vin.scriptSig.hex);
|
||||
input.setOriginalTxid(vin.txid);
|
||||
for (GeneralCoinAddress address : this.mAddresses) {
|
||||
if (address.getAddressString(this.mAccount.getNetworkParam()).equals(addr)) {
|
||||
input.setAddress(address);
|
||||
tempAccount = address.getAccount();
|
||||
|
||||
if (!address.hasTransactionOutput(input, this.mAccount.getNetworkParam())) {
|
||||
address.getTransactionOutput().add(input);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
transaction.getTxInputs().add(input);
|
||||
}
|
||||
|
||||
for (Vout vout : txi.vout) {
|
||||
if(vout.scriptPubKey.addresses == null || vout.scriptPubKey.addresses.length <= 0){
|
||||
// The address is null, this must be a memo
|
||||
String hex = vout.scriptPubKey.hex;
|
||||
int opReturnIndex = hex.indexOf("6a");
|
||||
if(opReturnIndex >= 0) {
|
||||
byte[] memoBytes = new byte[Integer.parseInt(hex.substring(opReturnIndex+2,opReturnIndex+4),16)];
|
||||
for(int i = 0; i < memoBytes.length;i++){
|
||||
memoBytes[i] = Byte.parseByte(hex.substring(opReturnIndex+4+(i*2),opReturnIndex+6+(i*2)),16);
|
||||
}
|
||||
transaction.setMemo(new String(memoBytes));
|
||||
}
|
||||
}else {
|
||||
GTxIO output = new GTxIO();
|
||||
output.setAmount((long) (vout.value * Math.pow(10, this.mAccount.getCryptoCoin().getPrecision())));
|
||||
output.setTransaction(transaction);
|
||||
output.setOut(false);
|
||||
output.setType(this.mAccount.getCryptoCoin());
|
||||
String addr = vout.scriptPubKey.addresses[0];
|
||||
output.setAddressString(addr);
|
||||
output.setIndex(vout.n);
|
||||
output.setScriptHex(vout.scriptPubKey.hex);
|
||||
for (GeneralCoinAddress address : this.mAddresses) {
|
||||
if (address.getAddressString(this.mAccount.getNetworkParam()).equals(addr)) {
|
||||
output.setAddress(address);
|
||||
tempAccount = address.getAccount();
|
||||
|
||||
if (!address.hasTransactionInput(output, this.mAccount.getNetworkParam())) {
|
||||
address.getTransactionInput().add(output);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
transaction.getTxOutputs().add(output);
|
||||
}
|
||||
}
|
||||
if(txi.txlock && txi.confirmations< this.mAccount.getCryptoNet().getConfirmationsNeeded()){
|
||||
transaction.setConfirm(this.mAccount.getCryptoNet().getConfirmationsNeeded());
|
||||
}
|
||||
//TODO database
|
||||
/*SCWallDatabase db = new SCWallDatabase(this.mContext);
|
||||
long idTransaction = db.getGeneralTransactionId(transaction);
|
||||
if (idTransaction == -1) {
|
||||
db.putGeneralTransaction(transaction);
|
||||
} else {
|
||||
transaction.setId(idTransaction);
|
||||
db.updateGeneralTransaction(transaction);
|
||||
}*/
|
||||
|
||||
if (tempAccount != null && transaction.getConfirm() < this.mAccount.getCryptoNet().getConfirmationsNeeded()) {
|
||||
new GetTransactionData(transaction.getTxid(), tempAccount, this.mContext, true).start();
|
||||
}
|
||||
for (GeneralCoinAddress address : this.mAddresses) {
|
||||
if (address.updateTransaction(transaction)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(changed) {
|
||||
this.mAccount.balanceChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Failure of the call
|
||||
* @param call The call object
|
||||
* @param t The reason for the failure
|
||||
*/
|
||||
@Override
|
||||
public void onFailure(Call<AddressTxi> call, Throwable t) {
|
||||
Log.e("GetTransactionByAddress", "Error in json format");
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to start the insight api call
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
if (this.mAddresses.size() > 0) {
|
||||
StringBuilder addressToQuery = new StringBuilder();
|
||||
for (GeneralCoinAddress address : this.mAddresses) {
|
||||
addressToQuery.append(address.getAddressString(this.mAccount.getNetworkParam())).append(",");
|
||||
}
|
||||
addressToQuery.deleteCharAt(addressToQuery.length() - 1);
|
||||
InsightApiService service = this.mServiceGenerator.getService(InsightApiService.class);
|
||||
Call<AddressTxi> addressTxiCall = service.getTransactionByAddress(InsightApiConstants.getPath(this.mAccount.getCryptoCoin()),addressToQuery.toString());
|
||||
addressTxiCall.enqueue(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
package cy.agorise.crystalwallet.apigenerator.insightapi;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Txi;
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vin;
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vout;
|
||||
import cy.agorise.crystalwallet.models.GTxIO;
|
||||
import cy.agorise.crystalwallet.models.GeneralCoinAccount;
|
||||
import cy.agorise.crystalwallet.models.GeneralCoinAddress;
|
||||
import cy.agorise.crystalwallet.models.GeneralTransaction;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* CThis class retrieve the data of a single transaction
|
||||
*/
|
||||
|
||||
public class GetTransactionData extends Thread implements Callback<Txi> {
|
||||
/**
|
||||
* The account to be query
|
||||
*/
|
||||
private final GeneralCoinAccount mAccount;
|
||||
/**
|
||||
* The transaction txid to be query
|
||||
*/
|
||||
private String mTxId;
|
||||
/**
|
||||
* The serviceGenerator to call
|
||||
*/
|
||||
private InsightApiServiceGenerator mServiceGenerator;
|
||||
/**
|
||||
* This app context, used to save on the DB
|
||||
*/
|
||||
private Context mContext;
|
||||
/**
|
||||
* If has to wait for another confirmation
|
||||
*/
|
||||
private boolean mMustWait = false;
|
||||
|
||||
/**
|
||||
* Constructor used to query for a transaction with unknown confirmations
|
||||
* @param txid The txid of the transaciton to be query
|
||||
* @param account The account to be query
|
||||
* @param context This app Context
|
||||
*/
|
||||
public GetTransactionData(String txid, GeneralCoinAccount account, Context context) {
|
||||
this(txid, account, context, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Consturctor to be used qhen the confirmations of the transaction are known
|
||||
* @param txid The txid of the transaciton to be query
|
||||
* @param account The account to be query
|
||||
* @param context This app Context
|
||||
* @param mustWait If there is less confirmation that needed
|
||||
*/
|
||||
public GetTransactionData(String txid, GeneralCoinAccount account, Context context, boolean mustWait) {
|
||||
String serverUrl = InsightApiConstants.sProtocol + "://" + InsightApiConstants.getAddress(account.getCryptoCoin()) +"/";
|
||||
this.mAccount = account;
|
||||
this.mTxId= txid;
|
||||
this.mServiceGenerator = new InsightApiServiceGenerator(serverUrl);
|
||||
this.mContext = context;
|
||||
this.mMustWait = mustWait;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to start the insight api call
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
if (this.mMustWait) {
|
||||
//We are waiting for confirmation
|
||||
try {
|
||||
Thread.sleep(InsightApiConstants.sWaitTime);
|
||||
} catch (InterruptedException ignored) {
|
||||
//TODO this exception never rises
|
||||
}
|
||||
}
|
||||
|
||||
InsightApiService service = this.mServiceGenerator.getService(InsightApiService.class);
|
||||
Call<Txi> txiCall = service.getTransaction(InsightApiConstants.getPath(this.mAccount.getCryptoCoin()),this.mTxId);
|
||||
txiCall.enqueue(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call<Txi> call, Response<Txi> response) {
|
||||
if (response.isSuccessful()) {
|
||||
Txi txi = response.body();
|
||||
|
||||
GeneralTransaction transaction = new GeneralTransaction();
|
||||
transaction.setAccount(this.mAccount);
|
||||
transaction.setTxid(txi.txid);
|
||||
transaction.setBlock(txi.blockheight);
|
||||
transaction.setDate(new Date(txi.time * 1000));
|
||||
transaction.setFee((long) (txi.fee * Math.pow(10,this.mAccount.getCryptoCoin().getPrecision())));
|
||||
transaction.setConfirm(txi.confirmations);
|
||||
transaction.setType(this.mAccount.getCryptoCoin());
|
||||
transaction.setBlockHeight(txi.blockheight);
|
||||
|
||||
for (Vin vin : txi.vin) {
|
||||
GTxIO input = new GTxIO();
|
||||
input.setAmount((long) (vin.value * Math.pow(10,this.mAccount.getCryptoCoin().getPrecision())));
|
||||
input.setTransaction(transaction);
|
||||
input.setOut(true);
|
||||
input.setType(this.mAccount.getCryptoCoin());
|
||||
String addr = vin.addr;
|
||||
input.setAddressString(addr);
|
||||
input.setIndex(vin.n);
|
||||
input.setScriptHex(vin.scriptSig.hex);
|
||||
input.setOriginalTxid(vin.txid);
|
||||
for (GeneralCoinAddress address : this.mAccount.getAddresses()) {
|
||||
if (address.getAddressString(this.mAccount.getNetworkParam()).equals(addr)) {
|
||||
input.setAddress(address);
|
||||
if (!address.hasTransactionOutput(input, this.mAccount.getNetworkParam())) {
|
||||
address.getTransactionOutput().add(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
transaction.getTxInputs().add(input);
|
||||
}
|
||||
|
||||
for (Vout vout : txi.vout) {
|
||||
if(vout.scriptPubKey.addresses == null || vout.scriptPubKey.addresses.length <= 0){
|
||||
// The address is null, this must be a memo
|
||||
String hex = vout.scriptPubKey.hex;
|
||||
int opReturnIndex = hex.indexOf("6a");
|
||||
if(opReturnIndex >= 0) {
|
||||
byte[] memoBytes = new byte[Integer.parseInt(hex.substring(opReturnIndex+2,opReturnIndex+4),16)];
|
||||
for(int i = 0; i < memoBytes.length;i++){
|
||||
memoBytes[i] = Byte.parseByte(hex.substring(opReturnIndex+4+(i*2),opReturnIndex+6+(i*2)),16);
|
||||
}
|
||||
transaction.setMemo(new String(memoBytes));
|
||||
System.out.println("Memo read : " + transaction.getMemo()); //TODO log this line
|
||||
}
|
||||
|
||||
}else {
|
||||
GTxIO output = new GTxIO();
|
||||
output.setAmount((long) (vout.value * Math.pow(10, this.mAccount.getCryptoCoin().getPrecision())));
|
||||
output.setTransaction(transaction);
|
||||
output.setOut(false);
|
||||
output.setType(this.mAccount.getCryptoCoin());
|
||||
String addr = vout.scriptPubKey.addresses[0];
|
||||
output.setAddressString(addr);
|
||||
output.setIndex(vout.n);
|
||||
output.setScriptHex(vout.scriptPubKey.hex);
|
||||
for (GeneralCoinAddress address : this.mAccount.getAddresses()) {
|
||||
if (address.getAddressString(this.mAccount.getNetworkParam()).equals(addr)) {
|
||||
output.setAddress(address);
|
||||
if (!address.hasTransactionInput(output, this.mAccount.getNetworkParam())) {
|
||||
address.getTransactionInput().add(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
transaction.getTxOutputs().add(output);
|
||||
}
|
||||
}
|
||||
|
||||
// This is for features like dash instantSend
|
||||
if(txi.txlock && txi.confirmations< this.mAccount.getCryptoNet().getConfirmationsNeeded()){
|
||||
transaction.setConfirm(this.mAccount.getCryptoNet().getConfirmationsNeeded());
|
||||
}
|
||||
|
||||
//TODO database
|
||||
/*SCWallDatabase db = new SCWallDatabase(this.mContext);
|
||||
long idTransaction = db.getGeneralTransactionId(transaction);
|
||||
if (idTransaction == -1) {
|
||||
db.putGeneralTransaction(transaction);
|
||||
} else {
|
||||
transaction.setId(idTransaction);
|
||||
db.updateGeneralTransaction(transaction);
|
||||
}*/
|
||||
|
||||
this.mAccount.updateTransaction(transaction);
|
||||
this.mAccount.balanceChange();
|
||||
|
||||
if (transaction.getConfirm() < this.mAccount.getCryptoNet().getConfirmationsNeeded()) {
|
||||
//If transaction weren't confirmed, add the transaction to watch for change on the confirmations
|
||||
new GetTransactionData(this.mTxId, this.mAccount, this.mContext, true).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO handle the failure response
|
||||
* @param call the Call object
|
||||
* @param t the reason of the failure
|
||||
*/
|
||||
@Override
|
||||
public void onFailure(Call<Txi> call, Throwable t) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package cy.agorise.crystalwallet.apigenerator.insightapi;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import cy.agorise.crystalwallet.enums.CryptoCoin;
|
||||
|
||||
/**
|
||||
* Class holds all constant related to the Insight Api
|
||||
*
|
||||
*/
|
||||
|
||||
abstract class InsightApiConstants {
|
||||
/**
|
||||
* Protocol of the insight api calls
|
||||
*/
|
||||
static final String sProtocol = "https";
|
||||
/**
|
||||
* Protocol of the insigiht api Socket.IO connection
|
||||
*/
|
||||
static final String sProtocolSocketIO = "http";
|
||||
/**
|
||||
* Contains each url information for each coin
|
||||
*/
|
||||
private static final HashMap<CryptoCoin,AddressPort> sServerAddressPort = new HashMap<>();
|
||||
/**
|
||||
* Insight api Socket.IO new transaction by address notification
|
||||
*/
|
||||
static final String sChangeAddressRoom = "bitcoind/addresstxid";
|
||||
/**
|
||||
* Socket.io subscribe command
|
||||
*/
|
||||
static final String sSubscribeEmmit = "subscribe";
|
||||
/**
|
||||
* Tag used in the response of the address transaction notification
|
||||
*/
|
||||
static final String sTxTag = "txid";
|
||||
|
||||
/**
|
||||
* Wait time to check for confirmations
|
||||
*/
|
||||
static long sWaitTime = (30 * 1000); //wait 1 minute
|
||||
|
||||
//Filled the serverAddressPort maps with static data
|
||||
static{
|
||||
//serverAddressPort.put(Coin.BITCOIN,new AddressPort("fr.blockpay.ch",3002,"node/btc/testnet","insight-api"));
|
||||
sServerAddressPort.put(CryptoCoin.BITCOIN,new AddressPort("fr.blockpay.ch",3003,"node/btc/testnet","insight-api"));
|
||||
//serverAddressPort.put(Coin.BITCOIN_TEST,new AddressPort("fr.blockpay.ch",3003,"node/btc/testnet","insight-api"));
|
||||
sServerAddressPort.put(CryptoCoin.LITECOIN,new AddressPort("fr.blockpay.ch",3009,"node/ltc","insight-lite-api"));
|
||||
sServerAddressPort.put(CryptoCoin.DASH,new AddressPort("fr.blockpay.ch",3005,"node/dash","insight-api-dash"));
|
||||
sServerAddressPort.put(CryptoCoin.DOGECOIN,new AddressPort("fr.blockpay.ch",3006,"node/dogecoin","insight-api"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the insight api server address
|
||||
* @param coin The coin of the API to find
|
||||
* @return The String address of the server, can be a name or the IP
|
||||
*/
|
||||
static String getAddress(CryptoCoin coin){
|
||||
return sServerAddressPort.get(coin).mServerAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the port of the server Insight API
|
||||
* @param coin The coin of the API to find
|
||||
* @return The server number port
|
||||
*/
|
||||
static int getPort(CryptoCoin coin){
|
||||
return sServerAddressPort.get(coin).mPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url path of the server Insight API
|
||||
* @param coin The coin of the API to find
|
||||
* @return The path of the Insight API
|
||||
*/
|
||||
static String getPath(CryptoCoin coin){
|
||||
return sServerAddressPort.get(coin).mPath + "/" + sServerAddressPort.get(coin).mInsightPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains all the url info neccessary to connects to the insight api
|
||||
*/
|
||||
private static class AddressPort{
|
||||
/**
|
||||
* The server address
|
||||
*/
|
||||
final String mServerAddress;
|
||||
/**
|
||||
* The port used in the Socket.io
|
||||
*/
|
||||
final int mPort;
|
||||
/**
|
||||
* The path of the coin server
|
||||
*/
|
||||
final String mPath;
|
||||
/**
|
||||
* The path of the insight api
|
||||
*/
|
||||
final String mInsightPath;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param serverAddress The server address of the Insight API
|
||||
* @param port the port number of the Insight API
|
||||
* @param path the path to the Insight API before the last /
|
||||
* @param insightPath the path after the last / of the Insight API
|
||||
*/
|
||||
AddressPort(String serverAddress, int port, String path, String insightPath) {
|
||||
this.mServerAddress = serverAddress;
|
||||
this.mPort = port;
|
||||
this.mPath = path;
|
||||
this.mInsightPath = insightPath;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package cy.agorise.crystalwallet.apigenerator.insightapi;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.models.AddressTxi;
|
||||
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Txi;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Field;
|
||||
import retrofit2.http.FormUrlEncoded;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.Path;
|
||||
|
||||
/**
|
||||
* Holds each call to the insigh api server
|
||||
*/
|
||||
|
||||
interface InsightApiService {
|
||||
|
||||
/**
|
||||
* The query for the info of a single transaction
|
||||
* @param path The path of the insight api without the server address
|
||||
* @param txid the transasction to be query
|
||||
*/
|
||||
@GET("{path}/tx/{txid}")
|
||||
Call<Txi> getTransaction(@Path(value = "path", encoded = true) String path, @Path(value = "txid", encoded = true) String txid);
|
||||
|
||||
/**
|
||||
* The query for the transasctions of multiples addresses
|
||||
* @param path The path of the insight api without the server address
|
||||
* @param addrs the addresses to be query each separated with a ","
|
||||
*/
|
||||
@GET("{path}/addrs/{addrs}/txs")
|
||||
Call<AddressTxi> getTransactionByAddress(@Path(value = "path", encoded = true) String path, @Path(value = "addrs", encoded = true) String addrs);
|
||||
|
||||
/**
|
||||
* Broadcast Transaction
|
||||
* @param path The path of the insight api without the server address
|
||||
* @param rawtx the rawtx to send in Hex String
|
||||
*/
|
||||
@FormUrlEncoded
|
||||
@POST("{path}/tx/send")
|
||||
Call<Txi> broadcastTransaction(@Path(value = "path", encoded = true) String path, @Field("rawtx") String rawtx);
|
||||
|
||||
/**
|
||||
* Get the estimate rate fee for a coin in the Insight API
|
||||
* @param path The path of the insight api without the server address
|
||||
*/
|
||||
@GET("{path}/utils/estimatefee?nbBlocks=2")
|
||||
Call<JsonObject> estimateFee(@Path(value = "path", encoded = true) String path);
|
||||
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
package cy.agorise.crystalwallet.apigenerator.insightapi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
/**
|
||||
* Generatir fir tge okhttp connection of the Insight API
|
||||
* TODO finish documentation
|
||||
*/
|
||||
|
||||
class InsightApiServiceGenerator {
|
||||
/**
|
||||
* Tag used for logging
|
||||
*/
|
||||