Merge branch 'feat_central_broker' into develop
1
.gitignore
vendored
|
@ -104,4 +104,3 @@ graphenej/build
|
|||
|
||||
local.properties
|
||||
|
||||
sample
|
||||
|
|
16
build.gradle
|
@ -3,11 +3,25 @@ subprojects {
|
|||
mavenCentral()
|
||||
}
|
||||
}
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven {
|
||||
url "https://maven.google.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url 'https://maven.google.com/'
|
||||
name 'Google'
|
||||
}
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.3.0'
|
||||
classpath 'com.android.tools.build:gradle:3.1.4'
|
||||
}
|
||||
}
|
0
gradlew
vendored
Normal file → Executable file
|
@ -2,33 +2,39 @@ group 'cy.agorise'
|
|||
version '0.4.7-alpha2'
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply from: 'maven-push.gradle'
|
||||
|
||||
|
||||
dependencies {
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
compile 'com.neovisionaries:nv-websocket-client:1.30'
|
||||
compile 'org.bitcoinj:bitcoinj-core:0.14.3'
|
||||
compile group: 'com.google.code.gson', name: 'gson', version: '2.8.0'
|
||||
compile group: "org.tukaani", name: "xz", version: "1.6"
|
||||
}
|
||||
|
||||
//apply from: 'maven-push.gradle'
|
||||
|
||||
android {
|
||||
compileSdkVersion 24
|
||||
buildToolsVersion "25.0.0"
|
||||
buildToolsVersion '27.0.3'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 24
|
||||
versionCode 12
|
||||
versionName "0.4.7-alpha3"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
}
|
||||
buildTypes {
|
||||
debug{}
|
||||
preRelease{}
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation group: 'junit', name: 'junit', version: '4.12'
|
||||
implementation 'com.neovisionaries:nv-websocket-client:1.30'
|
||||
implementation 'org.bitcoinj:bitcoinj-core:0.14.3'
|
||||
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.0'
|
||||
implementation group: "org.tukaani", name: "xz", version: "1.6"
|
||||
|
||||
// Rx dependencies
|
||||
api 'io.reactivex.rxjava2:rxandroid:2.0.2'
|
||||
api 'io.reactivex.rxjava2:rxjava:2.1.16'
|
||||
api 'com.jakewharton.rxrelay2:rxrelay:2.0.0'
|
||||
api 'com.squareup.okhttp3:okhttp:3.5.0'
|
||||
}
|
|
@ -1,6 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="cy.agorise.graphenej">
|
||||
|
||||
<uses-sdk android:minSdkVersion="1" />
|
||||
<application/>
|
||||
|
||||
<application>
|
||||
<service
|
||||
android:name=".api.android.NetworkService"
|
||||
android:enabled="true"
|
||||
android:exported="true"></service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -9,5 +9,10 @@ package cy.agorise.graphenej;
|
|||
public enum AuthorityType {
|
||||
OWNER,
|
||||
ACTIVE,
|
||||
MEMO
|
||||
MEMO;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%d", this.ordinal());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
package cy.agorise.graphenej;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import cy.agorise.graphenej.interfaces.ByteSerializable;
|
||||
import cy.agorise.graphenej.interfaces.JsonSerializable;
|
||||
import cy.agorise.graphenej.operations.TransferOperation;
|
||||
|
||||
/**
|
||||
* Created by nelson on 11/5/16.
|
||||
* Base class that represents a generic operation
|
||||
*/
|
||||
public abstract class BaseOperation implements ByteSerializable, JsonSerializable {
|
||||
|
||||
|
@ -32,4 +39,54 @@ public abstract class BaseOperation implements ByteSerializable, JsonSerializabl
|
|||
array.add(this.getId());
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* De-serializer used to unpack data from a generic operation. The general format used in the
|
||||
* JSON-RPC blockchain API is the following:
|
||||
* </p>
|
||||
*
|
||||
* <code>[OPERATION_ID, OPERATION_OBJECT]</code><br>
|
||||
*
|
||||
* <p>
|
||||
* Where <code>OPERATION_ID</code> is one of the operations defined in {@link cy.agorise.graphenej.OperationType}
|
||||
* and <code>OPERATION_OBJECT</code> is the actual operation serialized in the JSON format.
|
||||
* </p>
|
||||
* Here's an example of this serialized form for a transfer operation:<br><br>
|
||||
*<pre>
|
||||
*[
|
||||
* 0,
|
||||
* {
|
||||
* "fee": {
|
||||
* "amount": 264174,
|
||||
* "asset_id": "1.3.0"
|
||||
* },
|
||||
* "from": "1.2.138632",
|
||||
* "to": "1.2.129848",
|
||||
* "amount": {
|
||||
* "amount": 100,
|
||||
* "asset_id": "1.3.0"
|
||||
* },
|
||||
* "extensions": []
|
||||
* }
|
||||
*]
|
||||
*</pre><br>
|
||||
* If this class is used, this serialized data will be translated to a TransferOperation object instance.<br>
|
||||
*
|
||||
* TODO: Add support for operations other than the 'transfer'
|
||||
*/
|
||||
public static class OperationDeserializer implements JsonDeserializer<BaseOperation> {
|
||||
|
||||
@Override
|
||||
public BaseOperation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
BaseOperation operation = null;
|
||||
if(json.isJsonArray()){
|
||||
JsonArray array = json.getAsJsonArray();
|
||||
if(array.get(0).getAsLong() == OperationType.TRANSFER_OPERATION.ordinal()){
|
||||
operation = context.deserialize(array.get(1), TransferOperation.class);
|
||||
}
|
||||
}
|
||||
return operation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package cy.agorise.graphenej;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import org.bitcoinj.core.DumpedPrivateKey;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
|
@ -69,7 +71,15 @@ public class BrainKey {
|
|||
public BrainKey(String words, int sequence) {
|
||||
this.mBrainKey = words;
|
||||
this.sequenceNumber = sequence;
|
||||
String encoded = String.format("%s %d", words, sequence);
|
||||
derivePrivateKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the actual private key from the brainkey + sequence number
|
||||
*/
|
||||
private void derivePrivateKey(){
|
||||
@SuppressLint("DefaultLocale")
|
||||
String encoded = String.format("%s %d", this.mBrainKey, this.sequenceNumber);
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-512");
|
||||
byte[] bytes = md.digest(encoded.getBytes("UTF-8"));
|
||||
|
@ -120,19 +130,28 @@ public class BrainKey {
|
|||
}
|
||||
|
||||
/**
|
||||
* Brain key words getter
|
||||
* @return: The word sequence that comprises this brain key
|
||||
* Brain key words getter.
|
||||
* @return The word sequence that comprises this brain key
|
||||
*/
|
||||
public String getBrainKey(){
|
||||
return mBrainKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sequence number getter
|
||||
* @return: The sequence number used alongside with the brain key words in order
|
||||
* Sequence number getter.
|
||||
* @return The sequence number used alongside with the brain key words in order
|
||||
* to derive the private key
|
||||
*/
|
||||
public int getSequenceNumber(){
|
||||
return sequenceNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sequence number setter.
|
||||
* @param sequenceNumber The sequence number used to generate a specific key from this brainkey
|
||||
*/
|
||||
public void setSequenceNumber(int sequenceNumber) {
|
||||
this.sequenceNumber = sequenceNumber;
|
||||
derivePrivateKey();
|
||||
}
|
||||
}
|
|
@ -1,12 +1,17 @@
|
|||
package cy.agorise.graphenej;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import cy.agorise.graphenej.interfaces.ByteSerializable;
|
||||
import cy.agorise.graphenej.interfaces.JsonSerializable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Created by nelson on 11/9/16.
|
||||
*/
|
||||
|
@ -40,4 +45,15 @@ public class Extensions implements JsonSerializable, ByteSerializable {
|
|||
public int size(){
|
||||
return extensions.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom de-serializer used to avoid problems when de-serializing an object that contains
|
||||
* an extension array.
|
||||
*/
|
||||
public static class ExtensionsDeserializer implements JsonDeserializer<Extensions> {
|
||||
@Override
|
||||
public Extensions deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package cy.agorise.graphenej.objects;
|
||||
package cy.agorise.graphenej;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.google.gson.Gson;
|
||||
|
@ -19,9 +19,6 @@ import java.security.MessageDigest;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import cy.agorise.graphenej.Address;
|
||||
import cy.agorise.graphenej.PublicKey;
|
||||
import cy.agorise.graphenej.Util;
|
||||
import cy.agorise.graphenej.errors.ChecksumException;
|
||||
import cy.agorise.graphenej.errors.MalformedAddressException;
|
||||
import cy.agorise.graphenej.interfaces.ByteSerializable;
|
||||
|
@ -32,7 +29,6 @@ import cy.agorise.graphenej.interfaces.JsonSerializable;
|
|||
* {@url https://bitshares.org/doxygen/structgraphene_1_1chain_1_1memo__data.html}
|
||||
*/
|
||||
public class Memo implements ByteSerializable, JsonSerializable {
|
||||
public final static String TAG = "Memo";
|
||||
public static final String KEY_FROM = "from";
|
||||
public static final String KEY_TO = "to";
|
||||
public static final String KEY_NONCE = "nonce";
|
||||
|
@ -291,12 +287,14 @@ public class Memo implements ByteSerializable, JsonSerializable {
|
|||
memoObject.addProperty(KEY_FROM, "");
|
||||
memoObject.addProperty(KEY_TO, "");
|
||||
memoObject.addProperty(KEY_NONCE, "");
|
||||
if(this.message != null)
|
||||
memoObject.addProperty(KEY_MESSAGE, Util.bytesToHex(this.message));
|
||||
return null;
|
||||
}else{
|
||||
memoObject.addProperty(KEY_FROM, this.from.toString());
|
||||
memoObject.addProperty(KEY_TO, this.to.toString());
|
||||
memoObject.addProperty(KEY_NONCE, String.format("%x", this.nonce));
|
||||
if(this.message != null)
|
||||
memoObject.addProperty(KEY_MESSAGE, Util.bytesToHex(this.message));
|
||||
}
|
||||
return memoObject;
|
||||
|
@ -310,8 +308,9 @@ public class Memo implements ByteSerializable, JsonSerializable {
|
|||
*/
|
||||
public JsonElement toJson(boolean decimal){
|
||||
JsonElement jsonElement = toJsonObject();
|
||||
if(decimal){
|
||||
if(decimal && jsonElement != null){
|
||||
JsonObject jsonObject = (JsonObject) jsonElement;
|
||||
// The nonce is interpreted in base 16, but it is going to be written in base 10
|
||||
BigInteger nonce = new BigInteger(jsonObject.get(KEY_NONCE).getAsString(), 16);
|
||||
jsonObject.addProperty(KEY_NONCE, nonce.toString());
|
||||
}
|
|
@ -132,4 +132,8 @@ public class OrderBook {
|
|||
}
|
||||
return obtainedBase;
|
||||
}
|
||||
|
||||
public List<LimitOrder> getLimitOrders(){
|
||||
return limitOrders;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,4 +53,9 @@ public class PublicKey implements ByteSerializable, Serializable {
|
|||
PublicKey other = (PublicKey) obj;
|
||||
return this.publicKey.equals(other.getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getAddress();
|
||||
}
|
||||
}
|
|
@ -14,14 +14,17 @@ public class RPC {
|
|||
public static final String CALL_CANCEL_ALL_SUBSCRIPTIONS = "cancel_all_subscriptions";
|
||||
public static final String CALL_GET_ACCOUNT_BY_NAME = "get_account_by_name";
|
||||
public static final String CALL_GET_ACCOUNTS = "get_accounts";
|
||||
public static final String CALL_GET_FULL_ACCOUNTS = "get_full_accounts";
|
||||
public static final String CALL_GET_DYNAMIC_GLOBAL_PROPERTIES = "get_dynamic_global_properties";
|
||||
public static final String CALL_BROADCAST_TRANSACTION = "broadcast_transaction";
|
||||
public static final String CALL_GET_REQUIRED_FEES = "get_required_fees";
|
||||
public static final String CALL_GET_KEY_REFERENCES = "get_key_references";
|
||||
public static final String CALL_GET_RELATIVE_ACCOUNT_HISTORY = "get_relative_account_history";
|
||||
public static final String CALL_GET_ACCOUNT_HISTORY = "get_account_history";
|
||||
public static final String CALL_GET_ACCOUNT_HISTORY_BY_OPERATIONS = "get_account_history_by_operations";
|
||||
public static final String CALL_LOOKUP_ACCOUNTS = "lookup_accounts";
|
||||
public static final String CALL_LIST_ASSETS = "list_assets";
|
||||
public static final String GET_OBJECTS = "get_objects";
|
||||
public static final String CALL_GET_OBJECTS = "get_objects";
|
||||
public static final String GET_ACCOUNT_BALANCES = "get_account_balances";
|
||||
public static final String CALL_LOOKUP_ASSET_SYMBOLS = "lookup_asset_symbols";
|
||||
public static final String CALL_GET_BLOCK_HEADER = "get_block_header";
|
||||
|
|
|
@ -47,6 +47,7 @@ public class UserAccount extends GrapheneObject implements ByteSerializable, Jso
|
|||
public static final String KEY_OWNER_SPECIAL_AUTHORITY = "owner_special_authority";
|
||||
public static final String KEY_ACTIVE_SPECIAL_AUTHORITY = "active_special_authority";
|
||||
public static final String KEY_N_CONTROL_FLAGS = "top_n_control_flags";
|
||||
public static final String LIFETIME_EXPIRATION_DATE = "1969-12-31T23:59:59";
|
||||
|
||||
@Expose
|
||||
private String name;
|
||||
|
@ -84,6 +85,7 @@ public class UserAccount extends GrapheneObject implements ByteSerializable, Jso
|
|||
@Expose
|
||||
private long referrerRewardsPercentage;
|
||||
|
||||
private boolean isLifeTime;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -248,6 +250,14 @@ public class UserAccount extends GrapheneObject implements ByteSerializable, Jso
|
|||
this.statistics = statistics;
|
||||
}
|
||||
|
||||
public boolean isLifeTime() {
|
||||
return isLifeTime;
|
||||
}
|
||||
|
||||
public void setLifeTime(boolean lifeTime) {
|
||||
isLifeTime = lifeTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializer used to build a UserAccount instance from the full JSON-formatted response obtained
|
||||
* by the 'get_objects' API call.
|
||||
|
@ -274,8 +284,10 @@ public class UserAccount extends GrapheneObject implements ByteSerializable, Jso
|
|||
// Handling the deserialization and assignation of the membership date, which internally
|
||||
// is stored as a long POSIX time value
|
||||
try{
|
||||
Date date = dateFormat.parse(jsonAccount.get(KEY_MEMBERSHIP_EXPIRATION_DATE).getAsString());
|
||||
String expirationDate = jsonAccount.get(KEY_MEMBERSHIP_EXPIRATION_DATE).getAsString();
|
||||
Date date = dateFormat.parse(expirationDate);
|
||||
userAccount.setMembershipExpirationDate(date.getTime());
|
||||
userAccount.setLifeTime(expirationDate.equals(LIFETIME_EXPIRATION_DATE));
|
||||
} catch (ParseException e) {
|
||||
System.out.println("ParseException. Msg: "+e.getMessage());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package cy.agorise.graphenej.api;
|
||||
|
||||
/**
|
||||
* Class used to list all currently supported API accesses
|
||||
*/
|
||||
|
||||
public class ApiAccess {
|
||||
public static final int API_NONE = 0x00;
|
||||
public static final int API_DATABASE = 0x01;
|
||||
public static final int API_HISTORY = 0x02;
|
||||
public static final int API_NETWORK_BROADCAST = 0x04;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package cy.agorise.graphenej.api;
|
||||
|
||||
/**
|
||||
* Class used to send connection status updates.
|
||||
*
|
||||
* Connection status updates can be any of the following:
|
||||
* - {@link ConnectionStatusUpdate#CONNECTED}
|
||||
* - {@link ConnectionStatusUpdate#AUTHENTICATED}
|
||||
* - {@link ConnectionStatusUpdate#API_UPDATE}
|
||||
* - {@link ConnectionStatusUpdate#DISCONNECTED}
|
||||
*
|
||||
* This is specified by the field called {@link #updateCode}.
|
||||
*
|
||||
* If the updateCode is ConnectionStatusUpdate#API_UPDATE another extra field called
|
||||
* {@link #api} is used to specify which api we're getting access to.
|
||||
*/
|
||||
|
||||
public class ConnectionStatusUpdate {
|
||||
// Constant used to announce that a connection has been established
|
||||
public final static int CONNECTED = 0;
|
||||
// Constant used to announce a successful authentication
|
||||
public final static int AUTHENTICATED = 1;
|
||||
// Constant used to announce an api update
|
||||
public final static int API_UPDATE = 2;
|
||||
// Constant used to announce a disconnection event
|
||||
public final static int DISCONNECTED = 3;
|
||||
|
||||
/**
|
||||
* The update code is the general purpose of the update message. Can be any of the following:
|
||||
* - {@link ConnectionStatusUpdate#CONNECTED}
|
||||
* - {@link ConnectionStatusUpdate#AUTHENTICATED}
|
||||
* - {@link ConnectionStatusUpdate#API_UPDATE}
|
||||
* - {@link ConnectionStatusUpdate#DISCONNECTED}
|
||||
*/
|
||||
private int updateCode;
|
||||
|
||||
/**
|
||||
* This field is used in case the updateCode is {@link ConnectionStatusUpdate#API_UPDATE} and
|
||||
* it serves to specify which API we're getting access to.
|
||||
*
|
||||
* It can be any of the fields defined in {@link ApiAccess}
|
||||
*/
|
||||
private int api;
|
||||
|
||||
public ConnectionStatusUpdate(int updateCode, int api){
|
||||
this.updateCode = updateCode;
|
||||
this.api = api;
|
||||
}
|
||||
|
||||
public int getUpdateCode() {
|
||||
return updateCode;
|
||||
}
|
||||
|
||||
public void setUpdateCode(int updateCode) {
|
||||
this.updateCode = updateCode;
|
||||
}
|
||||
|
||||
public int getApi() {
|
||||
return api;
|
||||
}
|
||||
|
||||
public void setApi(int api) {
|
||||
this.api = api;
|
||||
}
|
||||
}
|
|
@ -75,11 +75,9 @@ public class GetObjects extends BaseGrapheneHandler {
|
|||
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
|
||||
ArrayList<Serializable> params = new ArrayList<>();
|
||||
ArrayList<Serializable> subParams = new ArrayList<>();
|
||||
for(String id : this.ids){
|
||||
subParams.add(id);
|
||||
}
|
||||
subParams.addAll(this.ids);
|
||||
params.add(subParams);
|
||||
ApiCall apiCall = new ApiCall(0, RPC.GET_OBJECTS, params, RPC.VERSION, 0);
|
||||
ApiCall apiCall = new ApiCall(0, RPC.CALL_GET_OBJECTS, params, RPC.VERSION, 0);
|
||||
websocket.sendText(apiCall.toJsonString());
|
||||
}
|
||||
|
||||
|
|
|
@ -18,9 +18,9 @@ import cy.agorise.graphenej.UserAccount;
|
|||
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
import cy.agorise.graphenej.models.BaseResponse;
|
||||
import cy.agorise.graphenej.models.HistoricalTransfer;
|
||||
import cy.agorise.graphenej.models.OperationHistory;
|
||||
import cy.agorise.graphenej.models.WitnessResponse;
|
||||
import cy.agorise.graphenej.objects.Memo;
|
||||
import cy.agorise.graphenej.Memo;
|
||||
import cy.agorise.graphenej.operations.TransferOperation;
|
||||
|
||||
/**
|
||||
|
@ -158,12 +158,13 @@ public class GetRelativeAccountHistory extends BaseGrapheneHandler {
|
|||
|
||||
sendRelativeAccountHistoryRequest();
|
||||
}else if(baseResponse.id >= GET_HISTORY_DATA){
|
||||
Type RelativeAccountHistoryResponse = new TypeToken<WitnessResponse<List<HistoricalTransfer>>>(){}.getType();
|
||||
Type RelativeAccountHistoryResponse = new TypeToken<WitnessResponse<List<OperationHistory>>>(){}.getType();
|
||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
gsonBuilder.registerTypeAdapter(OperationHistory.class, new OperationHistory.OperationHistoryDeserializer());
|
||||
gsonBuilder.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer());
|
||||
gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer());
|
||||
gsonBuilder.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer());
|
||||
WitnessResponse<List<HistoricalTransfer>> transfersResponse = gsonBuilder.create().fromJson(response, RelativeAccountHistoryResponse);
|
||||
WitnessResponse<List<OperationHistory>> transfersResponse = gsonBuilder.create().fromJson(response, RelativeAccountHistoryResponse);
|
||||
mListener.onSuccess(transfersResponse);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,10 @@ import cy.agorise.graphenej.interfaces.SubscriptionHub;
|
|||
import cy.agorise.graphenej.interfaces.SubscriptionListener;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
import cy.agorise.graphenej.models.DynamicGlobalProperties;
|
||||
import cy.agorise.graphenej.models.OperationHistory;
|
||||
import cy.agorise.graphenej.models.SubscriptionResponse;
|
||||
import cy.agorise.graphenej.models.WitnessResponse;
|
||||
import cy.agorise.graphenej.objects.Memo;
|
||||
import cy.agorise.graphenej.Memo;
|
||||
import cy.agorise.graphenej.operations.CustomOperation;
|
||||
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
||||
import cy.agorise.graphenej.operations.TransferOperation;
|
||||
|
@ -61,7 +62,7 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs
|
|||
private int subscriptionCounter = 0;
|
||||
private HashMap<Long, BaseGrapheneHandler> mHandlerMap = new HashMap<>();
|
||||
private List<BaseGrapheneHandler> pendingHandlerList = new ArrayList<>();
|
||||
private boolean printLogs;
|
||||
private boolean printLogs = true;
|
||||
|
||||
// State variables
|
||||
private boolean isUnsubscribing;
|
||||
|
@ -96,6 +97,7 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs
|
|||
builder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer());
|
||||
builder.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer());
|
||||
builder.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer());
|
||||
builder.registerTypeAdapter(OperationHistory.class, new OperationHistory.OperationHistoryDeserializer());
|
||||
this.gson = builder.create();
|
||||
}
|
||||
|
||||
|
@ -186,7 +188,7 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs
|
|||
}
|
||||
|
||||
payload.add(objects);
|
||||
ApiCall subscribe = new ApiCall(databaseApiId, RPC.GET_OBJECTS, payload, RPC.VERSION, MANUAL_SUBSCRIPTION_ID);
|
||||
ApiCall subscribe = new ApiCall(databaseApiId, RPC.CALL_GET_OBJECTS, payload, RPC.VERSION, MANUAL_SUBSCRIPTION_ID);
|
||||
websocket.sendText(subscribe.toJsonString());
|
||||
subscriptionCounter++;
|
||||
}else{
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
package cy.agorise.graphenej.api.android;
|
||||
|
||||
import com.google.gson.ExclusionStrategy;
|
||||
import com.google.gson.FieldAttributes;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import cy.agorise.graphenej.AccountOptions;
|
||||
import cy.agorise.graphenej.Asset;
|
||||
import cy.agorise.graphenej.AssetAmount;
|
||||
import cy.agorise.graphenej.AssetOptions;
|
||||
import cy.agorise.graphenej.Authority;
|
||||
import cy.agorise.graphenej.BaseOperation;
|
||||
import cy.agorise.graphenej.Extensions;
|
||||
import cy.agorise.graphenej.LimitOrder;
|
||||
import cy.agorise.graphenej.Transaction;
|
||||
import cy.agorise.graphenej.UserAccount;
|
||||
import cy.agorise.graphenej.api.calls.GetAccountByName;
|
||||
import cy.agorise.graphenej.api.calls.GetAccountHistoryByOperations;
|
||||
import cy.agorise.graphenej.api.calls.GetAccounts;
|
||||
import cy.agorise.graphenej.api.calls.GetBlock;
|
||||
import cy.agorise.graphenej.api.calls.GetBlockHeader;
|
||||
import cy.agorise.graphenej.api.calls.GetFullAccounts;
|
||||
import cy.agorise.graphenej.api.calls.GetLimitOrders;
|
||||
import cy.agorise.graphenej.api.calls.GetMarketHistory;
|
||||
import cy.agorise.graphenej.api.calls.GetObjects;
|
||||
import cy.agorise.graphenej.api.calls.GetRelativeAccountHistory;
|
||||
import cy.agorise.graphenej.api.calls.GetRequiredFees;
|
||||
import cy.agorise.graphenej.api.calls.ListAssets;
|
||||
import cy.agorise.graphenej.api.calls.LookupAssetSymbols;
|
||||
import cy.agorise.graphenej.models.AccountProperties;
|
||||
import cy.agorise.graphenej.models.Block;
|
||||
import cy.agorise.graphenej.models.BlockHeader;
|
||||
import cy.agorise.graphenej.models.BucketObject;
|
||||
import cy.agorise.graphenej.models.FullAccountDetails;
|
||||
import cy.agorise.graphenej.models.HistoryOperationDetail;
|
||||
import cy.agorise.graphenej.models.OperationHistory;
|
||||
import cy.agorise.graphenej.Memo;
|
||||
import cy.agorise.graphenej.operations.CustomOperation;
|
||||
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
||||
import cy.agorise.graphenej.operations.TransferOperation;
|
||||
|
||||
/**
|
||||
* Class used to store a mapping of request class to two important things:
|
||||
*
|
||||
* 1- The class to which the corresponding response should be de-serialized to
|
||||
* 2- An instance of the Gson class, with all required type adapters
|
||||
*/
|
||||
public class DeserializationMap {
|
||||
private final String TAG = this.getClass().getName();
|
||||
|
||||
private HashMap<Class, Class> mClassMap = new HashMap<>();
|
||||
|
||||
private HashMap<Class, Gson> mGsonMap = new HashMap<>();
|
||||
|
||||
public DeserializationMap(){
|
||||
Gson genericGson = new Gson();
|
||||
|
||||
// GetBlock
|
||||
mClassMap.put(GetBlock.class, Block.class);
|
||||
Gson getBlockGson = new GsonBuilder()
|
||||
.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer())
|
||||
.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer())
|
||||
.registerTypeAdapter(LimitOrderCreateOperation.class, new LimitOrderCreateOperation.LimitOrderCreateDeserializer())
|
||||
.registerTypeAdapter(CustomOperation.class, new CustomOperation.CustomOperationDeserializer())
|
||||
.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer())
|
||||
.create();
|
||||
mGsonMap.put(GetBlock.class, getBlockGson);
|
||||
|
||||
// GetAccounts
|
||||
mClassMap.put(GetAccounts.class, List.class);
|
||||
Gson getAccountsGson = new GsonBuilder()
|
||||
.setExclusionStrategies(new SkipAccountOptionsStrategy())
|
||||
.registerTypeAdapter(Authority.class, new Authority.AuthorityDeserializer())
|
||||
.registerTypeAdapter(AccountOptions.class, new AccountOptions.AccountOptionsDeserializer())
|
||||
.create();
|
||||
mGsonMap.put(GetAccounts.class, getAccountsGson);
|
||||
|
||||
// GetRequiredFees
|
||||
mClassMap.put(GetRequiredFees.class, List.class);
|
||||
Gson getRequiredFeesGson = new GsonBuilder()
|
||||
.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer())
|
||||
.create();
|
||||
mGsonMap.put(GetRequiredFees.class, getRequiredFeesGson);
|
||||
|
||||
// GetRelativeAccountHistory
|
||||
mClassMap.put(GetRelativeAccountHistory.class, List.class);
|
||||
Gson getRelativeAcountHistoryGson = new GsonBuilder()
|
||||
.setExclusionStrategies(new SkipAccountOptionsStrategy(), new SkipAssetOptionsStrategy())
|
||||
.registerTypeAdapter(BaseOperation.class, new BaseOperation.OperationDeserializer())
|
||||
.registerTypeAdapter(OperationHistory.class, new OperationHistory.OperationHistoryDeserializer())
|
||||
.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer())
|
||||
.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer())
|
||||
.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer())
|
||||
.create();
|
||||
mGsonMap.put(GetRelativeAccountHistory.class, getRelativeAcountHistoryGson);
|
||||
|
||||
// GetBlockHeader
|
||||
mClassMap.put(GetBlockHeader.class, BlockHeader.class);
|
||||
mGsonMap.put(GetBlockHeader.class, genericGson);
|
||||
|
||||
// GetMarketHistory
|
||||
mClassMap.put(GetMarketHistory.class, List.class);
|
||||
Gson getMarketHistoryGson = new GsonBuilder()
|
||||
.registerTypeAdapter(BucketObject.class, new BucketObject.BucketDeserializer())
|
||||
.create();
|
||||
mGsonMap.put(GetMarketHistory.class, getMarketHistoryGson);
|
||||
|
||||
// LookupAssetSymbols
|
||||
mClassMap.put(LookupAssetSymbols.class, List.class);
|
||||
Gson lookupAssetSymbolGson = new GsonBuilder()
|
||||
.registerTypeAdapter(Asset.class, new Asset.AssetDeserializer())
|
||||
.create();
|
||||
mGsonMap.put(LookupAssetSymbols.class, lookupAssetSymbolGson);
|
||||
|
||||
// GetObjects
|
||||
mClassMap.put(GetObjects.class, List.class);
|
||||
Gson getObjectsGson = new GsonBuilder()
|
||||
.registerTypeAdapter(Asset.class, new Asset.AssetDeserializer())
|
||||
.create();
|
||||
mGsonMap.put(GetObjects.class, getObjectsGson);
|
||||
|
||||
// ListAssets
|
||||
mClassMap.put(ListAssets.class, List.class);
|
||||
Gson listAssetsGson = new GsonBuilder()
|
||||
.registerTypeAdapter(Asset.class, new Asset.AssetDeserializer())
|
||||
.create();
|
||||
mGsonMap.put(ListAssets.class, listAssetsGson);
|
||||
|
||||
// GetAccountByName
|
||||
mClassMap.put(GetAccountByName.class, AccountProperties.class);
|
||||
Gson getAccountByNameGson = new GsonBuilder()
|
||||
.registerTypeAdapter(Authority.class, new Authority.AuthorityDeserializer())
|
||||
.registerTypeAdapter(AccountOptions.class, new AccountOptions.AccountOptionsDeserializer())
|
||||
.create();
|
||||
mGsonMap.put(GetAccountByName.class, getAccountByNameGson);
|
||||
|
||||
// GetLimitOrders
|
||||
mClassMap.put(GetLimitOrders.class, List.class);
|
||||
Gson getLimitOrdersGson = new GsonBuilder()
|
||||
.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer())
|
||||
.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer())
|
||||
.registerTypeAdapter(LimitOrder.class, new LimitOrder.LimitOrderDeserializer())
|
||||
.create();
|
||||
mGsonMap.put(GetLimitOrders.class, getLimitOrdersGson);
|
||||
|
||||
// GetAccountHistoryByOperations
|
||||
mClassMap.put(GetAccountHistoryByOperations.class, HistoryOperationDetail.class);
|
||||
Gson getAccountHistoryByOperationsGson = new GsonBuilder()
|
||||
.setExclusionStrategies(new DeserializationMap.SkipAccountOptionsStrategy(), new DeserializationMap.SkipAssetOptionsStrategy())
|
||||
.registerTypeAdapter(BaseOperation.class, new BaseOperation.OperationDeserializer())
|
||||
.registerTypeAdapter(OperationHistory.class, new OperationHistory.OperationHistoryDeserializer())
|
||||
.registerTypeAdapter(Extensions.class, new Extensions.ExtensionsDeserializer())
|
||||
.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer())
|
||||
.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer())
|
||||
.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer())
|
||||
.create();
|
||||
mGsonMap.put(GetAccountHistoryByOperations.class, getAccountHistoryByOperationsGson);
|
||||
|
||||
// GetFullAccounts
|
||||
mClassMap.put(GetFullAccounts.class, List.class);
|
||||
Gson getFullAccountsGson = new GsonBuilder()
|
||||
.registerTypeAdapter(FullAccountDetails.class, new FullAccountDetails.FullAccountDeserializer())
|
||||
.registerTypeAdapter(Authority.class, new Authority.AuthorityDeserializer())
|
||||
.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer())
|
||||
.registerTypeAdapter(AccountOptions.class, new AccountOptions.AccountOptionsDeserializer())
|
||||
.create();
|
||||
mGsonMap.put(GetFullAccounts.class, getFullAccountsGson);
|
||||
}
|
||||
|
||||
public Class getReceivedClass(Class _class){
|
||||
return mClassMap.get(_class);
|
||||
}
|
||||
|
||||
public Gson getGson(Class aClass) {
|
||||
return mGsonMap.get(aClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is required in order to break a recursion loop when de-serializing the
|
||||
* AccountProperties class instance.
|
||||
*/
|
||||
public static class SkipAccountOptionsStrategy implements ExclusionStrategy {
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipField(FieldAttributes f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipClass(Class<?> clazz) {
|
||||
return clazz == AccountOptions.class;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is required in order to break a recursion loop when de-serializing the
|
||||
* AssetAmount instance.
|
||||
*/
|
||||
public static class SkipAssetOptionsStrategy implements ExclusionStrategy {
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipField(FieldAttributes f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipClass(Class<?> clazz) {
|
||||
return clazz == AssetOptions.class;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,526 @@
|
|||
package cy.agorise.graphenej.api.android;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import cy.agorise.graphenej.Asset;
|
||||
import cy.agorise.graphenej.AssetAmount;
|
||||
import cy.agorise.graphenej.BaseOperation;
|
||||
import cy.agorise.graphenej.LimitOrder;
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.Transaction;
|
||||
import cy.agorise.graphenej.UserAccount;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.api.ConnectionStatusUpdate;
|
||||
import cy.agorise.graphenej.api.bitshares.Nodes;
|
||||
import cy.agorise.graphenej.api.calls.ApiCallable;
|
||||
import cy.agorise.graphenej.api.calls.GetAccounts;
|
||||
import cy.agorise.graphenej.api.calls.GetFullAccounts;
|
||||
import cy.agorise.graphenej.api.calls.GetLimitOrders;
|
||||
import cy.agorise.graphenej.api.calls.GetMarketHistory;
|
||||
import cy.agorise.graphenej.api.calls.GetObjects;
|
||||
import cy.agorise.graphenej.api.calls.GetRelativeAccountHistory;
|
||||
import cy.agorise.graphenej.api.calls.GetRequiredFees;
|
||||
import cy.agorise.graphenej.api.calls.ListAssets;
|
||||
import cy.agorise.graphenej.models.AccountProperties;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
import cy.agorise.graphenej.models.Block;
|
||||
import cy.agorise.graphenej.models.BlockHeader;
|
||||
import cy.agorise.graphenej.models.BucketObject;
|
||||
import cy.agorise.graphenej.models.DynamicGlobalProperties;
|
||||
import cy.agorise.graphenej.models.FullAccountDetails;
|
||||
import cy.agorise.graphenej.models.HistoryOperationDetail;
|
||||
import cy.agorise.graphenej.models.JsonRpcNotification;
|
||||
import cy.agorise.graphenej.models.JsonRpcResponse;
|
||||
import cy.agorise.graphenej.models.OperationHistory;
|
||||
import cy.agorise.graphenej.Memo;
|
||||
import cy.agorise.graphenej.operations.CustomOperation;
|
||||
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
||||
import cy.agorise.graphenej.operations.TransferOperation;
|
||||
import io.reactivex.annotations.Nullable;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.WebSocket;
|
||||
import okhttp3.WebSocketListener;
|
||||
|
||||
/**
|
||||
* Service in charge of maintaining a connection to the full node.
|
||||
*/
|
||||
|
||||
public class NetworkService extends Service {
|
||||
private final String TAG = this.getClass().getName();
|
||||
|
||||
private static final int NORMAL_CLOSURE_STATUS = 1000;
|
||||
|
||||
public static final String KEY_USERNAME = "key_username";
|
||||
|
||||
public static final String KEY_PASSWORD = "key_password";
|
||||
|
||||
public static final String KEY_REQUESTED_APIS = "key_requested_apis";
|
||||
|
||||
/**
|
||||
* Constant used to pass a custom list of node URLs. This should be a simple
|
||||
* comma separated list of URLs.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* wss://domain1.com/ws,wss://domain2.com/ws,wss://domain3.com/ws
|
||||
*/
|
||||
public static final String KEY_CUSTOM_NODE_URLS = "key_custom_node_urls";
|
||||
|
||||
private final IBinder mBinder = new LocalBinder();
|
||||
|
||||
private WebSocket mWebSocket;
|
||||
|
||||
private int mSocketIndex;
|
||||
|
||||
// Username and password used to connect to a specific node
|
||||
private String mUsername;
|
||||
private String mPassword;
|
||||
|
||||
private boolean isLoggedIn = false;
|
||||
|
||||
private String mLastCall;
|
||||
private long mCurrentId = 0;
|
||||
|
||||
// Requested APIs passed to this service
|
||||
private int mRequestedApis;
|
||||
|
||||
// Variable used to keep track of the currently obtained API accesses
|
||||
private HashMap<Integer, Integer> mApiIds = new HashMap<Integer, Integer>();
|
||||
|
||||
private ArrayList<String> mNodeUrls = new ArrayList<>();
|
||||
|
||||
private Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer())
|
||||
.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer())
|
||||
.registerTypeAdapter(LimitOrderCreateOperation.class, new LimitOrderCreateOperation.LimitOrderCreateDeserializer())
|
||||
.registerTypeAdapter(CustomOperation.class, new CustomOperation.CustomOperationDeserializer())
|
||||
.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer())
|
||||
.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer())
|
||||
.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer())
|
||||
.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer())
|
||||
.registerTypeAdapter(BaseOperation.class, new BaseOperation.OperationDeserializer())
|
||||
.registerTypeAdapter(OperationHistory.class, new OperationHistory.OperationHistoryDeserializer())
|
||||
.registerTypeAdapter(JsonRpcNotification.class, new JsonRpcNotification.JsonRpcNotificationDeserializer())
|
||||
.create();
|
||||
|
||||
// Map used to keep track of outgoing request ids and its request types. This is just
|
||||
// one of two required mappings. The second one is implemented by the DeserializationMap
|
||||
// class.
|
||||
private HashMap<Long, Class> mRequestClassMap = new HashMap<>();
|
||||
|
||||
// This class is used to keep track of the mapping between request classes and response
|
||||
// payload classes. It also provides a handy method that returns a Gson deserializer instance
|
||||
// suited for every response type.
|
||||
private DeserializationMap mDeserializationMap = new DeserializationMap();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
|
||||
// Retrieving credentials and requested API data from the shared preferences
|
||||
mUsername = pref.getString(NetworkService.KEY_USERNAME, "");
|
||||
mPassword = pref.getString(NetworkService.KEY_PASSWORD, "");
|
||||
mRequestedApis = pref.getInt(NetworkService.KEY_REQUESTED_APIS, -1);
|
||||
|
||||
// If the user of the library desires, a custom list of node URLs can
|
||||
// be passed using the KEY_CUSTOM_NODE_URLS constant
|
||||
String serializedNodeUrls = pref.getString(NetworkService.KEY_CUSTOM_NODE_URLS, "");
|
||||
|
||||
// Deciding whether to use an externally provided list of node URLs, or use our internal one
|
||||
if(serializedNodeUrls.equals("")){
|
||||
mNodeUrls.addAll(Arrays.asList(Nodes.NODE_URLS));
|
||||
}else{
|
||||
String[] urls = serializedNodeUrls.split(",");
|
||||
mNodeUrls.addAll(Arrays.asList(urls));
|
||||
}
|
||||
connect();
|
||||
}
|
||||
|
||||
private void connect(){
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
String url = mNodeUrls.get(mSocketIndex % mNodeUrls.size());
|
||||
Log.d(TAG,"Trying to connect with: "+url);
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
client.newWebSocket(request, mWebSocketListener);
|
||||
}
|
||||
|
||||
public long sendMessage(String message){
|
||||
if(mWebSocket != null){
|
||||
if(mWebSocket.send(message)){
|
||||
Log.v(TAG,"-> " + message);
|
||||
return mCurrentId;
|
||||
}
|
||||
}else{
|
||||
throw new RuntimeException("Websocket connection has not yet been established");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that will send a message to the full node, and takes as an argument one of the
|
||||
* API call wrapper classes. This is the preferred method of sending blockchain API calls.
|
||||
*
|
||||
* @param apiCallable The object that will get serialized into a request
|
||||
* @param requiredApi The required APIs for this specific request. Should be one of the
|
||||
* constants specified in the ApiAccess class.
|
||||
* @return The id of the message that was just sent, or -1 if no message was sent.
|
||||
*/
|
||||
public long sendMessage(ApiCallable apiCallable, int requiredApi){
|
||||
if(requiredApi != -1 && mApiIds.containsKey(requiredApi) || requiredApi == ApiAccess.API_NONE){
|
||||
int apiId = 0;
|
||||
if(requiredApi != ApiAccess.API_NONE)
|
||||
apiId = mApiIds.get(requiredApi);
|
||||
ApiCall call = apiCallable.toApiCall(apiId, ++mCurrentId);
|
||||
mRequestClassMap.put(mCurrentId, apiCallable.getClass());
|
||||
if(mWebSocket != null && mWebSocket.send(call.toJsonString())){
|
||||
Log.v(TAG,"-> "+call.toJsonString());
|
||||
return mCurrentId;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to inform any external party a clue about the current connectivity status
|
||||
* @return True if the service is currently connected and logged in, false otherwise.
|
||||
*/
|
||||
public boolean isConnected(){
|
||||
return mWebSocket != null && isLoggedIn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if(mWebSocket != null)
|
||||
mWebSocket.close(NORMAL_CLOSURE_STATUS, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used for the client Binder. Because we know this service always
|
||||
* runs in the same process as its clients, we don't need to deal with IPC.
|
||||
*/
|
||||
public class LocalBinder extends Binder {
|
||||
public NetworkService getService() {
|
||||
// Return this instance of LocalService so clients can call public methods
|
||||
return NetworkService.this;
|
||||
}
|
||||
}
|
||||
|
||||
private WebSocketListener mWebSocketListener = new WebSocketListener() {
|
||||
|
||||
@Override
|
||||
public void onOpen(WebSocket webSocket, Response response) {
|
||||
super.onOpen(webSocket, response);
|
||||
mWebSocket = webSocket;
|
||||
|
||||
// Notifying all listeners about the new connection status
|
||||
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.CONNECTED, ApiAccess.API_NONE));
|
||||
|
||||
// If we're not yet logged in, we should do it now
|
||||
if(!isLoggedIn){
|
||||
ArrayList<Serializable> loginParams = new ArrayList<>();
|
||||
loginParams.add(mUsername);
|
||||
loginParams.add(mPassword);
|
||||
ApiCall loginCall = new ApiCall(1, RPC.CALL_LOGIN, loginParams, RPC.VERSION, ++mCurrentId);
|
||||
mLastCall = RPC.CALL_LOGIN;
|
||||
sendMessage(loginCall.toJsonString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(WebSocket webSocket, String text) {
|
||||
super.onMessage(webSocket, text);
|
||||
Log.v(TAG,"<- "+text);
|
||||
JsonRpcNotification notification = gson.fromJson(text, JsonRpcNotification.class);
|
||||
|
||||
if(notification.method != null){
|
||||
// If we are dealing with a notification
|
||||
handleJsonRpcNotification(notification);
|
||||
}else{
|
||||
// If we are dealing with a response
|
||||
JsonRpcResponse<?> response = gson.fromJson(text, JsonRpcResponse.class);
|
||||
if(response.result != null){
|
||||
// Handling initial handshake with the full node (authentication and API access checks)
|
||||
if(response.result instanceof Double || response.result instanceof Boolean){
|
||||
switch (mLastCall) {
|
||||
case RPC.CALL_LOGIN:
|
||||
isLoggedIn = true;
|
||||
|
||||
// Broadcasting result
|
||||
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.AUTHENTICATED, ApiAccess.API_NONE));
|
||||
|
||||
checkNextRequestedApiAccess();
|
||||
break;
|
||||
case RPC.CALL_DATABASE: {
|
||||
// Deserializing integer response
|
||||
Type IntegerJsonResponse = new TypeToken<JsonRpcResponse<Integer>>() {}.getType();
|
||||
JsonRpcResponse<Integer> apiIdResponse = gson.fromJson(text, IntegerJsonResponse);
|
||||
|
||||
// Storing the "database" api id
|
||||
mApiIds.put(ApiAccess.API_DATABASE, apiIdResponse.result);
|
||||
|
||||
// Broadcasting result
|
||||
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.API_UPDATE, ApiAccess.API_DATABASE));
|
||||
|
||||
checkNextRequestedApiAccess();
|
||||
break;
|
||||
}
|
||||
case RPC.CALL_HISTORY: {
|
||||
// Deserializing integer response
|
||||
Type IntegerJsonResponse = new TypeToken<JsonRpcResponse<Integer>>() {}.getType();
|
||||
JsonRpcResponse<Integer> apiIdResponse = gson.fromJson(text, IntegerJsonResponse);
|
||||
|
||||
// Broadcasting result
|
||||
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.API_UPDATE, ApiAccess.API_HISTORY));
|
||||
|
||||
// Storing the "history" api id
|
||||
mApiIds.put(ApiAccess.API_HISTORY, apiIdResponse.result);
|
||||
|
||||
checkNextRequestedApiAccess();
|
||||
break;
|
||||
}
|
||||
case RPC.CALL_NETWORK_BROADCAST:
|
||||
// Deserializing integer response
|
||||
Type IntegerJsonResponse = new TypeToken<JsonRpcResponse<Integer>>() {}.getType();
|
||||
JsonRpcResponse<Integer> apiIdResponse = gson.fromJson(text, IntegerJsonResponse);
|
||||
|
||||
// Broadcasting result
|
||||
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.API_UPDATE, ApiAccess.API_NETWORK_BROADCAST));
|
||||
|
||||
// Storing the "network_broadcast" api access
|
||||
mApiIds.put(ApiAccess.API_NETWORK_BROADCAST, apiIdResponse.result);
|
||||
|
||||
// All calls have been handled at this point
|
||||
mLastCall = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(response.error != null && response.error.message != null){
|
||||
// We could not make sense of this incoming message, just log a warning
|
||||
Log.w(TAG,"Error.Msg: "+response.error.message);
|
||||
}
|
||||
// Properly de-serialize all other fields and broadcasts to the event bus
|
||||
handleJsonRpcResponse(response, text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method that will de-serialize all fields of every kind of JSON-RPC response
|
||||
* and broadcast it to the event bus.
|
||||
*
|
||||
* @param response De-serialized response
|
||||
* @param text Raw text, as received
|
||||
*/
|
||||
private void handleJsonRpcResponse(JsonRpcResponse response, String text){
|
||||
JsonRpcResponse parsedResponse = null;
|
||||
|
||||
Class requestClass = mRequestClassMap.get(response.id);
|
||||
if(requestClass != null){
|
||||
// Removing the class entry in the map
|
||||
mRequestClassMap.remove(response.id);
|
||||
|
||||
// Obtaining the response payload class
|
||||
Class responsePayloadClass = mDeserializationMap.getReceivedClass(requestClass);
|
||||
Gson gson = mDeserializationMap.getGson(requestClass);
|
||||
if(responsePayloadClass == Block.class){
|
||||
// If the response payload is a Block instance, we proceed to de-serialize it
|
||||
Type GetBlockResponse = new TypeToken<JsonRpcResponse<Block>>() {}.getType();
|
||||
parsedResponse = gson.fromJson(text, GetBlockResponse);
|
||||
}else if(responsePayloadClass == BlockHeader.class){
|
||||
// If the response payload is a BlockHeader instance, we proceed to de-serialize it
|
||||
Type GetBlockHeaderResponse = new TypeToken<JsonRpcResponse<BlockHeader>>(){}.getType();
|
||||
parsedResponse = gson.fromJson(text, GetBlockHeaderResponse);
|
||||
} else if(responsePayloadClass == AccountProperties.class){
|
||||
Type GetAccountByNameResponse = new TypeToken<JsonRpcResponse<AccountProperties>>(){}.getType();
|
||||
parsedResponse = gson.fromJson(text, GetAccountByNameResponse);
|
||||
} else if(responsePayloadClass == HistoryOperationDetail.class){
|
||||
Type GetAccountHistoryByOperationsResponse = new TypeToken<JsonRpcResponse<HistoryOperationDetail>>(){}.getType();
|
||||
parsedResponse = gson.fromJson(text, GetAccountHistoryByOperationsResponse);
|
||||
}else if(responsePayloadClass == List.class){
|
||||
// If the response payload is a List, further inquiry is required in order to
|
||||
// determine a list of what is expected here
|
||||
if(requestClass == GetAccounts.class){
|
||||
// If the request call was the wrapper to the get_accounts API call, we know
|
||||
// the response should be in the form of a JsonRpcResponse<List<AccountProperties>>
|
||||
// so we proceed with that
|
||||
Type GetAccountsResponse = new TypeToken<JsonRpcResponse<List<AccountProperties>>>(){}.getType();
|
||||
parsedResponse = gson.fromJson(text, GetAccountsResponse);
|
||||
}else if(requestClass == GetRequiredFees.class){
|
||||
Type GetRequiredFeesResponse = new TypeToken<JsonRpcResponse<List<AssetAmount>>>(){}.getType();
|
||||
parsedResponse = gson.fromJson(text, GetRequiredFeesResponse);
|
||||
}else if(requestClass == GetRelativeAccountHistory.class){
|
||||
Type RelativeAccountHistoryResponse = new TypeToken<JsonRpcResponse<List<OperationHistory>>>(){}.getType();
|
||||
parsedResponse = gson.fromJson(text, RelativeAccountHistoryResponse);
|
||||
}else if(requestClass == GetMarketHistory.class){
|
||||
Type GetMarketHistoryResponse = new TypeToken<JsonRpcResponse<List<BucketObject>>>(){}.getType();
|
||||
parsedResponse = gson.fromJson(text, GetMarketHistoryResponse);
|
||||
}else if(requestClass == GetObjects.class){
|
||||
parsedResponse = handleGetObject(text);
|
||||
}else if(requestClass == ListAssets.class){
|
||||
Type LisAssetsResponse = new TypeToken<JsonRpcResponse<List<Asset>>>(){}.getType();
|
||||
parsedResponse = gson.fromJson(text, LisAssetsResponse);
|
||||
}else if(requestClass == GetLimitOrders.class){
|
||||
Type GetLimitOrdersResponse = new TypeToken<JsonRpcResponse<List<LimitOrder>>>() {}.getType();
|
||||
parsedResponse = gson.fromJson(text, GetLimitOrdersResponse);
|
||||
} else if (requestClass == GetFullAccounts.class) {
|
||||
Type GetFullAccountsResponse = new TypeToken<JsonRpcResponse<List<FullAccountDetails>>>(){}.getType();
|
||||
parsedResponse = gson.fromJson(text, GetFullAccountsResponse);
|
||||
} else {
|
||||
Log.w(TAG,"Unknown request class");
|
||||
}
|
||||
}else{
|
||||
Log.w(TAG,"Unhandled situation");
|
||||
}
|
||||
}
|
||||
|
||||
// In case the parsedResponse instance is null, we fall back to the raw response
|
||||
if(parsedResponse == null){
|
||||
parsedResponse = response;
|
||||
}
|
||||
// Broadcasting the parsed response to all interested listeners
|
||||
RxBus.getBusInstance().send(parsedResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method that will just broadcast a de-serialized notification to all interested parties
|
||||
* @param notification De-serialized notification
|
||||
*/
|
||||
private void handleJsonRpcNotification(JsonRpcNotification notification){
|
||||
// Broadcasting the parsed notification to all interested listeners
|
||||
RxBus.getBusInstance().send(notification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to try to deserialize a 'get_objects' API call. Since this request can be used
|
||||
* for several types of objects, the de-serialization procedure can be a bit more complex.
|
||||
*
|
||||
* @param response Response to a 'get_objects' API call
|
||||
*/
|
||||
private JsonRpcResponse handleGetObject(String response){
|
||||
//TODO: Implement a proper de-serialization logic
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to check all possible API accesses.
|
||||
*
|
||||
* The service will try to obtain sequentially API access ids for the following APIs:
|
||||
*
|
||||
* - Database
|
||||
* - History
|
||||
* - Network broadcast
|
||||
*/
|
||||
private void checkNextRequestedApiAccess(){
|
||||
if( (mRequestedApis & ApiAccess.API_DATABASE) == ApiAccess.API_DATABASE &&
|
||||
mApiIds.get(ApiAccess.API_DATABASE) == null){
|
||||
// If we need the "database" api access and we don't yet have it
|
||||
|
||||
ApiCall apiCall = new ApiCall(1, RPC.CALL_DATABASE, null, RPC.VERSION, ++mCurrentId);
|
||||
mLastCall = RPC.CALL_DATABASE;
|
||||
sendMessage(apiCall.toJsonString());
|
||||
} else if( (mRequestedApis & ApiAccess.API_HISTORY) == ApiAccess.API_HISTORY &&
|
||||
mApiIds.get(ApiAccess.API_HISTORY) == null){
|
||||
// If we need the "history" api access and we don't yet have it
|
||||
|
||||
ApiCall apiCall = new ApiCall(1, RPC.CALL_HISTORY, null, RPC.VERSION, ++mCurrentId);
|
||||
mLastCall = RPC.CALL_HISTORY;
|
||||
sendMessage(apiCall.toJsonString());
|
||||
}else if( (mRequestedApis & ApiAccess.API_NETWORK_BROADCAST) == ApiAccess.API_NETWORK_BROADCAST &&
|
||||
mApiIds.get(ApiAccess.API_NETWORK_BROADCAST) == null){
|
||||
// If we need the "network_broadcast" api access and we don't yet have it
|
||||
|
||||
ApiCall apiCall = new ApiCall(1, RPC.CALL_NETWORK_BROADCAST, null, RPC.VERSION, ++mCurrentId);
|
||||
mLastCall = RPC.CALL_NETWORK_BROADCAST;
|
||||
sendMessage(apiCall.toJsonString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed(WebSocket webSocket, int code, String reason) {
|
||||
super.onClosed(webSocket, code, reason);
|
||||
Log.d(TAG,"onClosed");
|
||||
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.DISCONNECTED, ApiAccess.API_NONE));
|
||||
|
||||
isLoggedIn = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
|
||||
super.onFailure(webSocket, t, response);
|
||||
Log.e(TAG,"onFailure. Exception: "+t.getClass().getName()+", Msg: "+t.getMessage());
|
||||
// Logging error stack trace
|
||||
for(StackTraceElement element : t.getStackTrace()){
|
||||
Log.e(TAG,String.format("%s#%s:%s", element.getClassName(), element.getMethodName(), element.getLineNumber()));
|
||||
}
|
||||
// Registering current status
|
||||
isLoggedIn = false;
|
||||
mCurrentId = 0;
|
||||
mApiIds.clear();
|
||||
|
||||
// If there is a response, we print it
|
||||
if(response != null){
|
||||
Log.e(TAG,"Response: "+response.message());
|
||||
}
|
||||
|
||||
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.DISCONNECTED, ApiAccess.API_NONE));
|
||||
mSocketIndex++;
|
||||
|
||||
if(mSocketIndex > mNodeUrls.size() * 3){
|
||||
Log.e(TAG,"Giving up on connections");
|
||||
stopSelf();
|
||||
}else{
|
||||
connect();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to check whether or not the network service is connected to a node that
|
||||
* offers a specific API.
|
||||
*
|
||||
* @param whichApi The API we want to use.
|
||||
* @return True if the node has got that API enabled, false otherwise
|
||||
*/
|
||||
public boolean hasApiId(int whichApi){
|
||||
return mApiIds.get(whichApi) != null;
|
||||
}
|
||||
|
||||
public ArrayList<String> getNodeUrls() {
|
||||
return mNodeUrls;
|
||||
}
|
||||
|
||||
public void setNodeUrls(ArrayList<String> mNodeUrls) {
|
||||
this.mNodeUrls = mNodeUrls;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package cy.agorise.graphenej.api.android;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* This class should be instantiated at the application level of the android app.
|
||||
*
|
||||
* It will monitor the interaction between the different activities of an app and help us decide
|
||||
* when the connection to the full node should be interrupted.
|
||||
*/
|
||||
|
||||
public class NetworkServiceManager implements Application.ActivityLifecycleCallbacks {
|
||||
private final String TAG = this.getClass().getName();
|
||||
|
||||
/**
|
||||
* Constant used to specify how long will the app wait for another activity to go through its starting life
|
||||
* cycle events before running the teardownConnectionTask task.
|
||||
*
|
||||
* This is used as a means to detect whether or not the user has left the app.
|
||||
*/
|
||||
private final int DISCONNECT_DELAY = 1500;
|
||||
|
||||
/**
|
||||
* Handler instance used to schedule tasks back to the main thread
|
||||
*/
|
||||
private Handler mHandler = new Handler();
|
||||
|
||||
/**
|
||||
* Weak reference to the application context
|
||||
*/
|
||||
private WeakReference<Context> mContextReference;
|
||||
|
||||
// In case we want to interact directly with the service
|
||||
private NetworkService mService;
|
||||
|
||||
/**
|
||||
* Runnable used to schedule a service disconnection once the app is not visible to the user for
|
||||
* more than DISCONNECT_DELAY milliseconds.
|
||||
*/
|
||||
private final Runnable mDisconnectRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Context context = mContextReference.get();
|
||||
if(mService != null){
|
||||
context.unbindService(mServiceConnection);
|
||||
mService = null;
|
||||
}
|
||||
context.stopService(new Intent(context, NetworkService.class));
|
||||
}
|
||||
};
|
||||
|
||||
public NetworkServiceManager(Context context){
|
||||
mContextReference = new WeakReference<Context>(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle bundle) {
|
||||
if(mService == null){
|
||||
// Starting a NetworkService instance
|
||||
Context context = mContextReference.get();
|
||||
Intent intent = new Intent(context, NetworkService.class);
|
||||
context.startService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
mHandler.removeCallbacks(mDisconnectRunnable);
|
||||
if(mService == null){
|
||||
Context context = mContextReference.get();
|
||||
Intent intent = new Intent(context, NetworkService.class);
|
||||
context.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
mHandler.postDelayed(mDisconnectRunnable, DISCONNECT_DELAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {}
|
||||
|
||||
/** Defines callbacks for backend binding, passed to bindService() */
|
||||
private ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className,
|
||||
IBinder service) {
|
||||
// We've bound to LocalService, cast the IBinder and get LocalService instance
|
||||
NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service;
|
||||
mService = binder.getService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName componentName) {}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package cy.agorise.graphenej.api.android;
|
||||
|
||||
import com.jakewharton.rxrelay2.PublishRelay;
|
||||
import com.jakewharton.rxrelay2.Relay;
|
||||
|
||||
import io.reactivex.BackpressureStrategy;
|
||||
import io.reactivex.Flowable;
|
||||
|
||||
/**
|
||||
* Explained here: https://blog.kaush.co/2014/12/24/implementing-an-event-bus-with-rxjava-rxbus/
|
||||
*/
|
||||
public class RxBus {
|
||||
|
||||
private static RxBus rxBus;
|
||||
|
||||
public static final RxBus getBusInstance(){
|
||||
if(rxBus == null){
|
||||
rxBus = new RxBus();
|
||||
}
|
||||
return rxBus;
|
||||
}
|
||||
|
||||
private final Relay<Object> _bus = PublishRelay.create().toSerialized();
|
||||
|
||||
public void send(Object o) {
|
||||
_bus.accept(o);
|
||||
}
|
||||
|
||||
public Flowable<Object> asFlowable() {
|
||||
return _bus.toFlowable(BackpressureStrategy.LATEST);
|
||||
}
|
||||
|
||||
public boolean hasObservers() {
|
||||
return _bus.hasObservers();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package cy.agorise.graphenej.api.bitshares;
|
||||
|
||||
/**
|
||||
* Known public nodes
|
||||
*/
|
||||
|
||||
public class Nodes {
|
||||
public static final String[] NODE_URLS = {
|
||||
"wss://dexnode.net/ws", // Dallas, USA
|
||||
"wss://bitshares.crypto.fans/ws", // Munich, Germany
|
||||
"wss://bitshares.openledger.info/ws", // Openledger node
|
||||
};
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by all classes that will produce an ApiCall object instance
|
||||
* as a result.
|
||||
*/
|
||||
|
||||
public interface ApiCallable {
|
||||
|
||||
/**
|
||||
*
|
||||
* @return An instance of the {@link ApiCall} class
|
||||
*/
|
||||
ApiCall toApiCall(int apiId, long sequenceId);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
public class CancelAllSubscriptions implements ApiCallable {
|
||||
public static final int REQUIRED_API = ApiAccess.API_DATABASE;
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
return new ApiCall(apiId, RPC.CALL_CANCEL_ALL_SUBSCRIPTIONS, new ArrayList<Serializable>(), RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
public class GetAccountByName implements ApiCallable {
|
||||
public static final int REQUIRED_API = ApiAccess.API_NONE;
|
||||
|
||||
private String accountName;
|
||||
|
||||
public GetAccountByName(String name){
|
||||
this.accountName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
ArrayList<Serializable> accountParams = new ArrayList<>();
|
||||
accountParams.add(this.accountName);
|
||||
return new ApiCall(apiId, RPC.CALL_GET_ACCOUNT_BY_NAME, accountParams, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.UserAccount;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
public class GetAccountHistory implements ApiCallable {
|
||||
public static final int REQUIRED_API = ApiAccess.API_HISTORY;
|
||||
|
||||
private UserAccount mUserAccount;
|
||||
private String startOperation;
|
||||
private String endOperation;
|
||||
private int limit;
|
||||
|
||||
public GetAccountHistory(UserAccount userAccount, String start, String end, int limit){
|
||||
this.mUserAccount = userAccount;
|
||||
this.startOperation = start;
|
||||
this.endOperation = end;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
public GetAccountHistory(String userId, String start, String end, int limit){
|
||||
this.mUserAccount = new UserAccount(userId);
|
||||
this.startOperation = start;
|
||||
this.endOperation = end;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
ArrayList<Serializable> params = new ArrayList<>();
|
||||
params.add(mUserAccount.getObjectId());
|
||||
params.add(endOperation);
|
||||
params.add(limit);
|
||||
params.add(startOperation);
|
||||
return new ApiCall(apiId, RPC.CALL_GET_ACCOUNT_HISTORY, params, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import cy.agorise.graphenej.OperationType;
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.UserAccount;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
public class GetAccountHistoryByOperations implements ApiCallable {
|
||||
|
||||
public static final int REQUIRED_API = ApiAccess.API_HISTORY;
|
||||
|
||||
private UserAccount mUserAccount;
|
||||
private List<OperationType> mOperationTypes;
|
||||
private long mStart;
|
||||
private long mLimit;
|
||||
|
||||
/**
|
||||
* @param userAccount The user account that should be queried
|
||||
* @param operationsTypes The IDs of the operation we want to get operations in the account( 0 = transfer , 1 = limit order create, ...)
|
||||
* @param start The sequence number where to start listing operations
|
||||
* @param limit The max number of entries to return (from start number)
|
||||
*/
|
||||
public GetAccountHistoryByOperations(UserAccount userAccount, List<OperationType> operationsTypes, long start, long limit){
|
||||
this.mUserAccount = userAccount;
|
||||
this.mOperationTypes = operationsTypes;
|
||||
this.mStart = start;
|
||||
this.mLimit = limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userAccount The user account that should be queried
|
||||
* @param operationsTypes The IDs of the operation we want to get operations in the account( 0 = transfer , 1 = limit order create, ...)
|
||||
* @param start The sequence number where to start listing operations
|
||||
* @param limit The max number of entries to return (from start number)
|
||||
*/
|
||||
public GetAccountHistoryByOperations(String userAccount, List<OperationType> operationsTypes, long start, long limit){
|
||||
if(userAccount.matches("^1\\.2\\.\\d*$")){
|
||||
this.mUserAccount = new UserAccount(userAccount);
|
||||
}else{
|
||||
this.mUserAccount = new UserAccount("", userAccount);
|
||||
}
|
||||
this.mOperationTypes = operationsTypes;
|
||||
this.mStart = start;
|
||||
this.mLimit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
ArrayList<Serializable> params = new ArrayList<>();
|
||||
if(mUserAccount.getName() != null){
|
||||
params.add(mUserAccount.getName());
|
||||
}else{
|
||||
params.add(mUserAccount.getObjectId());
|
||||
}
|
||||
ArrayList<Integer> operationTypes = new ArrayList<>();
|
||||
for(OperationType operationType : mOperationTypes){
|
||||
operationTypes.add(operationType.ordinal());
|
||||
}
|
||||
params.add(operationTypes);
|
||||
params.add(mStart);
|
||||
params.add(mLimit);
|
||||
return new ApiCall(apiId, RPC.CALL_GET_ACCOUNT_HISTORY_BY_OPERATIONS, params, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.UserAccount;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
/**
|
||||
* Wrapper around the "get_accounts" API call.
|
||||
*/
|
||||
public class GetAccounts implements ApiCallable {
|
||||
public static final int REQUIRED_API = ApiAccess.API_NONE;
|
||||
|
||||
private List<UserAccount> mUserAccounts;
|
||||
|
||||
public GetAccounts(List<UserAccount> accountList){
|
||||
mUserAccounts = accountList;
|
||||
}
|
||||
|
||||
public GetAccounts(UserAccount userAccount){
|
||||
mUserAccounts = new ArrayList<>();
|
||||
mUserAccounts.add(userAccount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
ArrayList<Serializable> params = new ArrayList<>();
|
||||
ArrayList<Serializable> accountIds = new ArrayList<>();
|
||||
for(UserAccount userAccount : mUserAccounts){
|
||||
accountIds.add(userAccount.getObjectId());
|
||||
}
|
||||
params.add(accountIds);
|
||||
return new ApiCall(apiId, RPC.CALL_GET_ACCOUNTS, params, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
/**
|
||||
* Wrapper around the "get_block" API call.
|
||||
*/
|
||||
|
||||
public class GetBlock implements ApiCallable {
|
||||
|
||||
public static final int REQUIRED_API = ApiAccess.API_DATABASE;
|
||||
|
||||
private long blockNumber;
|
||||
|
||||
public GetBlock(long blockNum){
|
||||
this.blockNumber = blockNum;
|
||||
}
|
||||
|
||||
public ApiCall toApiCall(int apiId, long sequenceId){
|
||||
ArrayList<Serializable> params = new ArrayList<>();
|
||||
String blockNum = String.format("%d", this.blockNumber);
|
||||
params.add(blockNum);
|
||||
return new ApiCall(apiId, RPC.CALL_GET_BLOCK, params, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
/**
|
||||
* Wrapper around the "get_block_header" API call. To be used in the single-connection mode.
|
||||
*/
|
||||
public class GetBlockHeader implements ApiCallable {
|
||||
public static final int REQUIRED_API = ApiAccess.API_DATABASE;
|
||||
|
||||
private long blockNumber;
|
||||
|
||||
public GetBlockHeader(long number){
|
||||
this.blockNumber = number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
ArrayList<Serializable> params = new ArrayList<>();
|
||||
String blockNum = String.format("%d", this.blockNumber);
|
||||
params.add(blockNum);
|
||||
|
||||
return new ApiCall(apiId, RPC.CALL_GET_BLOCK_HEADER, params, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
/**
|
||||
* Wrapper around the 'get_full_accounts' API call.
|
||||
*/
|
||||
public class GetFullAccounts implements ApiCallable {
|
||||
|
||||
public static final int REQUIRED_API = ApiAccess.API_NONE;
|
||||
|
||||
private List<String> mUserAccounts;
|
||||
private boolean mSubscribe;
|
||||
|
||||
public GetFullAccounts(List<String> accounts, boolean subscribe){
|
||||
this.mUserAccounts = accounts;
|
||||
this.mSubscribe = subscribe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
ArrayList<Serializable> params = new ArrayList<>();
|
||||
ArrayList<Serializable> accounts = new ArrayList<Serializable>(mUserAccounts);
|
||||
params.add(accounts);
|
||||
params.add(mSubscribe);
|
||||
return new ApiCall(apiId, RPC.CALL_GET_FULL_ACCOUNTS, params, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
/** Class that implements get_limit_orders request handler.
|
||||
*
|
||||
* Get limit orders in a given market.
|
||||
*
|
||||
* The request returns the limit orders, ordered from least price to greatest
|
||||
*
|
||||
* @see <a href="https://goo.gl/5sRTRq">get_limit_orders API doc</a>
|
||||
*
|
||||
*/
|
||||
public class GetLimitOrders implements ApiCallable {
|
||||
|
||||
public static final int REQUIRED_API = ApiAccess.API_DATABASE;
|
||||
|
||||
private String a;
|
||||
private String b;
|
||||
private int limit;
|
||||
|
||||
public GetLimitOrders(String a, String b, int limit){
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
ArrayList<Serializable> parameters = new ArrayList<>();
|
||||
parameters.add(a);
|
||||
parameters.add(b);
|
||||
parameters.add(limit);
|
||||
return new ApiCall(apiId, RPC.CALL_GET_LIMIT_ORDERS, parameters, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import cy.agorise.graphenej.Asset;
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
public class GetMarketHistory implements ApiCallable {
|
||||
public static final int REQUIRED_API = ApiAccess.API_HISTORY;
|
||||
|
||||
public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
|
||||
|
||||
// API call parameters
|
||||
private Asset base;
|
||||
private Asset quote;
|
||||
private long bucket;
|
||||
private Date start;
|
||||
private Date end;
|
||||
|
||||
/**
|
||||
* Constructor that receives the start and end time as UNIX timestamp in milliseconds.
|
||||
*
|
||||
* @param base Desired asset history
|
||||
* @param quote Asset to which the base price will be compared to
|
||||
* @param bucket The time interval (in seconds) for each point should be (analog to
|
||||
* candles on a candle stick graph).
|
||||
* @param start Timestamp (POSIX) of of the most recent operation to retrieve
|
||||
* (Note: The name can be counter intuitive, but it follow the original
|
||||
* API parameter name)
|
||||
* @param end Timestamp (POSIX) of the the earliest operation to retrieve
|
||||
*/
|
||||
public GetMarketHistory(Asset base, Asset quote, long bucket, long start, long end){
|
||||
this(base, quote, bucket, fromTimestamp(start), fromTimestamp(end));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that receives the start and end time as Date instance objects.
|
||||
*
|
||||
* @param base Desired asset history
|
||||
* @param quote Asset to which the base price will be compared to
|
||||
* @param bucket The time interval (in seconds) for each point should be (analog to
|
||||
* candles on a candle stick graph).
|
||||
* @param start Date and time of of the most recent operation to retrieve
|
||||
* (Note: The name can be counter intuitive, but it follow the original
|
||||
* API parameter name)
|
||||
* @param end Date and time of the the earliest operation to retrieve
|
||||
*/
|
||||
public GetMarketHistory(Asset base, Asset quote, long bucket, Date start, Date end){
|
||||
this.base = base;
|
||||
this.quote = quote;
|
||||
this.bucket = bucket;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method used to convert a timestamp to a Date.
|
||||
*
|
||||
* @param timestamp POSIX timestamp expressed in milliseconds since 1/1/1970
|
||||
* @return Date instance
|
||||
*/
|
||||
private static Date fromTimestamp(long timestamp){
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTimeInMillis(timestamp);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
ArrayList<Serializable> params = new ArrayList<>();
|
||||
params.add(this.base.getObjectId());
|
||||
params.add(this.quote.getObjectId());
|
||||
params.add(this.bucket);
|
||||
params.add(DATE_FORMAT.format(this.start));
|
||||
params.add(DATE_FORMAT.format(this.end));
|
||||
return new ApiCall(apiId, RPC.CALL_GET_MARKET_HISTORY, params, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
/**
|
||||
* Wrapper around the "get_objects" API call.
|
||||
*/
|
||||
public class GetObjects implements ApiCallable {
|
||||
public static final int REQUIRED_API = ApiAccess.API_DATABASE;
|
||||
private List<String> ids;
|
||||
|
||||
public GetObjects(List<String> ids){
|
||||
this.ids = ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
ArrayList<Serializable> params = new ArrayList<>();
|
||||
ArrayList<String> subParams = new ArrayList<>(ids);
|
||||
params.add(subParams);
|
||||
return new ApiCall(apiId, RPC.CALL_GET_OBJECTS, params, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.UserAccount;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
/**
|
||||
* Wrapper around the "get_relative_account_history" API call
|
||||
*/
|
||||
public class GetRelativeAccountHistory implements ApiCallable {
|
||||
|
||||
public static final int REQUIRED_API = ApiAccess.API_HISTORY;
|
||||
|
||||
// API call parameters
|
||||
private UserAccount mUserAccount;
|
||||
private int stop;
|
||||
private int limit;
|
||||
private int start;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param userAccount
|
||||
* @param stop
|
||||
* @param limit
|
||||
* @param start
|
||||
*/
|
||||
public GetRelativeAccountHistory(UserAccount userAccount, int stop, int limit, int start){
|
||||
this.mUserAccount = userAccount;
|
||||
this.stop = stop;
|
||||
this.limit = limit;
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
ArrayList<Serializable> params = new ArrayList<>();
|
||||
params.add(mUserAccount.getObjectId());
|
||||
params.add(this.stop);
|
||||
params.add(this.limit);
|
||||
params.add(this.start);
|
||||
return new ApiCall(apiId, RPC.CALL_GET_RELATIVE_ACCOUNT_HISTORY, params, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import cy.agorise.graphenej.Asset;
|
||||
import cy.agorise.graphenej.BaseOperation;
|
||||
import cy.agorise.graphenej.BlockData;
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.Transaction;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
/**
|
||||
* Wrapper around the "get_required_fees" API call
|
||||
*/
|
||||
|
||||
public class GetRequiredFees implements ApiCallable {
|
||||
|
||||
public static final int REQUIRED_API = ApiAccess.API_DATABASE;
|
||||
|
||||
private Transaction mTransaction;
|
||||
private Asset mFeeAsset;
|
||||
|
||||
public GetRequiredFees(Transaction transaction, Asset feeAsset){
|
||||
this.mTransaction = transaction;
|
||||
this.mFeeAsset = feeAsset;
|
||||
}
|
||||
|
||||
public GetRequiredFees(List<BaseOperation> operations, Asset feeAsset){
|
||||
this.mTransaction = new Transaction(new BlockData(0, 0, 0), operations);
|
||||
this.mFeeAsset = feeAsset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
// Building a new API call to request fees information
|
||||
ArrayList<Serializable> accountParams = new ArrayList<>();
|
||||
accountParams.add((Serializable) mTransaction.getOperations());
|
||||
accountParams.add(this.mFeeAsset.getObjectId());
|
||||
return new ApiCall(apiId, RPC.CALL_GET_REQUIRED_FEES, accountParams, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
public class ListAssets implements ApiCallable {
|
||||
|
||||
public static final int REQUIRED_API = ApiAccess.API_DATABASE;
|
||||
|
||||
/**
|
||||
* Constant that must be used as argument to the constructor of this class to indicate
|
||||
* that the user wants to get all existing assets.
|
||||
*/
|
||||
public static final int LIST_ALL = -1;
|
||||
|
||||
/**
|
||||
* Internal constant used to represent the maximum limit of assets retrieved in one call.
|
||||
*/
|
||||
public static final int MAX_BATCH_SIZE = 100;
|
||||
|
||||
private String lowerBound;
|
||||
private int limit;
|
||||
|
||||
public ListAssets(String lowerBoundSymbol, int limit){
|
||||
this.lowerBound = lowerBoundSymbol;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
ArrayList<Serializable> params = new ArrayList<>();
|
||||
params.add(this.lowerBound);
|
||||
if(limit > MAX_BATCH_SIZE || limit == LIST_ALL){
|
||||
params.add(MAX_BATCH_SIZE);
|
||||
}else{
|
||||
params.add(this.limit);
|
||||
}
|
||||
return new ApiCall(apiId, RPC.CALL_LIST_ASSETS, params, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import cy.agorise.graphenej.Asset;
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
public class LookupAssetSymbols implements ApiCallable {
|
||||
public static final int REQUIRED_API = ApiAccess.API_NONE;
|
||||
|
||||
private List<Asset> mAssetList;
|
||||
|
||||
public LookupAssetSymbols(List<Asset> assetList){
|
||||
this.mAssetList = assetList;
|
||||
}
|
||||
|
||||
public LookupAssetSymbols(Asset asset){
|
||||
mAssetList = new ArrayList<Asset>();
|
||||
mAssetList.add(asset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
ArrayList<Serializable> params = new ArrayList<>();
|
||||
ArrayList<String> subArray = new ArrayList<>();
|
||||
for(int i = 0; i < mAssetList.size(); i++){
|
||||
Asset asset = mAssetList.get(i);
|
||||
subArray.add(asset.getObjectId());
|
||||
params.add(subArray);
|
||||
}
|
||||
return new ApiCall(apiId, RPC.CALL_LOOKUP_ASSET_SYMBOLS, params, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
public class SetSubscribeCallback implements ApiCallable {
|
||||
public static final int REQUIRED_API = ApiAccess.API_DATABASE;
|
||||
|
||||
private boolean clearFilter;
|
||||
|
||||
public SetSubscribeCallback(boolean clearFilter){
|
||||
this.clearFilter = clearFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCall toApiCall(int apiId, long sequenceId) {
|
||||
ArrayList<Serializable> subscriptionParams = new ArrayList<>();
|
||||
subscriptionParams.add(new Long(sequenceId));
|
||||
subscriptionParams.add(clearFilter);
|
||||
return new ApiCall(apiId, RPC.CALL_SET_SUBSCRIBE_CALLBACK, subscriptionParams, RPC.VERSION, sequenceId);
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ import cy.agorise.graphenej.interfaces.JsonSerializable;
|
|||
* @see <a href="http://docs.bitshares.org/api/websocket.html">Websocket Calls & Notifications</a>
|
||||
*/
|
||||
public class ApiCall implements JsonSerializable {
|
||||
|
||||
public static final String KEY_SEQUENCE_ID = "id";
|
||||
public static final String KEY_METHOD = "method";
|
||||
public static final String KEY_PARAMS = "params";
|
||||
|
@ -65,7 +66,7 @@ public class ApiCall implements JsonSerializable {
|
|||
paramsArray.add(this.apiId);
|
||||
paramsArray.add(this.methodToCall);
|
||||
JsonArray methodParams = new JsonArray();
|
||||
|
||||
if(this.params != null){
|
||||
for(int i = 0; i < this.params.size(); i++){
|
||||
if(this.params.get(i) instanceof JsonSerializable) {
|
||||
// Sometimes the parameters are objects
|
||||
|
@ -86,6 +87,10 @@ public class ApiCall implements JsonSerializable {
|
|||
array.add(((JsonSerializable) element).toJsonObject());
|
||||
else if (element instanceof String) {
|
||||
array.add((String) element);
|
||||
}else if (element instanceof Long){
|
||||
array.add((Long) element);
|
||||
}else if(element instanceof Integer){
|
||||
array.add((Integer) element);
|
||||
}
|
||||
}
|
||||
methodParams.add(array);
|
||||
|
@ -95,6 +100,7 @@ public class ApiCall implements JsonSerializable {
|
|||
System.out.println("Skipping parameter of type: "+this.params.get(i).getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
paramsArray.add(methodParams);
|
||||
obj.add(KEY_PARAMS, paramsArray);
|
||||
obj.addProperty(KEY_JSON_RPC, this.jsonrpc);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package cy.agorise.graphenej.models;
|
||||
|
||||
/**
|
||||
* Created by nelson on 11/12/16.
|
||||
* Base response class
|
||||
* @deprecated Use {@link JsonRpcResponse} instead
|
||||
*/
|
||||
public class BaseResponse {
|
||||
public long id;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
package cy.agorise.graphenej.models;
|
||||
|
||||
/**
|
||||
* Created by nelson on 12/13/16.
|
||||
* Class used to represent the response to the 'get_block_header' API call.
|
||||
*/
|
||||
public class BlockHeader {
|
||||
public String previous;
|
||||
public String timestamp;
|
||||
public String witness;
|
||||
public String transaction_merkle_root;
|
||||
public Object[] extension;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
package cy.agorise.graphenej.models;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Model class used in the de-serialization of the response to the 'get_full_accounts' API call.
|
||||
* @see cy.agorise.graphenej.api.calls.GetFullAccounts
|
||||
*/
|
||||
public class FullAccountDetails {
|
||||
private AccountProperties account;
|
||||
private Statistics statistics;
|
||||
|
||||
public FullAccountDetails(AccountProperties properties, Statistics statistics){
|
||||
this.account = properties;
|
||||
this.statistics = statistics;
|
||||
}
|
||||
|
||||
public AccountProperties getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
public void setAccount(AccountProperties account) {
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
public Statistics getStatistics() {
|
||||
return statistics;
|
||||
}
|
||||
|
||||
public void setStatistics(Statistics statistics) {
|
||||
this.statistics = statistics;
|
||||
}
|
||||
|
||||
public static class Statistics {
|
||||
public String id;
|
||||
public String owner;
|
||||
public String name;
|
||||
public String most_recent_op;
|
||||
public long total_ops;
|
||||
public long removed_ops;
|
||||
public long total_core_in_orders;
|
||||
public String core_in_balance;
|
||||
public boolean has_cashback_vb;
|
||||
public boolean is_voting;
|
||||
public long lifetime_fees_paid;
|
||||
public long pending_fees;
|
||||
public long pending_vested_fees;
|
||||
}
|
||||
|
||||
public static class FullAccountDeserializer implements JsonDeserializer<FullAccountDetails> {
|
||||
|
||||
@Override
|
||||
public FullAccountDetails deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonArray array = (JsonArray) json;
|
||||
JsonObject jsonObject = (JsonObject) array.get(1);
|
||||
AccountProperties properties = context.deserialize(jsonObject.get("account"), AccountProperties.class);
|
||||
Statistics statistics = context.deserialize(jsonObject.get("statistics"), Statistics.class);
|
||||
return new FullAccountDetails(properties, statistics);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package cy.agorise.graphenej.models;
|
||||
|
||||
import cy.agorise.graphenej.operations.TransferOperation;
|
||||
|
||||
|
||||
/**
|
||||
* This class offers support to deserialization of transfer operations received by the API
|
||||
* method get_relative_account_history.
|
||||
*
|
||||
* More operations types might be listed in the response of that method, but by using this class
|
||||
* those will be filtered out of the parsed result.
|
||||
*/
|
||||
public class HistoricalTransfer {
|
||||
private String id;
|
||||
private TransferOperation op;
|
||||
public Object[] result;
|
||||
private long block_num;
|
||||
private long trx_in_block;
|
||||
private long op_in_trx;
|
||||
private long virtual_op;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public TransferOperation getOperation() {
|
||||
return op;
|
||||
}
|
||||
|
||||
public void setOperation(TransferOperation op) {
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
public long getBlockNum() {
|
||||
return block_num;
|
||||
}
|
||||
|
||||
public void setBlockNum(long block_num) {
|
||||
this.block_num = block_num;
|
||||
}
|
||||
|
||||
public long getTransactionsInBlock() {
|
||||
return trx_in_block;
|
||||
}
|
||||
|
||||
public void setTransactionsInBlock(long trx_in_block) {
|
||||
this.trx_in_block = trx_in_block;
|
||||
}
|
||||
|
||||
public long getOperationsInTrx() {
|
||||
return op_in_trx;
|
||||
}
|
||||
|
||||
public void setOperationsInTrx(long op_in_trx) {
|
||||
this.op_in_trx = op_in_trx;
|
||||
}
|
||||
|
||||
public long getVirtualOp() {
|
||||
return virtual_op;
|
||||
}
|
||||
|
||||
public void setVirtualOp(long virtual_op) {
|
||||
this.virtual_op = virtual_op;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package cy.agorise.graphenej.models;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Model class used to represent the struct defined in graphene::app::history_operation_detail and
|
||||
* returned as response to the 'get_account_history_by_operations' API call.
|
||||
*/
|
||||
public class HistoryOperationDetail {
|
||||
private long total_count;
|
||||
List<OperationHistory> operation_history_objs;
|
||||
|
||||
public long getTotalCount() {
|
||||
return total_count;
|
||||
}
|
||||
|
||||
public void setTotalCount(long total_count) {
|
||||
this.total_count = total_count;
|
||||
}
|
||||
|
||||
public List<OperationHistory> getOperationHistoryObjs() {
|
||||
return operation_history_objs;
|
||||
}
|
||||
|
||||
public void setOperationHistoryObjs(List<OperationHistory> operation_history_objs) {
|
||||
this.operation_history_objs = operation_history_objs;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package cy.agorise.graphenej.models;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import cy.agorise.graphenej.GrapheneObject;
|
||||
import cy.agorise.graphenej.ObjectType;
|
||||
import cy.agorise.graphenej.OperationType;
|
||||
import cy.agorise.graphenej.Transaction;
|
||||
|
||||
/**
|
||||
* Class that represents a generic subscription notification.
|
||||
* The template for every subscription response is the following:
|
||||
*
|
||||
* {
|
||||
* "method": "notice"
|
||||
* "params": [
|
||||
* SUBSCRIPTION_ID,
|
||||
* [[
|
||||
* { "id": "2.1.0", ... },
|
||||
* { "id": ... },
|
||||
* { "id": ... },
|
||||
* { "id": ... }
|
||||
* ]]
|
||||
* ],
|
||||
* }
|
||||
*/
|
||||
public class JsonRpcNotification {
|
||||
public static final String KEY_METHOD = "method";
|
||||
public static final String KEY_PARAMS = "params";
|
||||
|
||||
public String method;
|
||||
public List<Serializable> params;
|
||||
|
||||
/**
|
||||
* Inner static class used to parse and deserialize subscription notifications.
|
||||
*/
|
||||
public static class JsonRpcNotificationDeserializer implements JsonDeserializer<JsonRpcNotification> {
|
||||
|
||||
@Override
|
||||
public JsonRpcNotification deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonRpcNotification notification = new JsonRpcNotification();
|
||||
JsonObject responseObject = json.getAsJsonObject();
|
||||
if(!responseObject.has(KEY_METHOD)){
|
||||
return notification;
|
||||
}
|
||||
notification.method = responseObject.get(KEY_METHOD).getAsString();
|
||||
|
||||
JsonArray paramsArray = responseObject.get(KEY_PARAMS).getAsJsonArray();
|
||||
notification.params = new ArrayList<>();
|
||||
notification.params.add(paramsArray.get(0).getAsInt());
|
||||
ArrayList<Serializable> secondArgument = new ArrayList<>();
|
||||
notification.params.add(secondArgument);
|
||||
|
||||
JsonArray subArray = paramsArray.get(1).getAsJsonArray().get(0).getAsJsonArray();
|
||||
for(JsonElement object : subArray){
|
||||
if(object.isJsonObject()){
|
||||
GrapheneObject grapheneObject = new GrapheneObject(object.getAsJsonObject().get(GrapheneObject.KEY_ID).getAsString());
|
||||
|
||||
JsonObject jsonObject = object.getAsJsonObject();
|
||||
if(grapheneObject.getObjectType() == ObjectType.ACCOUNT_BALANCE_OBJECT){
|
||||
AccountBalanceUpdate balanceObject = new AccountBalanceUpdate(grapheneObject.getObjectId());
|
||||
balanceObject.owner = jsonObject.get(AccountBalanceUpdate.KEY_OWNER).getAsString();
|
||||
balanceObject.asset_type = jsonObject.get(AccountBalanceUpdate.KEY_ASSET_TYPE).getAsString();
|
||||
balanceObject.balance = jsonObject.get(AccountBalanceUpdate.KEY_BALANCE).getAsLong();
|
||||
secondArgument.add(balanceObject);
|
||||
}else if(grapheneObject.getObjectType() == ObjectType.DYNAMIC_GLOBAL_PROPERTY_OBJECT){
|
||||
DynamicGlobalProperties dynamicGlobalProperties = context.deserialize(object, DynamicGlobalProperties.class);
|
||||
secondArgument.add(dynamicGlobalProperties);
|
||||
}else if(grapheneObject.getObjectType() == ObjectType.TRANSACTION_OBJECT){
|
||||
BroadcastedTransaction broadcastedTransaction = new BroadcastedTransaction(grapheneObject.getObjectId());
|
||||
broadcastedTransaction.setTransaction((Transaction) context.deserialize(jsonObject.get(BroadcastedTransaction.KEY_TRX), Transaction.class));
|
||||
broadcastedTransaction.setTransactionId(jsonObject.get(BroadcastedTransaction.KEY_TRX_ID).getAsString());
|
||||
secondArgument.add(broadcastedTransaction);
|
||||
}else if(grapheneObject.getObjectType() == ObjectType.OPERATION_HISTORY_OBJECT){
|
||||
if(jsonObject.get(OperationHistory.KEY_OP).getAsJsonArray().get(0).getAsLong() == OperationType.TRANSFER_OPERATION.ordinal()){
|
||||
OperationHistory operationHistory = context.deserialize(jsonObject, OperationHistory.class);
|
||||
secondArgument.add(operationHistory);
|
||||
}else{
|
||||
//TODO: Add support for other operations
|
||||
}
|
||||
}else{
|
||||
//TODO: Add support for other types of objects
|
||||
}
|
||||
}else{
|
||||
secondArgument.add(object.getAsString());
|
||||
}
|
||||
}
|
||||
return notification;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package cy.agorise.graphenej.models;
|
||||
|
||||
/**
|
||||
* Used to represent a JSON-RPC response object
|
||||
*/
|
||||
|
||||
public class JsonRpcResponse<T> {
|
||||
public long id;
|
||||
public Error error;
|
||||
public T result;
|
||||
|
||||
public static class Error {
|
||||
public ErrorData data;
|
||||
public int code;
|
||||
public String message;
|
||||
public Error(String message){
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ErrorData {
|
||||
public int code;
|
||||
public String name;
|
||||
public String message;
|
||||
//TODO: Include stack data
|
||||
|
||||
public ErrorData(String message){
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
package cy.agorise.graphenej.models;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import cy.agorise.graphenej.BaseOperation;
|
||||
import cy.agorise.graphenej.GrapheneObject;
|
||||
|
||||
|
||||
/**
|
||||
* This class offers support to deserialization of transfer operations received by the API
|
||||
* method get_relative_account_history.
|
||||
*
|
||||
* More operations types might be listed in the response of that method, but by using this class
|
||||
* those will be filtered out of the parsed result.
|
||||
*/
|
||||
public class OperationHistory extends GrapheneObject implements Serializable {
|
||||
public static final String KEY_OP = "op";
|
||||
public static final String KEY_BLOCK_NUM = "block_num";
|
||||
public static final String KEY_TRX_IN_BLOCK = "trx_in_block";
|
||||
public static final String KEY_OP_IN_TRX = "op_in_trx";
|
||||
public static final String KEY_VIRTUAL_OP = "virtual_op";
|
||||
|
||||
private BaseOperation op;
|
||||
public Object[] result;
|
||||
private long block_num;
|
||||
private long trx_in_block;
|
||||
private long op_in_trx;
|
||||
private long virtual_op;
|
||||
|
||||
public OperationHistory(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
public BaseOperation getOperation() {
|
||||
return op;
|
||||
}
|
||||
|
||||
public void setOperation(BaseOperation op) {
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
public long getBlockNum() {
|
||||
return block_num;
|
||||
}
|
||||
|
||||
public void setBlockNum(long block_num) {
|
||||
this.block_num = block_num;
|
||||
}
|
||||
|
||||
public long getTransactionsInBlock() {
|
||||
return trx_in_block;
|
||||
}
|
||||
|
||||
public void setTransactionsInBlock(long trx_in_block) {
|
||||
this.trx_in_block = trx_in_block;
|
||||
}
|
||||
|
||||
public long getOperationsInTrx() {
|
||||
return op_in_trx;
|
||||
}
|
||||
|
||||
public void setOperationsInTrx(long op_in_trx) {
|
||||
this.op_in_trx = op_in_trx;
|
||||
}
|
||||
|
||||
public long getVirtualOp() {
|
||||
return virtual_op;
|
||||
}
|
||||
|
||||
public void setVirtualOp(long virtual_op) {
|
||||
this.virtual_op = virtual_op;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializer used to transform a an operation history object from its serialized form to an
|
||||
* OperationHistory instance.
|
||||
*
|
||||
* The serialized form of this object is the following:
|
||||
*
|
||||
* {
|
||||
"id": "1.11.178205535",
|
||||
"op": [
|
||||
14,
|
||||
{
|
||||
"fee": {
|
||||
"amount": 10425,
|
||||
"asset_id": "1.3.0"
|
||||
},
|
||||
"issuer": "1.2.374566",
|
||||
"asset_to_issue": {
|
||||
"amount": 8387660,
|
||||
"asset_id": "1.3.3271"
|
||||
},
|
||||
"issue_to_account": "1.2.797835",
|
||||
"extensions": []
|
||||
}
|
||||
],
|
||||
"result": [
|
||||
0,
|
||||
{}
|
||||
],
|
||||
"block_num": 26473240,
|
||||
"trx_in_block": 11,
|
||||
"op_in_trx": 0,
|
||||
"virtual_op": 660
|
||||
}
|
||||
* //TODO: Expand this deserializer for operation history objects that have an operation other than the transfer operation
|
||||
*/
|
||||
public static class OperationHistoryDeserializer implements JsonDeserializer<OperationHistory> {
|
||||
|
||||
@Override
|
||||
public OperationHistory deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
String id = jsonObject.get(KEY_ID).getAsString();
|
||||
long blockNum = jsonObject.get(KEY_BLOCK_NUM).getAsLong();
|
||||
long trxInBlock = jsonObject.get(KEY_TRX_IN_BLOCK).getAsLong();
|
||||
long opInTrx = jsonObject.get(KEY_OP_IN_TRX).getAsLong();
|
||||
BaseOperation operation = context.deserialize(jsonObject.get(KEY_OP), BaseOperation.class);
|
||||
long virtualOp = jsonObject.get(KEY_VIRTUAL_OP).getAsLong();
|
||||
OperationHistory operationHistory = new OperationHistory(id);
|
||||
operationHistory.setBlockNum(blockNum);
|
||||
operationHistory.setTransactionsInBlock(trxInBlock);
|
||||
operationHistory.setOperationsInTrx(opInTrx);
|
||||
operationHistory.setOperation(operation);
|
||||
operationHistory.setVirtualOp(virtualOp);
|
||||
return operationHistory;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ import java.util.List;
|
|||
|
||||
import cy.agorise.graphenej.GrapheneObject;
|
||||
import cy.agorise.graphenej.ObjectType;
|
||||
import cy.agorise.graphenej.OperationType;
|
||||
import cy.agorise.graphenej.Transaction;
|
||||
import cy.agorise.graphenej.interfaces.SubscriptionListener;
|
||||
|
||||
|
@ -43,15 +44,12 @@ import cy.agorise.graphenej.interfaces.SubscriptionListener;
|
|||
* To minimize CPU usage, we introduce a scheme of selective parsing, implemented by the static inner class
|
||||
* SubscriptionResponseDeserializer.
|
||||
*
|
||||
* Created by nelson on 1/12/17.
|
||||
*/
|
||||
public class SubscriptionResponse {
|
||||
private static final String TAG = "SubscriptionResponse";
|
||||
public static final String KEY_ID = "id";
|
||||
public static final String KEY_METHOD = "method";
|
||||
public static final String KEY_PARAMS = "params";
|
||||
|
||||
public int id;
|
||||
public String method;
|
||||
public List<Serializable> params;
|
||||
|
||||
|
@ -182,6 +180,14 @@ public class SubscriptionResponse {
|
|||
broadcastedTransaction.setTransactionId(jsonObject.get(BroadcastedTransaction.KEY_TRX_ID).getAsString());
|
||||
objectMap.put(ObjectType.TRANSACTION_OBJECT, true);
|
||||
secondArgument.add(broadcastedTransaction);
|
||||
}else if(grapheneObject.getObjectType() == ObjectType.OPERATION_HISTORY_OBJECT){
|
||||
if(jsonObject.get(OperationHistory.KEY_OP).getAsJsonArray().get(0).getAsLong() == OperationType.TRANSFER_OPERATION.ordinal()){
|
||||
OperationHistory operationHistory = context.deserialize(jsonObject, OperationHistory.class);
|
||||
objectMap.put(ObjectType.OPERATION_HISTORY_OBJECT, true);
|
||||
secondArgument.add(operationHistory);
|
||||
}else{
|
||||
//TODO: Add support for other operations
|
||||
}
|
||||
}else{
|
||||
//TODO: Add support for other types of objects
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package cy.agorise.graphenej.models;
|
|||
|
||||
/**
|
||||
* Generic witness response
|
||||
* @deprecated Use {@link JsonRpcResponse} instead
|
||||
*/
|
||||
public class WitnessResponse<T> extends BaseResponse{
|
||||
public static final String KEY_ID = "id";
|
||||
|
|
|
@ -17,7 +17,7 @@ import cy.agorise.graphenej.AssetAmount;
|
|||
import cy.agorise.graphenej.BaseOperation;
|
||||
import cy.agorise.graphenej.OperationType;
|
||||
import cy.agorise.graphenej.UserAccount;
|
||||
import cy.agorise.graphenej.objects.Memo;
|
||||
import cy.agorise.graphenej.Memo;
|
||||
|
||||
/**
|
||||
* Class used to encapsulate the TransferOperation operation related functionalities.
|
||||
|
|
|
@ -3,7 +3,7 @@ package cy.agorise.graphenej.operations;
|
|||
import cy.agorise.graphenej.AssetAmount;
|
||||
import cy.agorise.graphenej.UserAccount;
|
||||
import cy.agorise.graphenej.errors.MalformedOperationException;
|
||||
import cy.agorise.graphenej.objects.Memo;
|
||||
import cy.agorise.graphenej.Memo;
|
||||
|
||||
/**
|
||||
* Factory class used to build a transfer operation
|
||||
|
|
|
@ -10,6 +10,9 @@ import org.junit.Test;
|
|||
*/
|
||||
public class BrainKeyTest {
|
||||
public final String TEST_BRAINKEY = "BARIC BICKERN LITZ TIPFUL JINGLED POOL TUMBAK PURIST APOPYLE DURAIN SATLIJK FAUCAL";
|
||||
|
||||
public final String TEST_BRAINKEY_OPENLEDGER = "ona refan abscise neebor battik terbia bandit sundra gasser debar phytol frat hauler accede primy garland";
|
||||
|
||||
private BrainKey mBrainKey;
|
||||
|
||||
@Before
|
||||
|
@ -17,6 +20,9 @@ public class BrainKeyTest {
|
|||
mBrainKey = new BrainKey(TEST_BRAINKEY, BrainKey.DEFAULT_SEQUENCE_NUMBER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test making sure that a simple brainkey can successfully generate the expected public address
|
||||
*/
|
||||
@Test
|
||||
public void testAddress(){
|
||||
Address address = mBrainKey.getPublicAddress(Address.BITSHARES_PREFIX);
|
||||
|
@ -24,4 +30,25 @@ public class BrainKeyTest {
|
|||
"BTS61UqqgE3ARuTGcckzARsdQm4EMFdBEwYyi1pbwyHrZZWrCDhT2",
|
||||
address.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test making sure that a OpenLedger's brainkey can successfully generate the given
|
||||
* 'owner' and 'active' keys.
|
||||
*/
|
||||
@Test
|
||||
public void testOpenledgerAddress(){
|
||||
BrainKey brainKey1 = new BrainKey(TEST_BRAINKEY_OPENLEDGER, 0);
|
||||
BrainKey brainKey2 = new BrainKey(TEST_BRAINKEY_OPENLEDGER, 1);
|
||||
|
||||
Address ownerAddress = brainKey1.getPublicAddress(Address.BITSHARES_PREFIX);
|
||||
Address activeAddress = brainKey2.getPublicAddress(Address.BITSHARES_PREFIX);
|
||||
|
||||
Assert.assertEquals("Owner address matches",
|
||||
"BTS6dqT3J7tUcZP6xHo2mHkL8tq8zw5TQgGd6ntRMXH1EoNsCWTzm",
|
||||
ownerAddress.toString());
|
||||
|
||||
Assert.assertEquals("Active address matches",
|
||||
"BTS6DKvgY3yPyN7wKrhBGYhrnghhLSVCYz3ugUdi9pDPkicS6B7N2",
|
||||
activeAddress.toString());
|
||||
}
|
||||
}
|
|
@ -24,7 +24,6 @@ import cy.agorise.graphenej.api.TransactionBroadcastSequence;
|
|||
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
|
||||
import cy.agorise.graphenej.models.BaseResponse;
|
||||
import cy.agorise.graphenej.models.WitnessResponse;
|
||||
import cy.agorise.graphenej.objects.Memo;
|
||||
import cy.agorise.graphenej.operations.CustomOperation;
|
||||
import cy.agorise.graphenej.operations.LimitOrderCancelOperation;
|
||||
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
package cy.agorise.graphenej.api;
|
||||
|
||||
import com.neovisionaries.ws.client.WebSocketException;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import cy.agorise.graphenej.UserAccount;
|
||||
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
|
||||
import cy.agorise.graphenej.models.AccountProperties;
|
||||
import cy.agorise.graphenej.models.BaseResponse;
|
||||
import cy.agorise.graphenej.models.WitnessResponse;
|
||||
|
||||
public class GetAccountsTest extends BaseApiTest {
|
||||
private UserAccount ltmAccount = new UserAccount("1.2.99700");
|
||||
private UserAccount nonLtmAccount = new UserAccount("1.2.140994");
|
||||
|
||||
@Test
|
||||
public void testGetAccount(){
|
||||
ArrayList<UserAccount> userAccounts = new ArrayList<>();
|
||||
userAccounts.add(ltmAccount);
|
||||
userAccounts.add(nonLtmAccount);
|
||||
mWebSocket.addListener(new GetAccounts(userAccounts, true, new WitnessResponseListener(){
|
||||
|
||||
@Override
|
||||
public void onSuccess(WitnessResponse response) {
|
||||
System.out.println("onSuccess.");
|
||||
List<AccountProperties> accounts = (List<AccountProperties>) response.result;
|
||||
System.out.println(String.format("Got %d accounts", accounts.size()));
|
||||
for(AccountProperties accountProperties : accounts){
|
||||
System.out.println("account name....: "+accountProperties.name);
|
||||
System.out.println("expiration date.: "+accountProperties.membership_expiration_date);
|
||||
}
|
||||
AccountProperties ltmAccountProperties = accounts.get(0);
|
||||
AccountProperties nonLtmAccountProperties = accounts.get(1);
|
||||
Assert.assertEquals(ltmAccountProperties.membership_expiration_date, UserAccount.LIFETIME_EXPIRATION_DATE);
|
||||
Assert.assertFalse(nonLtmAccountProperties.membership_expiration_date.equals(UserAccount.LIFETIME_EXPIRATION_DATE));
|
||||
synchronized (GetAccountsTest.this){
|
||||
GetAccountsTest.this.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(BaseResponse.Error error) {
|
||||
System.out.println("onError. Msg: "+error.message);
|
||||
synchronized (GetAccountsTest.this){
|
||||
GetAccountsTest.this.notifyAll();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
try{
|
||||
mWebSocket.connect();
|
||||
synchronized (this){
|
||||
wait();
|
||||
}
|
||||
}catch (WebSocketException e) {
|
||||
System.out.println("WebSocketException. Msg: " + e.getMessage());
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println("InterruptedException. Msg: "+e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ public class GetObjectsTest extends BaseApiTest{
|
|||
private final Asset asset = new Asset("1.3.0", "BTS", 5);
|
||||
private final UserAccount account = new UserAccount("1.2.116354");
|
||||
private final UserAccount bilthon_25 = new UserAccount("1.2.151069");
|
||||
private UserAccount ltmAccount = new UserAccount("1.2.99700");
|
||||
private final String[] bitAssetIds = new String[]{"2.4.21", "2.4.83"};
|
||||
|
||||
@Test
|
||||
|
@ -109,6 +110,50 @@ public class GetObjectsTest extends BaseApiTest{
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLtmAccount(){
|
||||
ArrayList<String> ids = new ArrayList<>();
|
||||
ids.add(ltmAccount.getObjectId());
|
||||
mWebSocket.addListener(new GetObjects(ids, new WitnessResponseListener() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(WitnessResponse response) {
|
||||
System.out.println("onSuccess");
|
||||
List<GrapheneObject> result = (List<GrapheneObject>) response.result;
|
||||
UserAccount userAccount = (UserAccount) result.get(0);
|
||||
System.out.println("Account name.....: "+userAccount.getName());
|
||||
System.out.println("Is LTM...........: "+userAccount.isLifeTime());
|
||||
System.out.println("json string......: "+userAccount.toJsonString());
|
||||
System.out.println("owner............: "+userAccount.getOwner().getKeyAuthList().get(0).getAddress());
|
||||
System.out.println("active key.......: "+userAccount.getActive().getKeyAuthList().get(0).getAddress());
|
||||
System.out.println("memo: "+userAccount.getOptions().getMemoKey().getAddress());
|
||||
Assert.assertEquals("We expect this account to be LTM",true, userAccount.isLifeTime());
|
||||
synchronized (GetObjectsTest.this){
|
||||
GetObjectsTest.this.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(BaseResponse.Error error) {
|
||||
System.out.println("onError");
|
||||
synchronized (GetObjectsTest.this){
|
||||
GetObjectsTest.this.notifyAll();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
try {
|
||||
mWebSocket.connect();
|
||||
synchronized (this){
|
||||
wait();
|
||||
}
|
||||
}catch (WebSocketException e) {
|
||||
System.out.println("WebSocketException. Msg: " + e.getMessage());
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println("InterruptedException. Msg: "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitAssetData(){
|
||||
try{
|
||||
|
|
|
@ -8,7 +8,7 @@ import java.util.List;
|
|||
import cy.agorise.graphenej.UserAccount;
|
||||
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
|
||||
import cy.agorise.graphenej.models.BaseResponse;
|
||||
import cy.agorise.graphenej.models.HistoricalTransfer;
|
||||
import cy.agorise.graphenej.models.OperationHistory;
|
||||
import cy.agorise.graphenej.models.WitnessResponse;
|
||||
import cy.agorise.graphenej.operations.TransferOperation;
|
||||
|
||||
|
@ -51,11 +51,11 @@ public class GetRelativeAccountHistoryTest extends BaseApiTest {
|
|||
public void onSuccess(WitnessResponse response) {
|
||||
System.out.println("mTransferHistoryListener.onSuccess");
|
||||
historicalTransferCount++;
|
||||
WitnessResponse<List<HistoricalTransfer>> resp = response;
|
||||
for(HistoricalTransfer historicalTransfer : resp.result){
|
||||
WitnessResponse<List<OperationHistory>> resp = response;
|
||||
for(OperationHistory historicalTransfer : resp.result){
|
||||
if(historicalTransfer.getOperation() != null){
|
||||
System.out.println("Got transfer operation!");
|
||||
TransferOperation transferOperation = historicalTransfer.getOperation();
|
||||
TransferOperation transferOperation = (TransferOperation) historicalTransfer.getOperation();
|
||||
System.out.println(String.format("%s - > %s, memo: %s",
|
||||
transferOperation.getFrom().getObjectId(),
|
||||
transferOperation.getTo().getObjectId(),
|
||||
|
|
|
@ -10,13 +10,14 @@ import java.util.Timer;
|
|||
import java.util.TimerTask;
|
||||
|
||||
import cy.agorise.graphenej.ObjectType;
|
||||
import cy.agorise.graphenej.Transaction;
|
||||
import cy.agorise.graphenej.interfaces.NodeErrorListener;
|
||||
import cy.agorise.graphenej.interfaces.SubscriptionListener;
|
||||
import cy.agorise.graphenej.models.BaseResponse;
|
||||
import cy.agorise.graphenej.models.BroadcastedTransaction;
|
||||
import cy.agorise.graphenej.models.DynamicGlobalProperties;
|
||||
import cy.agorise.graphenej.models.OperationHistory;
|
||||
import cy.agorise.graphenej.models.SubscriptionResponse;
|
||||
import cy.agorise.graphenej.Transaction;
|
||||
|
||||
/**
|
||||
* Class used to encapsulate all tests that relate to the {@see SubscriptionMessagesHub} class.
|
||||
|
@ -178,7 +179,7 @@ public class SubscriptionMessagesHubTest extends BaseApiTest {
|
|||
@Test
|
||||
public void testBroadcastedTransactionDeserializer(){
|
||||
try{
|
||||
mMessagesHub = new SubscriptionMessagesHub("", "", mErrorListener);
|
||||
mMessagesHub = new SubscriptionMessagesHub("", "", true, mErrorListener);
|
||||
mMessagesHub.addSubscriptionListener(new SubscriptionListener() {
|
||||
private int MAX_MESSAGES = 15;
|
||||
private int messageCounter = 0;
|
||||
|
@ -197,7 +198,7 @@ public class SubscriptionMessagesHubTest extends BaseApiTest {
|
|||
if(item instanceof BroadcastedTransaction){
|
||||
BroadcastedTransaction broadcastedTransaction = (BroadcastedTransaction) item;
|
||||
Transaction tx = broadcastedTransaction.getTransaction();
|
||||
System.out.println(String.format("Got %d operations", tx.getOperations().size()));
|
||||
// System.out.println(String.format("Got %d operations", tx.getOperations().size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -213,6 +214,30 @@ public class SubscriptionMessagesHubTest extends BaseApiTest {
|
|||
}
|
||||
});
|
||||
|
||||
mMessagesHub.addSubscriptionListener(new SubscriptionListener() {
|
||||
|
||||
@Override
|
||||
public ObjectType getInterestObjectType() {
|
||||
return ObjectType.OPERATION_HISTORY_OBJECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscriptionUpdate(SubscriptionResponse response) {
|
||||
System.out.println("onSubscriptionUpdate. response.params.size: "+response.params.size());
|
||||
if(response.params.size() == 2){
|
||||
List<Serializable> payload = (List) response.params.get(1);
|
||||
if(payload.size() > 0){
|
||||
for(Serializable item : payload){
|
||||
if(item instanceof OperationHistory){
|
||||
OperationHistory operationHistory = (OperationHistory) item;
|
||||
System.out.println("Operation history: <id:"+operationHistory.getObjectId()+", op: "+operationHistory.getOperation().toJsonString()+">");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mWebSocket.addListener(mMessagesHub);
|
||||
mWebSocket.connect();
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package cy.agorise.graphenej.api.calls;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import cy.agorise.graphenej.UserAccount;
|
||||
import cy.agorise.graphenej.models.ApiCall;
|
||||
|
||||
public class GetAccountHistoryTest {
|
||||
|
||||
@Test
|
||||
public void testSerialization(){
|
||||
UserAccount userAccount = new UserAccount("1.2.139293");
|
||||
String end = "1.11.225030218";
|
||||
String start = "1.11.225487973";
|
||||
int limit = 20;
|
||||
GetAccountHistory getAccountHistory = new GetAccountHistory(userAccount, start, end, limit);
|
||||
ApiCall apiCall = getAccountHistory.toApiCall(2, 3);
|
||||
String serialized = apiCall.toJsonString();
|
||||
System.out.println("> "+serialized);
|
||||
String expected = "{\"id\":3,\"method\":\"call\",\"params\":[2,\"get_account_history\",[\"1.2.139293\",\"1.11.225030218\",20,\"1.11.225487973\"]],\"jsonrpc\":\"2.0\"}";
|
||||
Assert.assertEquals("Serialized is as expected", expected, serialized);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package cy.agorise.graphenej.models;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
import cy.agorise.graphenej.AccountOptions;
|
||||
import cy.agorise.graphenej.Authority;
|
||||
import cy.agorise.graphenej.Memo;
|
||||
|
||||
public class FullAccountDetailsTest {
|
||||
|
||||
@Test
|
||||
public void testDeserialization(){
|
||||
String serialized = "{\"id\":0,\"jsonrpc\":\"2.0\",\"result\":[[\"bilthon-1\",{\"account\":{\"id\":\"1.2.139205\",\"membership_expiration_date\":\"1970-01-01T00:00:00\",\"registrar\":\"1.2.117600\",\"referrer\":\"1.2.90200\",\"lifetime_referrer\":\"1.2.90200\",\"network_fee_percentage\":2000,\"lifetime_referrer_fee_percentage\":3000,\"referrer_rewards_percentage\":9000,\"name\":\"bilthon-1\",\"owner\":{\"weight_threshold\":1,\"account_auths\":[],\"key_auths\":[[\"BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY\",1]],\"address_auths\":[]},\"active\":{\"weight_threshold\":1,\"account_auths\":[],\"key_auths\":[[\"BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY\",1]],\"address_auths\":[]},\"options\":{\"memo_key\":\"BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY\",\"voting_account\":\"1.2.5\",\"num_witness\":0,\"num_committee\":0,\"votes\":[],\"extensions\":[]},\"statistics\":\"2.6.139205\",\"whitelisting_accounts\":[],\"blacklisting_accounts\":[],\"whitelisted_accounts\":[],\"blacklisted_accounts\":[],\"owner_special_authority\":[0,{}],\"active_special_authority\":[0,{}],\"top_n_control_flags\":0},\"statistics\":{\"id\":\"2.6.139205\",\"owner\":\"1.2.139205\",\"name\":\"bilthon-1\",\"most_recent_op\":\"2.9.6668024\",\"total_ops\":3,\"removed_ops\":0,\"total_core_in_orders\":0,\"core_in_balance\":71279,\"has_cashback_vb\":false,\"is_voting\":false,\"lifetime_fees_paid\":28721,\"pending_fees\":0,\"pending_vested_fees\":0},\"registrar_name\":\"bitshares-munich-faucet\",\"referrer_name\":\"bitshares-munich\",\"lifetime_referrer_name\":\"bitshares-munich\",\"votes\":[],\"balances\":[{\"id\":\"2.5.44951\",\"owner\":\"1.2.139205\",\"asset_type\":\"1.3.0\",\"balance\":71279,\"maintenance_flag\":false}],\"vesting_balances\":[],\"limit_orders\":[],\"call_orders\":[],\"settle_orders\":[],\"proposals\":[],\"assets\":[],\"withdraws\":[]}],[\"bilthon-2\",{\"account\":{\"id\":\"1.2.139207\",\"membership_expiration_date\":\"1970-01-01T00:00:00\",\"registrar\":\"1.2.117600\",\"referrer\":\"1.2.90200\",\"lifetime_referrer\":\"1.2.90200\",\"network_fee_percentage\":2000,\"lifetime_referrer_fee_percentage\":3000,\"referrer_rewards_percentage\":9000,\"name\":\"bilthon-2\",\"owner\":{\"weight_threshold\":1,\"account_auths\":[],\"key_auths\":[[\"BTS7gD2wtSauXpSCBin1rYctBcPWeZieX7YrVk1DuQpg9peczSqTv\",1]],\"address_auths\":[]},\"active\":{\"weight_threshold\":1,\"account_auths\":[],\"key_auths\":[[\"BTS7gD2wtSauXpSCBin1rYctBcPWeZieX7YrVk1DuQpg9peczSqTv\",1]],\"address_auths\":[]},\"options\":{\"memo_key\":\"BTS7gD2wtSauXpSCBin1rYctBcPWeZieX7YrVk1DuQpg9peczSqTv\",\"voting_account\":\"1.2.5\",\"num_witness\":0,\"num_committee\":0,\"votes\":[],\"extensions\":[]},\"statistics\":\"2.6.139207\",\"whitelisting_accounts\":[],\"blacklisting_accounts\":[],\"whitelisted_accounts\":[],\"blacklisted_accounts\":[],\"owner_special_authority\":[0,{}],\"active_special_authority\":[0,{}],\"top_n_control_flags\":0},\"statistics\":{\"id\":\"2.6.139207\",\"owner\":\"1.2.139207\",\"name\":\"bilthon-2\",\"most_recent_op\":\"2.9.6159244\",\"total_ops\":1,\"removed_ops\":0,\"total_core_in_orders\":0,\"core_in_balance\":0,\"has_cashback_vb\":false,\"is_voting\":false,\"lifetime_fees_paid\":0,\"pending_fees\":0,\"pending_vested_fees\":0},\"registrar_name\":\"bitshares-munich-faucet\",\"referrer_name\":\"bitshares-munich\",\"lifetime_referrer_name\":\"bitshares-munich\",\"votes\":[],\"balances\":[],\"vesting_balances\":[],\"limit_orders\":[],\"call_orders\":[],\"settle_orders\":[],\"proposals\":[],\"assets\":[],\"withdraws\":[]}]]}";
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(FullAccountDetails.class, new FullAccountDetails.FullAccountDeserializer())
|
||||
.registerTypeAdapter(Authority.class, new Authority.AuthorityDeserializer())
|
||||
.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer())
|
||||
.registerTypeAdapter(AccountOptions.class, new AccountOptions.AccountOptionsDeserializer())
|
||||
.create();
|
||||
Type FullAccountDetailsResponse = new TypeToken<JsonRpcResponse<List<FullAccountDetails>>>() {}.getType();
|
||||
JsonRpcResponse<List<FullAccountDetails>> response = gson.fromJson(serialized, FullAccountDetailsResponse);
|
||||
Assert.assertNotNull(response.result);
|
||||
Assert.assertNull(response.error);
|
||||
List<FullAccountDetails> fullAccountDetailsList = response.result;
|
||||
Assert.assertNotNull(fullAccountDetailsList);
|
||||
Assert.assertEquals(2, fullAccountDetailsList.size());
|
||||
Assert.assertNotNull(fullAccountDetailsList.get(0).getAccount());
|
||||
Assert.assertEquals("bilthon-1", fullAccountDetailsList.get(0).getAccount().name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package cy.agorise.graphenej.models;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import cy.agorise.graphenej.AssetAmount;
|
||||
import cy.agorise.graphenej.BaseOperation;
|
||||
import cy.agorise.graphenej.Extensions;
|
||||
import cy.agorise.graphenej.UserAccount;
|
||||
import cy.agorise.graphenej.api.android.DeserializationMap;
|
||||
import cy.agorise.graphenej.Memo;
|
||||
|
||||
public class HistoryOperationDetailsTest {
|
||||
|
||||
@Test
|
||||
public void testDeserialization(){
|
||||
String text = "{\"id\":5,\"jsonrpc\":\"2.0\",\"result\":{\"total_count\":2,\"operation_history_objs\":[{\"id\":\"1.11.5701809\",\"op\":[0,{\"fee\":{\"amount\":264174,\"asset_id\":\"1.3.0\"},\"from\":\"1.2.99700\",\"to\":\"1.2.138632\",\"amount\":{\"amount\":20000,\"asset_id\":\"1.3.120\"},\"extensions\":[]}],\"result\":[0,{}],\"block_num\":11094607,\"trx_in_block\":0,\"op_in_trx\":0,\"virtual_op\":31767},{\"id\":\"1.11.5701759\",\"op\":[0,{\"fee\":{\"amount\":264174,\"asset_id\":\"1.3.0\"},\"from\":\"1.2.99700\",\"to\":\"1.2.138632\",\"amount\":{\"amount\":10000000,\"asset_id\":\"1.3.0\"},\"extensions\":[]}],\"result\":[0,{}],\"block_num\":11094501,\"trx_in_block\":0,\"op_in_trx\":0,\"virtual_op\":31717}]}}\n";
|
||||
Gson gson = new GsonBuilder()
|
||||
.setExclusionStrategies(new DeserializationMap.SkipAccountOptionsStrategy(), new DeserializationMap.SkipAssetOptionsStrategy())
|
||||
.registerTypeAdapter(BaseOperation.class, new BaseOperation.OperationDeserializer())
|
||||
.registerTypeAdapter(OperationHistory.class, new OperationHistory.OperationHistoryDeserializer())
|
||||
.registerTypeAdapter(Memo.class, new Memo.MemoSerializer())
|
||||
.registerTypeAdapter(Extensions.class, new Extensions.ExtensionsDeserializer())
|
||||
.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer())
|
||||
.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer())
|
||||
.create();
|
||||
|
||||
Type GetAccountHistoryByOperationsResponse = new TypeToken<JsonRpcResponse<HistoryOperationDetail>>(){}.getType();
|
||||
JsonRpcResponse<HistoryOperationDetail> response = gson.fromJson(text, GetAccountHistoryByOperationsResponse);
|
||||
Assert.assertNotNull(response.result);
|
||||
Assert.assertNotNull(response.result.operation_history_objs);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package cy.agorise.graphenej.models;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import cy.agorise.graphenej.AssetAmount;
|
||||
import cy.agorise.graphenej.Transaction;
|
||||
import cy.agorise.graphenej.UserAccount;
|
||||
import cy.agorise.graphenej.Memo;
|
||||
import cy.agorise.graphenej.operations.CustomOperation;
|
||||
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
||||
import cy.agorise.graphenej.operations.TransferOperation;
|
||||
|
||||
public class JsonRpcNotificationTest {
|
||||
|
||||
private String text = "{\"method\":\"notice\",\"params\":[3,[[{\"id\":\"2.1.0\",\"head_block_number\":30071834,\"head_block_id\":\"01cadc1a5f3f517e2eba9588111aef3af3c59916\",\"time\":\"2018-08-30T18:19:45\",\"current_witness\":\"1.6.74\",\"next_maintenance_time\":\"2018-08-30T19:00:00\",\"last_budget_time\":\"2018-08-30T18:00:00\",\"witness_budget\":80800000,\"accounts_registered_this_interval\":9,\"recently_missed_count\":0,\"current_aslot\":30228263,\"recent_slots_filled\":\"340282366920938463463374607431768211455\",\"dynamic_flags\":0,\"last_irreversible_block_num\":30071813}]]]}";
|
||||
|
||||
@Test
|
||||
public void failResponseDeserialization(){
|
||||
Gson gson = new Gson();
|
||||
JsonRpcResponse<?> response = gson.fromJson(text, JsonRpcResponse.class);
|
||||
// The result field of this de-serialized object should be null
|
||||
Assert.assertNull(response.result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void succeedNotificationDeserialization(){
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer())
|
||||
.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer())
|
||||
.registerTypeAdapter(LimitOrderCreateOperation.class, new LimitOrderCreateOperation.LimitOrderCreateDeserializer())
|
||||
.registerTypeAdapter(CustomOperation.class, new CustomOperation.CustomOperationDeserializer())
|
||||
.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer())
|
||||
.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer())
|
||||
.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer())
|
||||
.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer())
|
||||
.registerTypeAdapter(OperationHistory.class, new OperationHistory.OperationHistoryDeserializer())
|
||||
.registerTypeAdapter(JsonRpcNotification.class, new JsonRpcNotification.JsonRpcNotificationDeserializer())
|
||||
.create();
|
||||
JsonRpcNotification notification = gson.fromJson(text, JsonRpcNotification.class);
|
||||
// Should deserialize a 'params' array with 2 elements
|
||||
Assert.assertEquals(2, notification.params.size());
|
||||
// The first element should be the number 3
|
||||
Assert.assertEquals(3, notification.params.get(0));
|
||||
ArrayList<Serializable> secondArgument = (ArrayList<Serializable>) notification.params.get(1);
|
||||
// The second element should be an array of length 1
|
||||
Assert.assertEquals(1, secondArgument.size());
|
||||
// Extracting the payload, which should be in itself another array
|
||||
DynamicGlobalProperties payload = (DynamicGlobalProperties) secondArgument.get(0);
|
||||
// Dynamic global properties head_block_number should match
|
||||
Assert.assertEquals(30071834, payload.head_block_number);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package cy.agorise.graphenej.models;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class JsonRpcResponseTest {
|
||||
|
||||
@Test
|
||||
public void deserializeJsonRpcResponse(){
|
||||
String text = "{\"id\":4,\"jsonrpc\":\"2.0\",\"result\":[{\"id\":\"2.1.0\",\"head_block_number\":30071833,\"head_block_id\":\"01cadc1964cb04ab551463e26033ab0f159bc8e1\",\"time\":\"2018-08-30T18:19:42\",\"current_witness\":\"1.6.71\",\"next_maintenance_time\":\"2018-08-30T19:00:00\",\"last_budget_time\":\"2018-08-30T18:00:00\",\"witness_budget\":80900000,\"accounts_registered_this_interval\":9,\"recently_missed_count\":0,\"current_aslot\":30228262,\"recent_slots_filled\":\"340282366920938463463374607431768211455\",\"dynamic_flags\":0,\"last_irreversible_block_num\":30071813}]}";
|
||||
Gson gson = new Gson();
|
||||
JsonRpcResponse<?> response = gson.fromJson(text, JsonRpcResponse.class);
|
||||
System.out.println("response: "+response.result);
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertNotNull(response.result);
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import org.junit.Test;
|
|||
import java.math.BigInteger;
|
||||
|
||||
import cy.agorise.graphenej.Address;
|
||||
import cy.agorise.graphenej.Memo;
|
||||
import cy.agorise.graphenej.PublicKey;
|
||||
import cy.agorise.graphenej.TestAccounts;
|
||||
import cy.agorise.graphenej.Util;
|
||||
|
|
1
sample/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build
|
47
sample/build.gradle
Normal file
|
@ -0,0 +1,47 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
|
||||
|
||||
defaultConfig {
|
||||
applicationId "cy.agorise.labs.sample"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 27
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
multiDexEnabled true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
api project(':graphenej')
|
||||
implementation 'com.android.support:appcompat-v7:27.1.1'
|
||||
implementation 'com.android.support:recyclerview-v7:27.1.1'
|
||||
implementation 'com.android.support:design:27.1.1'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
|
||||
implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1'
|
||||
implementation 'com.jakewharton:butterknife:8.8.1'
|
||||
implementation 'com.google.code.gson:gson:2.8.4'
|
||||
implementation 'com.google.guava:guava:25.0-jre'
|
||||
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.1', {
|
||||
exclude group: 'com.android.support', module: 'support-annotations'
|
||||
})
|
||||
implementation 'com.android.support:multidex:1.0.1'
|
||||
}
|
21
sample/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -0,0 +1,26 @@
|
|||
package cy.sample.labs.sample;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() throws Exception {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
assertEquals("com.luminiasoft.labs.sample", appContext.getPackageName());
|
||||
}
|
||||
}
|
25
sample/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="cy.agorise.labs.sample">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=".SampleApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".SubscriptionActivity" />
|
||||
<activity android:name=".CallsActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".PerformCallActivity"></activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,95 @@
|
|||
package cy.agorise.labs.sample;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.DividerItemDecoration;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import cy.agorise.graphenej.RPC;
|
||||
|
||||
public class CallsActivity extends AppCompatActivity {
|
||||
private final String TAG = this.getClass().getName();
|
||||
|
||||
@BindView(R.id.call_list)
|
||||
RecyclerView mRecyclerView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_calls);
|
||||
ButterKnife.bind(this);
|
||||
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
|
||||
mRecyclerView.setHasFixedSize(true);
|
||||
mRecyclerView.setLayoutManager(linearLayoutManager);
|
||||
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, linearLayoutManager.getOrientation()));
|
||||
mRecyclerView.setAdapter(new CallAdapter());
|
||||
}
|
||||
|
||||
private final class CallAdapter extends RecyclerView.Adapter<CallAdapter.ViewHolder> {
|
||||
|
||||
private String[] supportedCalls = new String[]{
|
||||
RPC.CALL_GET_OBJECTS,
|
||||
RPC.CALL_GET_ACCOUNTS,
|
||||
RPC.CALL_GET_BLOCK,
|
||||
RPC.CALL_GET_BLOCK_HEADER,
|
||||
RPC.CALL_GET_MARKET_HISTORY,
|
||||
RPC.CALL_GET_RELATIVE_ACCOUNT_HISTORY,
|
||||
RPC.CALL_GET_REQUIRED_FEES,
|
||||
RPC.CALL_LOOKUP_ASSET_SYMBOLS,
|
||||
RPC.CALL_LIST_ASSETS,
|
||||
RPC.CALL_GET_ACCOUNT_BY_NAME,
|
||||
RPC.CALL_GET_LIMIT_ORDERS,
|
||||
RPC.CALL_GET_ACCOUNT_HISTORY_BY_OPERATIONS,
|
||||
RPC.CALL_GET_FULL_ACCOUNTS,
|
||||
RPC.CALL_SET_SUBSCRIBE_CALLBACK
|
||||
};
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
TextView v = (TextView) LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_call, parent, false);
|
||||
return new ViewHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
String name = supportedCalls[position];
|
||||
String formattedName = name.replace("_", " ").toUpperCase();
|
||||
holder.mCallNameView.setText(formattedName);
|
||||
holder.mCallNameView.setOnClickListener((view) -> {
|
||||
String selectedCall = supportedCalls[position];
|
||||
Intent intent;
|
||||
if(selectedCall.equals(RPC.CALL_SET_SUBSCRIBE_CALLBACK)){
|
||||
intent = new Intent(CallsActivity.this, SubscriptionActivity.class);
|
||||
}else{
|
||||
intent = new Intent(CallsActivity.this, PerformCallActivity.class);
|
||||
intent.putExtra(Constants.KEY_SELECTED_CALL, selectedCall);
|
||||
}
|
||||
startActivity(intent);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return supportedCalls.length;
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public TextView mCallNameView;
|
||||
|
||||
public ViewHolder(TextView view) {
|
||||
super(view);
|
||||
this.mCallNameView = view;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package cy.agorise.labs.sample;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
|
||||
import cy.agorise.graphenej.api.android.NetworkService;
|
||||
|
||||
public abstract class ConnectedActivity extends AppCompatActivity implements ServiceConnection {
|
||||
private final String TAG = this.getClass().getName();
|
||||
|
||||
/* Network service connection */
|
||||
protected NetworkService mNetworkService;
|
||||
|
||||
/**
|
||||
* Flag used to keep track of the NetworkService binding state
|
||||
*/
|
||||
private boolean mShouldUnbindNetwork;
|
||||
|
||||
private ServiceConnection mNetworkServiceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className,
|
||||
IBinder service) {
|
||||
// We've bound to LocalService, cast the IBinder and get LocalService instance
|
||||
NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service;
|
||||
mNetworkService = binder.getService();
|
||||
|
||||
ConnectedActivity.this.onServiceConnected(className, service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName componentName) {
|
||||
ConnectedActivity.this.onServiceDisconnected(componentName);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
// Binding to NetworkService
|
||||
Intent intent = new Intent(this, NetworkService.class);
|
||||
if(bindService(intent, mNetworkServiceConnection, Context.BIND_AUTO_CREATE)){
|
||||
mShouldUnbindNetwork = true;
|
||||
}else{
|
||||
Log.e(TAG,"Binding to the network service failed.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
// Unbinding from network service
|
||||
if(mShouldUnbindNetwork){
|
||||
unbindService(mNetworkServiceConnection);
|
||||
mShouldUnbindNetwork = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package cy.agorise.labs.sample;
|
||||
|
||||
public class Constants {
|
||||
/**
|
||||
* Key used to pass the selected call as an intent extra
|
||||
*/
|
||||
public static final String KEY_SELECTED_CALL = "key_call";
|
||||
}
|
|
@ -0,0 +1,448 @@
|
|||
package cy.agorise.labs.sample;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.design.widget.TextInputEditText;
|
||||
import android.support.design.widget.TextInputLayout;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import cy.agorise.graphenej.OperationType;
|
||||
import cy.agorise.graphenej.RPC;
|
||||
import cy.agorise.graphenej.UserAccount;
|
||||
import cy.agorise.graphenej.api.ConnectionStatusUpdate;
|
||||
import cy.agorise.graphenej.api.android.DeserializationMap;
|
||||
import cy.agorise.graphenej.api.android.RxBus;
|
||||
import cy.agorise.graphenej.api.calls.GetAccountByName;
|
||||
import cy.agorise.graphenej.api.calls.GetAccountHistoryByOperations;
|
||||
import cy.agorise.graphenej.api.calls.GetAccounts;
|
||||
import cy.agorise.graphenej.api.calls.GetBlock;
|
||||
import cy.agorise.graphenej.api.calls.GetFullAccounts;
|
||||
import cy.agorise.graphenej.api.calls.GetLimitOrders;
|
||||
import cy.agorise.graphenej.api.calls.GetObjects;
|
||||
import cy.agorise.graphenej.api.calls.ListAssets;
|
||||
import cy.agorise.graphenej.models.JsonRpcResponse;
|
||||
import cy.agorise.graphenej.Memo;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
|
||||
public class PerformCallActivity extends ConnectedActivity {
|
||||
private final String TAG = this.getClass().getName();
|
||||
|
||||
@BindView(R.id.response)
|
||||
TextView mResponseView;
|
||||
|
||||
@BindView(R.id.container_param1)
|
||||
TextInputLayout mParam1View;
|
||||
|
||||
@BindView(R.id.container_param2)
|
||||
TextInputLayout mParam2View;
|
||||
|
||||
@BindView(R.id.container_param3)
|
||||
TextInputLayout mParam3View;
|
||||
|
||||
@BindView(R.id.container_param4)
|
||||
TextInputLayout mParam4View;
|
||||
|
||||
@BindView(R.id.param1)
|
||||
TextInputEditText param1;
|
||||
|
||||
@BindView(R.id.param2)
|
||||
TextInputEditText param2;
|
||||
|
||||
@BindView(R.id.param3)
|
||||
TextInputEditText param3;
|
||||
|
||||
@BindView(R.id.param4)
|
||||
TextInputEditText param4;
|
||||
|
||||
@BindView(R.id.button_send)
|
||||
Button mButtonSend;
|
||||
|
||||
// Field used to map a request id to its type
|
||||
private HashMap<Long, String> responseMap = new HashMap<>();
|
||||
|
||||
// Current request type. Ex: 'get_objects', 'get_accounts', etc
|
||||
private String mRPC;
|
||||
|
||||
private Disposable mDisposable;
|
||||
|
||||
private Gson gson = new GsonBuilder()
|
||||
.setExclusionStrategies(new DeserializationMap.SkipAccountOptionsStrategy(), new DeserializationMap.SkipAssetOptionsStrategy())
|
||||
.registerTypeAdapter(Memo.class, new Memo.MemoSerializer())
|
||||
.create();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_perform_call);
|
||||
ButterKnife.bind(this);
|
||||
|
||||
mRPC = getIntent().getStringExtra(Constants.KEY_SELECTED_CALL);
|
||||
Log.d(TAG,"Selected call: "+mRPC);
|
||||
switch (mRPC){
|
||||
case RPC.CALL_GET_OBJECTS:
|
||||
setupGetObjects();
|
||||
break;
|
||||
case RPC.CALL_GET_ACCOUNTS:
|
||||
setupGetAccounts();
|
||||
break;
|
||||
case RPC.CALL_GET_BLOCK:
|
||||
setupGetBlock();
|
||||
break;
|
||||
case RPC.CALL_GET_BLOCK_HEADER:
|
||||
setupGetBlockHeader();
|
||||
break;
|
||||
case RPC.CALL_GET_MARKET_HISTORY:
|
||||
setupGetMarketHistory();
|
||||
break;
|
||||
case RPC.CALL_GET_RELATIVE_ACCOUNT_HISTORY:
|
||||
setupGetRelativeAccountHistory();
|
||||
break;
|
||||
case RPC.CALL_GET_REQUIRED_FEES:
|
||||
break;
|
||||
case RPC.CALL_LOOKUP_ASSET_SYMBOLS:
|
||||
setupLookupAssetSymbols();
|
||||
break;
|
||||
case RPC.CALL_LIST_ASSETS:
|
||||
setupListAssets();
|
||||
break;
|
||||
case RPC.CALL_GET_ACCOUNT_BY_NAME:
|
||||
setupAccountByName();
|
||||
break;
|
||||
case RPC.CALL_GET_ACCOUNT_HISTORY_BY_OPERATIONS:
|
||||
setupGetAccountHistoryByOperations();
|
||||
break;
|
||||
case RPC.CALL_GET_LIMIT_ORDERS:
|
||||
setupGetLimitOrders();
|
||||
case RPC.CALL_GET_FULL_ACCOUNTS:
|
||||
setupGetFullAccounts();
|
||||
break;
|
||||
default:
|
||||
Log.d(TAG,"Default called");
|
||||
}
|
||||
|
||||
mDisposable = RxBus.getBusInstance()
|
||||
.asFlowable()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Consumer<Object>() {
|
||||
|
||||
@Override
|
||||
public void accept(Object message) throws Exception {
|
||||
Log.d(TAG,"accept. Msg class: "+message.getClass());
|
||||
if(message instanceof ConnectionStatusUpdate){
|
||||
// TODO: Update UI ?
|
||||
}else if(message instanceof JsonRpcResponse){
|
||||
handleJsonRpcResponse((JsonRpcResponse) message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupGetObjects(){
|
||||
requiredInput(1);
|
||||
mParam1View.setHint(getResources().getString(R.string.get_objects_arg1));
|
||||
}
|
||||
|
||||
private void setupGetAccounts(){
|
||||
requiredInput(1);
|
||||
mParam1View.setHint(getResources().getString(R.string.get_accounts_arg1));
|
||||
}
|
||||
|
||||
private void setupGetBlock(){
|
||||
requiredInput(1);
|
||||
mParam1View.setHint(getResources().getString(R.string.get_block_arg1));
|
||||
}
|
||||
|
||||
private void setupGetBlockHeader(){
|
||||
requiredInput(1);
|
||||
mParam1View.setHint(getResources().getString(R.string.get_block_arg1));
|
||||
}
|
||||
|
||||
private void setupGetMarketHistory(){
|
||||
requiredInput(4);
|
||||
Resources resources = getResources();
|
||||
mParam1View.setHint(resources.getString(R.string.get_market_history_arg1));
|
||||
mParam2View.setHint(resources.getString(R.string.get_market_history_arg2));
|
||||
mParam3View.setHint(resources.getString(R.string.get_market_history_arg3));
|
||||
mParam4View.setHint(resources.getString(R.string.get_market_history_arg4));
|
||||
}
|
||||
|
||||
private void setupGetRelativeAccountHistory(){
|
||||
requiredInput(4);
|
||||
Resources resources = getResources();
|
||||
mParam1View.setHint(resources.getString(R.string.get_relative_account_history_arg1));
|
||||
mParam2View.setHint(resources.getString(R.string.get_relative_account_history_arg2));
|
||||
mParam3View.setHint(resources.getString(R.string.get_relative_account_history_arg3));
|
||||
mParam4View.setHint(resources.getString(R.string.get_relative_account_history_arg4));
|
||||
}
|
||||
|
||||
private void setupLookupAssetSymbols(){
|
||||
requiredInput(4);
|
||||
Resources resources = getResources();
|
||||
mParam1View.setHint(resources.getString(R.string.lookup_asset_symbols_arg1));
|
||||
mParam2View.setHint(resources.getString(R.string.lookup_asset_symbols_arg2));
|
||||
mParam3View.setHint(resources.getString(R.string.lookup_asset_symbols_arg3));
|
||||
mParam4View.setHint(resources.getString(R.string.lookup_asset_symbols_arg4));
|
||||
}
|
||||
|
||||
private void setupListAssets(){
|
||||
requiredInput(2);
|
||||
Resources resources = getResources();
|
||||
mParam1View.setHint(resources.getString(R.string.list_assets_arg1));
|
||||
mParam2View.setHint(resources.getString(R.string.list_assets_arg2));
|
||||
param2.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
}
|
||||
|
||||
private void setupAccountByName(){
|
||||
requiredInput(1);
|
||||
Resources resources = getResources();
|
||||
mParam1View.setHint(resources.getString(R.string.get_accounts_by_name_arg1));
|
||||
param1.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
}
|
||||
|
||||
private void setupGetAccountHistoryByOperations(){
|
||||
requiredInput(4);
|
||||
Resources resources = getResources();
|
||||
mParam1View.setHint(resources.getString(R.string.get_account_history_by_operations_arg1));
|
||||
mParam2View.setHint(resources.getString(R.string.get_account_history_by_operations_arg2));
|
||||
mParam3View.setHint(resources.getString(R.string.get_account_history_by_operations_arg3));
|
||||
mParam4View.setHint(resources.getString(R.string.get_account_history_by_operations_arg4));
|
||||
|
||||
param2.setText("0"); // Only transfer de-serialization is currently supported by the library!
|
||||
param2.setEnabled(false);
|
||||
param2.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
param3.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
param4.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
}
|
||||
|
||||
private void setupGetLimitOrders(){
|
||||
requiredInput(3);
|
||||
Resources resources = getResources();
|
||||
mParam1View.setHint(resources.getString(R.string.get_limit_orders_arg1));
|
||||
mParam2View.setHint(resources.getString(R.string.get_limit_orders_arg2));
|
||||
mParam3View.setHint(resources.getString(R.string.get_limit_orders_arg3));
|
||||
param1.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
param2.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
param3.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
}
|
||||
|
||||
private void setupGetFullAccounts(){
|
||||
requiredInput(1);
|
||||
mParam1View.setHint(getString(R.string.get_full_accounts_arg1));
|
||||
param1.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
}
|
||||
|
||||
private void requiredInput(int inputCount){
|
||||
if(inputCount == 1){
|
||||
mParam1View.setVisibility(View.VISIBLE);
|
||||
mParam2View.setVisibility(View.GONE);
|
||||
mParam3View.setVisibility(View.GONE);
|
||||
mParam4View.setVisibility(View.GONE);
|
||||
}else if(inputCount == 2){
|
||||
mParam1View.setVisibility(View.VISIBLE);
|
||||
mParam2View.setVisibility(View.VISIBLE);
|
||||
mParam3View.setVisibility(View.GONE);
|
||||
mParam4View.setVisibility(View.GONE);
|
||||
}else if(inputCount == 3){
|
||||
mParam1View.setVisibility(View.VISIBLE);
|
||||
mParam2View.setVisibility(View.VISIBLE);
|
||||
mParam3View.setVisibility(View.VISIBLE);
|
||||
mParam4View.setVisibility(View.GONE);
|
||||
}else if(inputCount == 4){
|
||||
mParam1View.setVisibility(View.VISIBLE);
|
||||
mParam2View.setVisibility(View.VISIBLE);
|
||||
mParam3View.setVisibility(View.VISIBLE);
|
||||
mParam4View.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.button_send)
|
||||
public void onSendClicked(Button v){
|
||||
switch (mRPC){
|
||||
case RPC.CALL_GET_OBJECTS:
|
||||
sendGetObjectsRequest();
|
||||
break;
|
||||
case RPC.CALL_GET_ACCOUNTS:
|
||||
sendGetAccountsRequest();
|
||||
break;
|
||||
case RPC.CALL_GET_BLOCK:
|
||||
break;
|
||||
case RPC.CALL_GET_BLOCK_HEADER:
|
||||
break;
|
||||
case RPC.CALL_GET_MARKET_HISTORY:
|
||||
break;
|
||||
case RPC.CALL_GET_RELATIVE_ACCOUNT_HISTORY:
|
||||
break;
|
||||
case RPC.CALL_GET_REQUIRED_FEES:
|
||||
break;
|
||||
case RPC.CALL_LOOKUP_ASSET_SYMBOLS:
|
||||
break;
|
||||
case RPC.CALL_LIST_ASSETS:
|
||||
sendListAssets();
|
||||
break;
|
||||
case RPC.CALL_GET_ACCOUNT_BY_NAME:
|
||||
getAccountByName();
|
||||
break;
|
||||
case RPC.CALL_GET_LIMIT_ORDERS:
|
||||
getLimitOrders();
|
||||
break;
|
||||
case RPC.CALL_GET_ACCOUNT_HISTORY_BY_OPERATIONS:
|
||||
getAccountHistoryByOperations();
|
||||
break;
|
||||
case RPC.CALL_GET_FULL_ACCOUNTS:
|
||||
getFullAccounts();
|
||||
default:
|
||||
Log.d(TAG,"Default called");
|
||||
}
|
||||
}
|
||||
|
||||
private void sendGetObjectsRequest(){
|
||||
String objectId = param1.getText().toString();
|
||||
if(objectId.matches("\\d\\.\\d{1,3}\\.\\d{1,10}")){
|
||||
ArrayList<String> array = new ArrayList<>();
|
||||
array.add(objectId);
|
||||
GetObjects getObjects = new GetObjects(array);
|
||||
long id = mNetworkService.sendMessage(getObjects, GetObjects.REQUIRED_API);
|
||||
responseMap.put(id, mRPC);
|
||||
}else{
|
||||
param1.setError(getResources().getString(R.string.error_input_id));
|
||||
}
|
||||
}
|
||||
|
||||
private void sendGetAccountsRequest(){
|
||||
String userId = param1.getText().toString();
|
||||
if(userId.matches("\\d\\.\\d{1,3}\\.\\d{1,10}")){
|
||||
GetAccounts getAccounts = new GetAccounts(new UserAccount(userId));
|
||||
long id = mNetworkService.sendMessage(getAccounts, GetBlock.REQUIRED_API);
|
||||
responseMap.put(id, mRPC);
|
||||
}else{
|
||||
param1.setError(getResources().getString(R.string.error_input_id));
|
||||
}
|
||||
}
|
||||
|
||||
private void sendListAssets(){
|
||||
try{
|
||||
String lowerBound = param1.getText().toString();
|
||||
int limit = Integer.parseInt(param2.getText().toString());
|
||||
ListAssets listAssets = new ListAssets(lowerBound, limit);
|
||||
long id = mNetworkService.sendMessage(listAssets, ListAssets.REQUIRED_API);
|
||||
responseMap.put(id, mRPC);
|
||||
}catch(NumberFormatException e){
|
||||
Toast.makeText(this, getString(R.string.error_number_format), Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG,"NumberFormatException while reading limit value. Msg: "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void getAccountByName(){
|
||||
String accountName = param1.getText().toString();
|
||||
long id = mNetworkService.sendMessage(new GetAccountByName(accountName), GetAccountByName.REQUIRED_API);
|
||||
responseMap.put(id, mRPC);
|
||||
}
|
||||
|
||||
private void getLimitOrders(){
|
||||
String assetA = param1.getText().toString();
|
||||
String assetB = param2.getText().toString();
|
||||
try{
|
||||
int limit = Integer.parseInt(param3.getText().toString());
|
||||
long id = mNetworkService.sendMessage(new GetLimitOrders(assetA, assetB, limit), GetLimitOrders.REQUIRED_API);
|
||||
}catch(NumberFormatException e){
|
||||
Toast.makeText(this, getString(R.string.error_number_format), Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG,"NumberFormatException while trying to read limit value. Msg: "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void getAccountHistoryByOperations(){
|
||||
try{
|
||||
String account = param1.getText().toString();
|
||||
ArrayList<OperationType> operationTypes = new ArrayList<>();
|
||||
operationTypes.add(OperationType.TRANSFER_OPERATION); // Currently restricted to transfer operations
|
||||
long start = Long.parseLong(param3.getText().toString());
|
||||
long limit = Long.parseLong(param4.getText().toString());
|
||||
long id = mNetworkService.sendMessage(new GetAccountHistoryByOperations(account, operationTypes, start, limit), GetAccountHistoryByOperations.REQUIRED_API);
|
||||
responseMap.put(id, mRPC);
|
||||
}catch(NumberFormatException e){
|
||||
Toast.makeText(this, getString(R.string.error_number_format), Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG,"NumberFormatException while trying to read arguments for 'get_account_history_by_operations'. Msg: "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void getFullAccounts(){
|
||||
ArrayList<String> accounts = new ArrayList<>();
|
||||
accounts.addAll(Arrays.asList(param1.getText().toString().split(",")));
|
||||
long id = mNetworkService.sendMessage(new GetFullAccounts(accounts, false), GetFullAccounts.REQUIRED_API);
|
||||
responseMap.put(id, mRPC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method that will decide what to do with each JSON-RPC response
|
||||
*
|
||||
* @param response The JSON-RPC api call response
|
||||
*/
|
||||
private void handleJsonRpcResponse(JsonRpcResponse response){
|
||||
long id = response.id;
|
||||
if(responseMap.get(id) != null){
|
||||
String request = responseMap.get(id);
|
||||
switch(request){
|
||||
case RPC.CALL_GET_ACCOUNTS:
|
||||
case RPC.CALL_GET_BLOCK:
|
||||
case RPC.CALL_GET_BLOCK_HEADER:
|
||||
case RPC.CALL_GET_MARKET_HISTORY:
|
||||
case RPC.CALL_GET_ACCOUNT_HISTORY:
|
||||
case RPC.CALL_GET_RELATIVE_ACCOUNT_HISTORY:
|
||||
case RPC.CALL_GET_REQUIRED_FEES:
|
||||
case RPC.CALL_LOOKUP_ASSET_SYMBOLS:
|
||||
case RPC.CALL_LIST_ASSETS:
|
||||
case RPC.CALL_GET_ACCOUNT_BY_NAME:
|
||||
case RPC.CALL_GET_LIMIT_ORDERS:
|
||||
case RPC.CALL_GET_ACCOUNT_HISTORY_BY_OPERATIONS:
|
||||
case RPC.CALL_GET_FULL_ACCOUNTS:
|
||||
mResponseView.setText(mResponseView.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n");
|
||||
break;
|
||||
default:
|
||||
Log.w(TAG,"Case not handled");
|
||||
mResponseView.setText(mResponseView.getText() + response.result.toString());
|
||||
}
|
||||
// Remember to remove the used id entry from the map, as it would
|
||||
// otherwise just increase the app's memory usage
|
||||
responseMap.remove(id);
|
||||
}else{
|
||||
Log.d(TAG,"No entry");
|
||||
mResponseView.setText(mResponseView.getText() + gson.toJson(response, JsonRpcResponse.class) + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if(!mDisposable.isDisposed())
|
||||
mDisposable.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
|
||||
// Called upon NetworkService connection
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName componentName) {
|
||||
// Called upon NetworkService disconnection
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package cy.agorise.labs.sample;
|
||||
|
||||
import android.app.Application;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import cy.agorise.graphenej.api.ApiAccess;
|
||||
import cy.agorise.graphenej.api.android.NetworkService;
|
||||
import cy.agorise.graphenej.api.android.NetworkServiceManager;
|
||||
|
||||
/**
|
||||
* Sample application class
|
||||
*/
|
||||
|
||||
public class SampleApplication extends Application {
|
||||
private final String TAG = this.getClass().getName();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// This variable would hold a list of custom nodes
|
||||
String customNodes = "wss://mydomain.net/ws,wss://myotherdomain.com/ws";
|
||||
|
||||
// Specifying some important information regarding the connection, such as the
|
||||
// credentials and the requested API accesses
|
||||
int requestedApis = ApiAccess.API_DATABASE | ApiAccess.API_HISTORY | ApiAccess.API_NETWORK_BROADCAST;
|
||||
PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.edit()
|
||||
.putString(NetworkService.KEY_USERNAME, "nelson")
|
||||
.putString(NetworkService.KEY_PASSWORD, "secret")
|
||||
.putInt(NetworkService.KEY_REQUESTED_APIS, requestedApis)
|
||||
// .putString(NetworkService.KEY_CUSTOM_NODE_URLS, customNodes)
|
||||
.apply();
|
||||
|
||||
/*
|
||||
* Registering this class as a listener to all activity's callback cycle events, in order to
|
||||
* better estimate when the user has left the app and it is safe to disconnect the websocket connection
|
||||
*/
|
||||
registerActivityLifecycleCallbacks(new NetworkServiceManager(this));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
package cy.agorise.labs.sample;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import cy.agorise.graphenej.api.android.NetworkService;
|
||||
import cy.agorise.graphenej.api.android.RxBus;
|
||||
import cy.agorise.graphenej.api.calls.CancelAllSubscriptions;
|
||||
import cy.agorise.graphenej.api.calls.SetSubscribeCallback;
|
||||
import cy.agorise.graphenej.models.JsonRpcNotification;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
|
||||
public class SubscriptionActivity extends AppCompatActivity {
|
||||
|
||||
private final String TAG = this.getClass().getName();
|
||||
|
||||
@BindView(R.id.text_field)
|
||||
TextView mTextField;
|
||||
|
||||
// In case we want to interact directly with the service
|
||||
private NetworkService mService;
|
||||
|
||||
private Disposable mDisposable;
|
||||
|
||||
// Notification counter
|
||||
private int counter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_second);
|
||||
|
||||
ButterKnife.bind(this);
|
||||
|
||||
mDisposable = RxBus.getBusInstance()
|
||||
.asFlowable()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Consumer<Object>() {
|
||||
|
||||
@Override
|
||||
public void accept(Object message) throws Exception {
|
||||
if(message instanceof String){
|
||||
Log.d(TAG,"Got text message: "+(message));
|
||||
mTextField.setText(mTextField.getText() + ((String) message) + "\n");
|
||||
}else if(message instanceof JsonRpcNotification){
|
||||
counter++;
|
||||
mTextField.setText(String.format("Got %d notifications so far", counter));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
// Bind to LocalService
|
||||
Intent intent = new Intent(this, NetworkService.class);
|
||||
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
unbindService(mConnection);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mDisposable.dispose();
|
||||
}
|
||||
|
||||
/** Defines callbacks for backend binding, passed to bindService() */
|
||||
private ServiceConnection mConnection = new ServiceConnection() {
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className,
|
||||
IBinder service) {
|
||||
Log.d(TAG,"onServiceConnected");
|
||||
// We've bound to LocalService, cast the IBinder and get LocalService instance
|
||||
NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service;
|
||||
mService = binder.getService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName componentName) {
|
||||
Log.d(TAG,"onServiceDisconnected");
|
||||
}
|
||||
};
|
||||
|
||||
@OnClick(R.id.subscribe)
|
||||
public void onTransferFeeUsdClicked(View v){
|
||||
mService.sendMessage(new SetSubscribeCallback(true), SetSubscribeCallback.REQUIRED_API);
|
||||
}
|
||||
|
||||
@OnClick(R.id.unsubscribe)
|
||||
public void onTransferFeeBtsClicked(View v){
|
||||
mService.sendMessage(new CancelAllSubscriptions(), CancelAllSubscriptions.REQUIRED_API);
|
||||
}
|
||||
}
|
34
sample/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
|
@ -0,0 +1,34 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
</vector>
|
170
sample/src/main/res/drawable/ic_launcher_background.xml
Normal file
|
@ -0,0 +1,170 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillColor="#26A69A"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
</vector>
|
9
sample/src/main/res/layout/activity_calls.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/call_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".CallsActivity">
|
||||
|
||||
</android.support.v7.widget.RecyclerView>
|
118
sample/src/main/res/layout/activity_perform_call.xml
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".PerformCallActivity">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/output_text_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/container_param1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
<TextView
|
||||
android:id="@+id/response"
|
||||
tools:text="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
android:id="@+id/container_param1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/container_param2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/output_text_container">
|
||||
<android.support.design.widget.TextInputEditText
|
||||
android:id="@+id/param1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
android:id="@+id/container_param2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/container_param3"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/container_param1">
|
||||
<android.support.design.widget.TextInputEditText
|
||||
android:id="@+id/param2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
android:id="@+id/container_param3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/container_param4"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/container_param2">
|
||||
<android.support.design.widget.TextInputEditText
|
||||
android:id="@+id/param3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
android:id="@+id/container_param4"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/button_send"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/container_param3">
|
||||
<android.support.design.widget.TextInputEditText
|
||||
android:id="@+id/param4"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_send"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="@string/action_send"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/container_param4" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
38
sample/src/main/res/layout/activity_second.xml
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="cy.agorise.labs.sample.SubscriptionActivity">
|
||||
<TextView
|
||||
android:id="@+id/text_field"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:lines="20"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/buttons_container"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
<LinearLayout
|
||||
android:id="@+id/buttons_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:weightSum="2"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
<Button
|
||||
android:id="@+id/subscribe"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="11sp"
|
||||
android:text="Subscribe"/>
|
||||
<Button
|
||||
android:id="@+id/unsubscribe"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="11sp"
|
||||
android:text="Unsubscribe"/>
|
||||
</LinearLayout>
|
||||
</android.support.constraint.ConstraintLayout>
|
13
sample/src/main/res/layout/item_call.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:gravity="center"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:padding="16dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
tools:text="Sample">
|
||||
</TextView>
|
5
sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
BIN
sample/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
sample/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
sample/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
sample/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
sample/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 9 KiB |
BIN
sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 15 KiB |
6
sample/src/main/res/values/colors.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
</resources>
|
57
sample/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,57 @@
|
|||
<resources>
|
||||
<string name="app_name">Sample</string>
|
||||
|
||||
<string name="error_input_id">The entered value doesn\'t seem to be an object id</string>
|
||||
<string name="error_number_format">Illegal number format</string>
|
||||
|
||||
<!-- Actions, buttons, etc -->
|
||||
<string name="action_send">Send</string>
|
||||
|
||||
<!-- GetObjects input field -->
|
||||
<string name="get_objects_arg1">Object id</string>
|
||||
|
||||
<!-- GetAccounts input field -->
|
||||
<string name="get_accounts_arg1">Account id</string>
|
||||
|
||||
<!-- GetBlock & GetBlockHeader input field -->
|
||||
<string name="get_block_arg1">Block id</string>
|
||||
|
||||
<!-- GetMarketHistory input fields -->
|
||||
<string name="get_market_history_arg1">Base asset</string>
|
||||
<string name="get_market_history_arg2">Quote asset</string>
|
||||
<string name="get_market_history_arg3">Start timestamp (Latest)</string>
|
||||
<string name="get_market_history_arg4">End timestamp (Earliest)</string>
|
||||
|
||||
<!-- GetRelativeAccountHistory input fields -->
|
||||
<string name="get_relative_account_history_arg1">User account</string>
|
||||
<string name="get_relative_account_history_arg2">Stop timestamp (Latest)</string>
|
||||
<string name="get_relative_account_history_arg3">Limit</string>
|
||||
<string name="get_relative_account_history_arg4">Start timestamp (Earliest)</string>
|
||||
|
||||
<!-- LookupAssetSymbols input fields -->
|
||||
<string name="lookup_asset_symbols_arg1">Asset 1 id</string>
|
||||
<string name="lookup_asset_symbols_arg2">Asset 2 id</string>
|
||||
<string name="lookup_asset_symbols_arg3">Asset 3 id</string>
|
||||
<string name="lookup_asset_symbols_arg4">Asset 4 id</string>
|
||||
|
||||
<!-- List assets input fields -->
|
||||
<string name="list_assets_arg1">Lower bound of symbol names to retrieve</string>
|
||||
<string name="list_assets_arg2">Maximum number of assets to fetch (must not exceed 100)</string>
|
||||
|
||||
<!-- Get account by name fields -->
|
||||
<string name="get_accounts_by_name_arg1">Account name</string>
|
||||
|
||||
<!-- GetAccountHistoryByOperations input fields -->
|
||||
<string name="get_account_history_by_operations_arg1">Account id or name</string>
|
||||
<string name="get_account_history_by_operations_arg2">Operaton type (only transfer type is supported)</string>
|
||||
<string name="get_account_history_by_operations_arg3">Start sequence number</string>
|
||||
<string name="get_account_history_by_operations_arg4">Limit</string>
|
||||
|
||||
<!-- GetLimitOrders input fields -->
|
||||
<string name="get_limit_orders_arg1">Asset A</string>
|
||||
<string name="get_limit_orders_arg2">Asset B</string>
|
||||
<string name="get_limit_orders_arg3">Number of orders</string>
|
||||
|
||||
<!-- GetFullAccounts input fields -->
|
||||
<string name="get_full_accounts_arg1">Account names or ids, separated by commas</string>
|
||||
</resources>
|
11
sample/src/main/res/values/styles.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,17 @@
|
|||
package cy.sample.labs.sample;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() throws Exception {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
include ':sample'
|
||||
rootProject.name = "Graphenej"
|
||||
|
||||
include ":graphenej"
|
||||
|
|